authorgravatar for twostepted@gmail.comTravis Staloch <twostepted@gmail.com> 2021-09-02 13:50:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 17:02:43-07:00
log29f41896ed9d99e82a88f4b63efa182ca0d2f93c
tree1e6eb1159df89f79b04f050da662653925c77b1c
parent79bc5891c1c4cde0592fe1b10b6c9a85914155cf

sat-arithmetic: add operator support

- adds initial support for the operators +|, -|, *|, <<|, +|=, -|=, *|=, <<|= - uses operators in addition to builtins in behavior test - adds binOpExt() and assignBinOpExt() to AstGen.zig. these need to be audited

21 files changed, 556 insertions(+), 54 deletions(-)

lib/std/zig/Ast.zig+32
......@@ -396,6 +396,7 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
396396 .assign_add,
397397 .assign_sub,
398398 .assign_bit_shift_left,
399 .assign_bit_shift_left_sat,
399400 .assign_bit_shift_right,
400401 .assign_bit_and,
401402 .assign_bit_xor,
......@@ -403,6 +404,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
403404 .assign_mul_wrap,
404405 .assign_add_wrap,
405406 .assign_sub_wrap,
407 .assign_mul_sat,
408 .assign_add_sat,
409 .assign_sub_sat,
406410 .assign,
407411 .merge_error_sets,
408412 .mul,
......@@ -410,12 +414,16 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
410414 .mod,
411415 .array_mult,
412416 .mul_wrap,
417 .mul_sat,
413418 .add,
414419 .sub,
415420 .array_cat,
416421 .add_wrap,
417422 .sub_wrap,
423 .add_sat,
424 .sub_sat,
418425 .bit_shift_left,
426 .bit_shift_left_sat,
419427 .bit_shift_right,
420428 .bit_and,
421429 .bit_xor,
......@@ -652,6 +660,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
652660 .assign_add,
653661 .assign_sub,
654662 .assign_bit_shift_left,
663 .assign_bit_shift_left_sat,
655664 .assign_bit_shift_right,
656665 .assign_bit_and,
657666 .assign_bit_xor,
......@@ -659,6 +668,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
659668 .assign_mul_wrap,
660669 .assign_add_wrap,
661670 .assign_sub_wrap,
671 .assign_mul_sat,
672 .assign_add_sat,
673 .assign_sub_sat,
662674 .assign,
663675 .merge_error_sets,
664676 .mul,
......@@ -666,12 +678,16 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
666678 .mod,
667679 .array_mult,
668680 .mul_wrap,
681 .mul_sat,
669682 .add,
670683 .sub,
671684 .array_cat,
672685 .add_wrap,
673686 .sub_wrap,
687 .add_sat,
688 .sub_sat,
674689 .bit_shift_left,
690 .bit_shift_left_sat,
675691 .bit_shift_right,
676692 .bit_and,
677693 .bit_xor,
......@@ -2525,6 +2541,8 @@ pub const Node = struct {
25252541 assign_sub,
25262542 /// `lhs <<= rhs`. main_token is op.
25272543 assign_bit_shift_left,
2544 /// `lhs <<|= rhs`. main_token is op.
2545 assign_bit_shift_left_sat,
25282546 /// `lhs >>= rhs`. main_token is op.
25292547 assign_bit_shift_right,
25302548 /// `lhs &= rhs`. main_token is op.
......@@ -2539,6 +2557,12 @@ pub const Node = struct {
25392557 assign_add_wrap,
25402558 /// `lhs -%= rhs`. main_token is op.
25412559 assign_sub_wrap,
2560 /// `lhs *|= rhs`. main_token is op.
2561 assign_mul_sat,
2562 /// `lhs +|= rhs`. main_token is op.
2563 assign_add_sat,
2564 /// `lhs -|= rhs`. main_token is op.
2565 assign_sub_sat,
25422566 /// `lhs = rhs`. main_token is op.
25432567 assign,
25442568 /// `lhs || rhs`. main_token is the `||`.
......@@ -2553,6 +2577,8 @@ pub const Node = struct {
25532577 array_mult,
25542578 /// `lhs *% rhs`. main_token is the `*%`.
25552579 mul_wrap,
2580 /// `lhs *| rhs`. main_token is the `*%`.
2581 mul_sat,
25562582 /// `lhs + rhs`. main_token is the `+`.
25572583 add,
25582584 /// `lhs - rhs`. main_token is the `-`.
......@@ -2563,8 +2589,14 @@ pub const Node = struct {
25632589 add_wrap,
25642590 /// `lhs -% rhs`. main_token is the `-%`.
25652591 sub_wrap,
2592 /// `lhs +| rhs`. main_token is the `+|`.
2593 add_sat,
2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,
25662596 /// `lhs << rhs`. main_token is the `<<`.
25672597 bit_shift_left,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 bit_shift_left_sat,
25682600 /// `lhs >> rhs`. main_token is the `>>`.
25692601 bit_shift_right,
25702602 /// `lhs & rhs`. main_token is the `&`.
lib/std/zig/parse.zig+8
......@@ -1269,6 +1269,7 @@ const Parser = struct {
12691269 .plus_equal => .assign_add,
12701270 .minus_equal => .assign_sub,
12711271 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_bit_shift_left_sat,
12721273 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
12731274 .ampersand_equal => .assign_bit_and,
12741275 .caret_equal => .assign_bit_xor,
......@@ -1276,6 +1277,9 @@ const Parser = struct {
12761277 .asterisk_percent_equal => .assign_mul_wrap,
12771278 .plus_percent_equal => .assign_add_wrap,
12781279 .minus_percent_equal => .assign_sub_wrap,
1280 .asterisk_pipe_equal => .assign_mul_sat,
1281 .plus_pipe_equal => .assign_add_sat,
1282 .minus_pipe_equal => .assign_sub_sat,
12791283 .equal => .assign,
12801284 else => return expr,
12811285 };
......@@ -1343,6 +1347,7 @@ const Parser = struct {
13431347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13441348
13451349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },
1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .bit_shift_left_sat },
13461351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },
13471352
13481353 .plus = .{ .prec = 60, .tag = .add },
......@@ -1350,6 +1355,8 @@ const Parser = struct {
13501355 .plus_plus = .{ .prec = 60, .tag = .array_cat },
13511356 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
13521357 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1358 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1359 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
13531360
13541361 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
13551362 .asterisk = .{ .prec = 70, .tag = .mul },
......@@ -1357,6 +1364,7 @@ const Parser = struct {
13571364 .percent = .{ .prec = 70, .tag = .mod },
13581365 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
13591366 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1367 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
13601368 });
13611369
13621370 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
lib/std/zig/render.zig+8
......@@ -333,26 +333,32 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
333333
334334 .add,
335335 .add_wrap,
336 .add_sat,
336337 .array_cat,
337338 .array_mult,
338339 .assign,
339340 .assign_bit_and,
340341 .assign_bit_or,
341342 .assign_bit_shift_left,
343 .assign_bit_shift_left_sat,
342344 .assign_bit_shift_right,
343345 .assign_bit_xor,
344346 .assign_div,
345347 .assign_sub,
346348 .assign_sub_wrap,
349 .assign_sub_sat,
347350 .assign_mod,
348351 .assign_add,
349352 .assign_add_wrap,
353 .assign_add_sat,
350354 .assign_mul,
351355 .assign_mul_wrap,
356 .assign_mul_sat,
352357 .bang_equal,
353358 .bit_and,
354359 .bit_or,
355360 .bit_shift_left,
361 .bit_shift_left_sat,
356362 .bit_shift_right,
357363 .bit_xor,
358364 .bool_and,
......@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
367373 .mod,
368374 .mul,
369375 .mul_wrap,
376 .mul_sat,
370377 .sub,
371378 .sub_wrap,
379 .sub_sat,
372380 .@"orelse",
373381 => {
374382 const infix = datas[node];
lib/std/zig/tokenizer.zig+79
......@@ -103,15 +103,21 @@ pub const Token = struct {
103103 plus_equal,
104104 plus_percent,
105105 plus_percent_equal,
106 plus_pipe,
107 plus_pipe_equal,
106108 minus,
107109 minus_equal,
108110 minus_percent,
109111 minus_percent_equal,
112 minus_pipe,
113 minus_pipe_equal,
110114 asterisk,
111115 asterisk_equal,
112116 asterisk_asterisk,
113117 asterisk_percent,
114118 asterisk_percent_equal,
119 asterisk_pipe,
120 asterisk_pipe_equal,
115121 arrow,
116122 colon,
117123 slash,
......@@ -124,6 +130,8 @@ pub const Token = struct {
124130 angle_bracket_left_equal,
125131 angle_bracket_angle_bracket_left,
126132 angle_bracket_angle_bracket_left_equal,
133 angle_bracket_angle_bracket_left_pipe,
134 angle_bracket_angle_bracket_left_pipe_equal,
127135 angle_bracket_right,
128136 angle_bracket_right_equal,
129137 angle_bracket_angle_bracket_right,
......@@ -227,15 +235,21 @@ pub const Token = struct {
227235 .plus_equal => "+=",
228236 .plus_percent => "+%",
229237 .plus_percent_equal => "+%=",
238 .plus_pipe => "+|",
239 .plus_pipe_equal => "+|=",
230240 .minus => "-",
231241 .minus_equal => "-=",
232242 .minus_percent => "-%",
233243 .minus_percent_equal => "-%=",
244 .minus_pipe => "-|",
245 .minus_pipe_equal => "-|=",
234246 .asterisk => "*",
235247 .asterisk_equal => "*=",
236248 .asterisk_asterisk => "**",
237249 .asterisk_percent => "*%",
238250 .asterisk_percent_equal => "*%=",
251 .asterisk_pipe => "*|",
252 .asterisk_pipe_equal => "*|=",
239253 .arrow => "->",
240254 .colon => ":",
241255 .slash => "/",
......@@ -248,6 +262,8 @@ pub const Token = struct {
248262 .angle_bracket_left_equal => "<=",
249263 .angle_bracket_angle_bracket_left => "<<",
250264 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .angle_bracket_angle_bracket_left_pipe => "<<|",
266 .angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
251267 .angle_bracket_right => ">",
252268 .angle_bracket_right_equal => ">=",
253269 .angle_bracket_angle_bracket_right => ">>",
......@@ -352,8 +368,10 @@ pub const Tokenizer = struct {
352368 pipe,
353369 minus,
354370 minus_percent,
371 minus_pipe,
355372 asterisk,
356373 asterisk_percent,
374 asterisk_pipe,
357375 slash,
358376 line_comment_start,
359377 line_comment,
......@@ -382,8 +400,10 @@ pub const Tokenizer = struct {
382400 percent,
383401 plus,
384402 plus_percent,
403 plus_pipe,
385404 angle_bracket_left,
386405 angle_bracket_angle_bracket_left,
406 angle_bracket_angle_bracket_left_pipe,
387407 angle_bracket_right,
388408 angle_bracket_angle_bracket_right,
389409 period,
......@@ -584,6 +604,9 @@ pub const Tokenizer = struct {
584604 '%' => {
585605 state = .asterisk_percent;
586606 },
607 '|' => {
608 state = .asterisk_pipe;
609 },
587610 else => {
588611 result.tag = .asterisk;
589612 break;
......@@ -602,6 +625,18 @@ pub const Tokenizer = struct {
602625 },
603626 },
604627
628 .asterisk_pipe => switch (c) {
629 '=' => {
630 result.tag = .asterisk_pipe_equal;
631 self.index += 1;
632 break;
633 },
634 else => {
635 result.tag = .asterisk_pipe;
636 break;
637 },
638 },
639
605640 .percent => switch (c) {
606641 '=' => {
607642 result.tag = .percent_equal;
......@@ -628,6 +663,9 @@ pub const Tokenizer = struct {
628663 '%' => {
629664 state = .plus_percent;
630665 },
666 '|' => {
667 state = .plus_pipe;
668 },
631669 else => {
632670 result.tag = .plus;
633671 break;
......@@ -646,6 +684,18 @@ pub const Tokenizer = struct {
646684 },
647685 },
648686
687 .plus_pipe => switch (c) {
688 '=' => {
689 result.tag = .plus_pipe_equal;
690 self.index += 1;
691 break;
692 },
693 else => {
694 result.tag = .plus_pipe;
695 break;
696 },
697 },
698
649699 .caret => switch (c) {
650700 '=' => {
651701 result.tag = .caret_equal;
......@@ -903,6 +953,9 @@ pub const Tokenizer = struct {
903953 '%' => {
904954 state = .minus_percent;
905955 },
956 '|' => {
957 state = .minus_pipe;
958 },
906959 else => {
907960 result.tag = .minus;
908961 break;
......@@ -920,6 +973,17 @@ pub const Tokenizer = struct {
920973 break;
921974 },
922975 },
976 .minus_pipe => switch (c) {
977 '=' => {
978 result.tag = .minus_pipe_equal;
979 self.index += 1;
980 break;
981 },
982 else => {
983 result.tag = .minus_pipe;
984 break;
985 },
986 },
923987
924988 .angle_bracket_left => switch (c) {
925989 '<' => {
......@@ -942,12 +1006,27 @@ pub const Tokenizer = struct {
9421006 self.index += 1;
9431007 break;
9441008 },
1009 '|' => {
1010 result.tag = .angle_bracket_angle_bracket_left_pipe;
1011 },
9451012 else => {
9461013 result.tag = .angle_bracket_angle_bracket_left;
9471014 break;
9481015 },
9491016 },
9501017
1018 .angle_bracket_angle_bracket_left_pipe => switch (c) {
1019 '=' => {
1020 result.tag = .angle_bracket_angle_bracket_left_pipe_equal;
1021 self.index += 1;
1022 break;
1023 },
1024 else => {
1025 result.tag = .angle_bracket_angle_bracket_left_pipe;
1026 break;
1027 },
1028 },
1029
9511030 .angle_bracket_right => switch (c) {
9521031 '>' => {
9531032 state = .angle_bracket_angle_bracket_right;
src/Air.zig+22
......@@ -44,6 +44,11 @@ pub const Inst = struct {
4444 /// is the same as both operands.
4545 /// Uses the `bin_op` field.
4646 addwrap,
47 /// Saturating integer addition.
48 /// Both operands are guaranteed to be the same type, and the result type
49 /// is the same as both operands.
50 /// Uses the `bin_op` field.
51 addsat,
4752 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
4853 /// Both operands are guaranteed to be the same type, and the result type
4954 /// is the same as both operands.
......@@ -54,6 +59,11 @@ pub const Inst = struct {
5459 /// is the same as both operands.
5560 /// Uses the `bin_op` field.
5661 subwrap,
62 /// Saturating integer subtraction.
63 /// Both operands are guaranteed to be the same type, and the result type
64 /// is the same as both operands.
65 /// Uses the `bin_op` field.
66 subsat,
5767 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
5868 /// Both operands are guaranteed to be the same type, and the result type
5969 /// is the same as both operands.
......@@ -64,6 +74,11 @@ pub const Inst = struct {
6474 /// is the same as both operands.
6575 /// Uses the `bin_op` field.
6676 mulwrap,
77 /// Saturating integer multiplication.
78 /// Both operands are guaranteed to be the same type, and the result type
79 /// is the same as both operands.
80 /// Uses the `bin_op` field.
81 mulsat,
6782 /// Integer or float division. For integers, wrapping is undefined behavior.
6883 /// Both operands are guaranteed to be the same type, and the result type
6984 /// is the same as both operands.
......@@ -110,6 +125,9 @@ pub const Inst = struct {
110125 /// Shift left. `<<`
111126 /// Uses the `bin_op` field.
112127 shl,
128 /// Shift left saturating. `<<|`
129 /// Uses the `bin_op` field.
130 shl_sat,
113131 /// Bitwise XOR. `^`
114132 /// Uses the `bin_op` field.
115133 xor,
......@@ -568,10 +586,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
568586
569587 .add,
570588 .addwrap,
589 .addsat,
571590 .sub,
572591 .subwrap,
592 .subsat,
573593 .mul,
574594 .mulwrap,
595 .mulsat,
575596 .div,
576597 .rem,
577598 .mod,
......@@ -582,6 +603,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
582603 .ptr_sub,
583604 .shr,
584605 .shl,
606 .shl_sat,
585607 => return air.typeOf(datas[inst].bin_op.lhs),
586608
587609 .cmp_lt,
src/AstGen.zig+122-2
......@@ -318,27 +318,35 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
318318 .assign_bit_and,
319319 .assign_bit_or,
320320 .assign_bit_shift_left,
321 .assign_bit_shift_left_sat,
321322 .assign_bit_shift_right,
322323 .assign_bit_xor,
323324 .assign_div,
324325 .assign_sub,
325326 .assign_sub_wrap,
327 .assign_sub_sat,
326328 .assign_mod,
327329 .assign_add,
328330 .assign_add_wrap,
331 .assign_add_sat,
329332 .assign_mul,
330333 .assign_mul_wrap,
334 .assign_mul_sat,
331335 .add,
332336 .add_wrap,
337 .add_sat,
333338 .sub,
334339 .sub_wrap,
340 .sub_sat,
335341 .mul,
336342 .mul_wrap,
343 .mul_sat,
337344 .div,
338345 .mod,
339346 .bit_and,
340347 .bit_or,
341348 .bit_shift_left,
349 .bit_shift_left_sat,
342350 .bit_shift_right,
343351 .bit_xor,
344352 .bang_equal,
......@@ -526,6 +534,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
526534 try assignShift(gz, scope, node, .shl);
527535 return rvalue(gz, rl, .void_value, node);
528536 },
537 .assign_bit_shift_left_sat => {
538 try assignBinOpExt(gz, scope, node, .shl_with_saturation);
539 return rvalue(gz, rl, .void_value, node);
540 },
529541 .assign_bit_shift_right => {
530542 try assignShift(gz, scope, node, .shr);
531543 return rvalue(gz, rl, .void_value, node);
......@@ -555,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
555567 try assignOp(gz, scope, node, .subwrap);
556568 return rvalue(gz, rl, .void_value, node);
557569 },
570 .assign_sub_sat => {
571 try assignBinOpExt(gz, scope, node, .sub_with_saturation);
572 return rvalue(gz, rl, .void_value, node);
573 },
558574 .assign_mod => {
559575 try assignOp(gz, scope, node, .mod_rem);
560576 return rvalue(gz, rl, .void_value, node);
......@@ -567,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
567583 try assignOp(gz, scope, node, .addwrap);
568584 return rvalue(gz, rl, .void_value, node);
569585 },
586 .assign_add_sat => {
587 try assignBinOpExt(gz, scope, node, .add_with_saturation);
588 return rvalue(gz, rl, .void_value, node);
589 },
570590 .assign_mul => {
571591 try assignOp(gz, scope, node, .mul);
572592 return rvalue(gz, rl, .void_value, node);
......@@ -575,17 +595,25 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
575595 try assignOp(gz, scope, node, .mulwrap);
576596 return rvalue(gz, rl, .void_value, node);
577597 },
598 .assign_mul_sat => {
599 try assignBinOpExt(gz, scope, node, .mul_with_saturation);
600 return rvalue(gz, rl, .void_value, node);
601 },
578602
579603 // zig fmt: off
580 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
581 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
604 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
605 .bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation),
606 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
582607
583608 .add => return simpleBinOp(gz, scope, rl, node, .add),
584609 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
610 .add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation),
585611 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
586612 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
613 .sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation),
587614 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
588615 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
616 .mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation),
589617 .div => return simpleBinOp(gz, scope, rl, node, .div),
590618 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
591619 .bit_and => {
......@@ -2685,6 +2713,31 @@ fn assignOp(
26852713 _ = try gz.addBin(.store, lhs_ptr, result);
26862714}
26872715
2716// TODO: is there an existing method to accomplish this?
2717// TODO: likely rename this to indicate rhs type coercion or add more params to make it more general
2718fn assignBinOpExt(
2719 gz: *GenZir,
2720 scope: *Scope,
2721 infix_node: Ast.Node.Index,
2722 op_inst_tag: Zir.Inst.Extended,
2723) InnerError!void {
2724 try emitDbgNode(gz, infix_node);
2725 const astgen = gz.astgen;
2726 const tree = astgen.tree;
2727 const node_datas = tree.nodes.items(.data);
2728
2729 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
2730 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2731 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
2732 const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);
2733 const result = try gz.addExtendedPayload(op_inst_tag, Zir.Inst.BinNode{
2734 .node = gz.nodeIndexToRelative(infix_node),
2735 .lhs = lhs,
2736 .rhs = rhs,
2737 });
2738 _ = try gz.addBin(.store, lhs_ptr, result);
2739}
2740
26882741fn assignShift(
26892742 gz: *GenZir,
26902743 scope: *Scope,
......@@ -2708,6 +2761,29 @@ fn assignShift(
27082761 _ = try gz.addBin(.store, lhs_ptr, result);
27092762}
27102763
2764fn assignShiftSat(
2765 gz: *GenZir,
2766 scope: *Scope,
2767 infix_node: ast.Node.Index,
2768 op_inst_tag: Zir.Inst.Tag,
2769) InnerError!void {
2770 try emitDbgNode(gz, infix_node);
2771 const astgen = gz.astgen;
2772 const tree = astgen.tree;
2773 const node_datas = tree.nodes.items(.data);
2774
2775 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
2776 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2777 const rhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
2778 const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);
2779
2780 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
2781 .lhs = lhs,
2782 .rhs = rhs,
2783 });
2784 _ = try gz.addBin(.store, lhs_ptr, result);
2785}
2786
27112787fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
27122788 const astgen = gz.astgen;
27132789 const tree = astgen.tree;
......@@ -7827,6 +7903,26 @@ fn shiftOp(
78277903 return rvalue(gz, rl, result, node);
78287904}
78297905
7906// TODO: is there an existing way to do this?
7907// TODO: likely rename this to reflect result_loc == .none or add more params to make it more general
7908fn binOpExt(
7909 gz: *GenZir,
7910 scope: *Scope,
7911 rl: ResultLoc,
7912 node: Ast.Node.Index,
7913 lhs_node: Ast.Node.Index,
7914 rhs_node: Ast.Node.Index,
7915 tag: Zir.Inst.Extended,
7916) InnerError!Zir.Inst.Ref {
7917 const lhs = try expr(gz, scope, .none, lhs_node);
7918 const rhs = try expr(gz, scope, .none, rhs_node);
7919 const result = try gz.addExtendedPayload(tag, Zir.Inst.Bin{
7920 .lhs = lhs,
7921 .rhs = rhs,
7922 });
7923 return rvalue(gz, rl, result, node);
7924}
7925
78307926fn cImport(
78317927 gz: *GenZir,
78327928 scope: *Scope,
......@@ -8119,26 +8215,32 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
81198215 .asm_simple,
81208216 .add,
81218217 .add_wrap,
8218 .add_sat,
81228219 .array_cat,
81238220 .array_mult,
81248221 .assign,
81258222 .assign_bit_and,
81268223 .assign_bit_or,
81278224 .assign_bit_shift_left,
8225 .assign_bit_shift_left_sat,
81288226 .assign_bit_shift_right,
81298227 .assign_bit_xor,
81308228 .assign_div,
81318229 .assign_sub,
81328230 .assign_sub_wrap,
8231 .assign_sub_sat,
81338232 .assign_mod,
81348233 .assign_add,
81358234 .assign_add_wrap,
8235 .assign_add_sat,
81368236 .assign_mul,
81378237 .assign_mul_wrap,
8238 .assign_mul_sat,
81388239 .bang_equal,
81398240 .bit_and,
81408241 .bit_or,
81418242 .bit_shift_left,
8243 .bit_shift_left_sat,
81428244 .bit_shift_right,
81438245 .bit_xor,
81448246 .bool_and,
......@@ -8154,10 +8256,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
81548256 .mod,
81558257 .mul,
81568258 .mul_wrap,
8259 .mul_sat,
81578260 .switch_range,
81588261 .field_access,
81598262 .sub,
81608263 .sub_wrap,
8264 .sub_sat,
81618265 .slice,
81628266 .slice_open,
81638267 .slice_sentinel,
......@@ -8352,26 +8456,32 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
83528456 .tagged_union_enum_tag_trailing,
83538457 .add,
83548458 .add_wrap,
8459 .add_sat,
83558460 .array_cat,
83568461 .array_mult,
83578462 .assign,
83588463 .assign_bit_and,
83598464 .assign_bit_or,
83608465 .assign_bit_shift_left,
8466 .assign_bit_shift_left_sat,
83618467 .assign_bit_shift_right,
83628468 .assign_bit_xor,
83638469 .assign_div,
83648470 .assign_sub,
83658471 .assign_sub_wrap,
8472 .assign_sub_sat,
83668473 .assign_mod,
83678474 .assign_add,
83688475 .assign_add_wrap,
8476 .assign_add_sat,
83698477 .assign_mul,
83708478 .assign_mul_wrap,
8479 .assign_mul_sat,
83718480 .bang_equal,
83728481 .bit_and,
83738482 .bit_or,
83748483 .bit_shift_left,
8484 .bit_shift_left_sat,
83758485 .bit_shift_right,
83768486 .bit_xor,
83778487 .bool_and,
......@@ -8387,9 +8497,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
83878497 .mod,
83888498 .mul,
83898499 .mul_wrap,
8500 .mul_sat,
83908501 .switch_range,
83918502 .sub,
83928503 .sub_wrap,
8504 .sub_sat,
83938505 .slice,
83948506 .slice_open,
83958507 .slice_sentinel,
......@@ -8524,26 +8636,32 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
85248636 .asm_simple,
85258637 .add,
85268638 .add_wrap,
8639 .add_sat,
85278640 .array_cat,
85288641 .array_mult,
85298642 .assign,
85308643 .assign_bit_and,
85318644 .assign_bit_or,
85328645 .assign_bit_shift_left,
8646 .assign_bit_shift_left_sat,
85338647 .assign_bit_shift_right,
85348648 .assign_bit_xor,
85358649 .assign_div,
85368650 .assign_sub,
85378651 .assign_sub_wrap,
8652 .assign_sub_sat,
85388653 .assign_mod,
85398654 .assign_add,
85408655 .assign_add_wrap,
8656 .assign_add_sat,
85418657 .assign_mul,
85428658 .assign_mul_wrap,
8659 .assign_mul_sat,
85438660 .bang_equal,
85448661 .bit_and,
85458662 .bit_or,
85468663 .bit_shift_left,
8664 .bit_shift_left_sat,
85478665 .bit_shift_right,
85488666 .bit_xor,
85498667 .bool_and,
......@@ -8559,10 +8677,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
85598677 .mod,
85608678 .mul,
85618679 .mul_wrap,
8680 .mul_sat,
85628681 .switch_range,
85638682 .field_access,
85648683 .sub,
85658684 .sub_wrap,
8685 .sub_sat,
85668686 .slice,
85678687 .slice_open,
85688688 .slice_sentinel,
src/Liveness.zig+4
......@@ -226,10 +226,13 @@ fn analyzeInst(
226226 switch (inst_tags[inst]) {
227227 .add,
228228 .addwrap,
229 .addsat,
229230 .sub,
230231 .subwrap,
232 .subsat,
231233 .mul,
232234 .mulwrap,
235 .mulsat,
233236 .div,
234237 .rem,
235238 .mod,
......@@ -252,6 +255,7 @@ fn analyzeInst(
252255 .ptr_elem_val,
253256 .ptr_ptr_elem_val,
254257 .shl,
258 .shl_sat,
255259 .shr,
256260 .atomic_store_unordered,
257261 .atomic_store_monotonic,
src/codegen.zig+12
......@@ -826,10 +826,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
826826 // zig fmt: off
827827 .add, .ptr_add => try self.airAdd(inst),
828828 .addwrap => try self.airAddWrap(inst),
829 .addsat => try self.airArithmeticOpSat(inst, "addsat"),
829830 .sub, .ptr_sub => try self.airSub(inst),
830831 .subwrap => try self.airSubWrap(inst),
832 .subsat => try self.airArithmeticOpSat(inst, "subsat"),
831833 .mul => try self.airMul(inst),
832834 .mulwrap => try self.airMulWrap(inst),
835 .mulsat => try self.airArithmeticOpSat(inst, "mulsat"),
833836 .div => try self.airDiv(inst),
834837 .rem => try self.airRem(inst),
835838 .mod => try self.airMod(inst),
......@@ -848,6 +851,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
848851 .xor => try self.airXor(inst),
849852 .shr => try self.airShr(inst),
850853 .shl => try self.airShl(inst),
854 .shl_sat => try self.airArithmeticOpSat(inst, "shl_sat"),
851855
852856 .alloc => try self.airAlloc(inst),
853857 .arg => try self.airArg(inst),
......@@ -1320,6 +1324,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13201324 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13211325 }
13221326
1327 fn airArithmeticOpSat(self: *Self, inst: Air.Inst.Index, comptime name: []const u8) !void {
1328 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1329 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1330 else => return self.fail("TODO implement " ++ name ++ " for {}", .{self.target.cpu.arch}),
1331 };
1332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1333 }
1334
13231335 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
13241336 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13251337 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+3
......@@ -885,14 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
885885 // that wrapping is UB.
886886 .add, .ptr_add => try airBinOp( f, inst, " + "),
887887 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
888 .addsat => return o.dg.fail("TODO: C backend: implement codegen for addsat", .{}),
888889 // TODO use a different strategy for sub that communicates to the optimizer
889890 // that wrapping is UB.
890891 .sub, .ptr_sub => try airBinOp( f, inst, " - "),
891892 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
893 .subsat => return o.dg.fail("TODO: C backend: implement codegen for subsat", .{}),
892894 // TODO use a different strategy for mul that communicates to the optimizer
893895 // that wrapping is UB.
894896 .mul => try airBinOp( f, inst, " * "),
895897 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
898 .mulsat => return o.dg.fail("TODO: C backend: implement codegen for mulsat", .{}),
896899 // TODO use a different strategy for div that communicates to the optimizer
897900 // that wrapping is UB.
898901 .div => try airBinOp( f, inst, " / "),
src/codegen/llvm.zig+49-17
......@@ -1236,12 +1236,15 @@ pub const FuncGen = struct {
12361236 for (body) |inst| {
12371237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
12381238 // zig fmt: off
1239 .add => try self.airAdd(inst, false),
1240 .addwrap => try self.airAdd(inst, true),
1241 .sub => try self.airSub(inst, false),
1242 .subwrap => try self.airSub(inst, true),
1243 .mul => try self.airMul(inst, false),
1244 .mulwrap => try self.airMul(inst, true),
1239 .add => try self.airAdd(inst, .standard),
1240 .addwrap => try self.airAdd(inst, .wrapping),
1241 .addsat => try self.airAdd(inst, .saturated),
1242 .sub => try self.airSub(inst, .standard),
1243 .subwrap => try self.airSub(inst, .wrapping),
1244 .subsat => try self.airSub(inst, .saturated),
1245 .mul => try self.airMul(inst, .standard),
1246 .mulwrap => try self.airMul(inst, .wrapping),
1247 .mulsat => try self.airMul(inst, .saturated),
12451248 .div => try self.airDiv(inst),
12461249 .rem => try self.airRem(inst),
12471250 .mod => try self.airMod(inst),
......@@ -1252,7 +1255,8 @@ pub const FuncGen = struct {
12521255 .bit_or, .bool_or => try self.airOr(inst),
12531256 .xor => try self.airXor(inst),
12541257
1255 .shl => try self.airShl(inst),
1258 .shl => try self.airShl(inst, false),
1259 .shl_sat => try self.airShl(inst, true),
12561260 .shr => try self.airShr(inst),
12571261
12581262 .cmp_eq => try self.airCmp(inst, .eq),
......@@ -2024,7 +2028,8 @@ pub const FuncGen = struct {
20242028 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
20252029 }
20262030
2027 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2031 const ArithmeticType = enum { standard, wrapping, saturated };
2032 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
20282033 if (self.liveness.isUnused(inst))
20292034 return null;
20302035
......@@ -2033,13 +2038,20 @@ pub const FuncGen = struct {
20332038 const rhs = try self.resolveInst(bin_op.rhs);
20342039 const inst_ty = self.air.typeOfIndex(inst);
20352040
2036 if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2037 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
2041 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2042 if (ty == .wrapping)
2043 return self.builder.buildAdd(lhs, rhs, "")
2044 else if (ty == .saturated) {
2045 if (inst_ty.isSignedInt())
2046 return self.builder.buildSAddSat(lhs, rhs, "")
2047 else
2048 return self.builder.buildUAddSat(lhs, rhs, "");
2049 }
20382050 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
20392051 return self.builder.buildNUWAdd(lhs, rhs, "");
20402052 }
20412053
2042 fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2054 fn airSub(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
20432055 if (self.liveness.isUnused(inst))
20442056 return null;
20452057
......@@ -2048,13 +2060,20 @@ pub const FuncGen = struct {
20482060 const rhs = try self.resolveInst(bin_op.rhs);
20492061 const inst_ty = self.air.typeOfIndex(inst);
20502062
2051 if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
2052 if (wrap) return self.builder.buildSub(lhs, rhs, "");
2063 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");
2064 if (ty == .wrapping)
2065 return self.builder.buildSub(lhs, rhs, "")
2066 else if (ty == .saturated) {
2067 if (inst_ty.isSignedInt())
2068 return self.builder.buildSSubSat(lhs, rhs, "")
2069 else
2070 return self.builder.buildUSubSat(lhs, rhs, "");
2071 }
20532072 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
20542073 return self.builder.buildNUWSub(lhs, rhs, "");
20552074 }
20562075
2057 fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2076 fn airMul(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
20582077 if (self.liveness.isUnused(inst))
20592078 return null;
20602079
......@@ -2063,8 +2082,15 @@ pub const FuncGen = struct {
20632082 const rhs = try self.resolveInst(bin_op.rhs);
20642083 const inst_ty = self.air.typeOfIndex(inst);
20652084
2066 if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
2067 if (wrap) return self.builder.buildMul(lhs, rhs, "");
2085 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");
2086 if (ty == .wrapping)
2087 return self.builder.buildMul(lhs, rhs, "")
2088 else if (ty == .saturated) {
2089 if (inst_ty.isSignedInt())
2090 return self.builder.buildSMulFixSat(lhs, rhs, "")
2091 else
2092 return self.builder.buildUMulFixSat(lhs, rhs, "");
2093 }
20682094 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
20692095 return self.builder.buildNUWMul(lhs, rhs, "");
20702096 }
......@@ -2174,7 +2200,7 @@ pub const FuncGen = struct {
21742200 return self.builder.buildXor(lhs, rhs, "");
21752201 }
21762202
2177 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2203 fn airShl(self: *FuncGen, inst: Air.Inst.Index, sat: bool) !?*const llvm.Value {
21782204 if (self.liveness.isUnused(inst))
21792205 return null;
21802206 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -2186,6 +2212,12 @@ pub const FuncGen = struct {
21862212 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
21872213 else
21882214 rhs;
2215 if (sat) {
2216 return if (lhs_type.isSignedInt())
2217 self.builder.buildSShlSat(lhs, casted_rhs, "")
2218 else
2219 self.builder.buildUShlSat(lhs, casted_rhs, "");
2220 }
21892221 return self.builder.buildShl(lhs, casted_rhs, "");
21902222 }
21912223
src/codegen/llvm/bindings.zig+24
......@@ -397,6 +397,12 @@ pub const Builder = opaque {
397397 pub const buildNUWAdd = LLVMBuildNUWAdd;
398398 extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
399399
400 pub const buildSAddSat = ZigLLVMBuildSAddSat;
401 extern fn ZigLLVMBuildSAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
402
403 pub const buildUAddSat = ZigLLVMBuildUAddSat;
404 extern fn ZigLLVMBuildUAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
405
400406 pub const buildFSub = LLVMBuildFSub;
401407 extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
402408
......@@ -409,6 +415,12 @@ pub const Builder = opaque {
409415 pub const buildNUWSub = LLVMBuildNUWSub;
410416 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
411417
418 pub const buildSSubSat = ZigLLVMBuildSSubSat;
419 extern fn ZigLLVMBuildSSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
420
421 pub const buildUSubSat = ZigLLVMBuildUSubSat;
422 extern fn ZigLLVMBuildUSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
423
412424 pub const buildFMul = LLVMBuildFMul;
413425 extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
414426
......@@ -421,6 +433,12 @@ pub const Builder = opaque {
421433 pub const buildNUWMul = LLVMBuildNUWMul;
422434 extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
423435
436 pub const buildSMulFixSat = ZigLLVMBuildSMulFixSat;
437 extern fn ZigLLVMBuildSMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
438
439 pub const buildUMulFixSat = ZigLLVMBuildUMulFixSat;
440 extern fn ZigLLVMBuildUMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
441
424442 pub const buildUDiv = LLVMBuildUDiv;
425443 extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
426444
......@@ -451,6 +469,12 @@ pub const Builder = opaque {
451469 pub const buildShl = LLVMBuildShl;
452470 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
453471
472 pub const buildSShlSat = ZigLLVMBuildSShlSat;
473 extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
474
475 pub const buildUShlSat = ZigLLVMBuildUShlSat;
476 extern fn ZigLLVMBuildUShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
477
454478 pub const buildOr = LLVMBuildOr;
455479 extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
456480
src/print_air.zig+4
......@@ -104,10 +104,13 @@ const Writer = struct {
104104
105105 .add,
106106 .addwrap,
107 .addsat,
107108 .sub,
108109 .subwrap,
110 .subsat,
109111 .mul,
110112 .mulwrap,
113 .mulsat,
111114 .div,
112115 .rem,
113116 .mod,
......@@ -130,6 +133,7 @@ const Writer = struct {
130133 .ptr_elem_val,
131134 .ptr_ptr_elem_val,
132135 .shl,
136 .shl_sat,
133137 .shr,
134138 .set_union_tag,
135139 => try w.writeBinOp(s, inst),
src/stage1/all_types.hpp+12-4
......@@ -812,14 +812,18 @@ enum BinOpType {
812812 BinOpTypeInvalid,
813813 BinOpTypeAssign,
814814 BinOpTypeAssignTimes,
815 BinOpTypeAssignTimesSat,
815816 BinOpTypeAssignTimesWrap,
816817 BinOpTypeAssignDiv,
817818 BinOpTypeAssignMod,
818819 BinOpTypeAssignPlus,
820 BinOpTypeAssignPlusSat,
819821 BinOpTypeAssignPlusWrap,
820822 BinOpTypeAssignMinus,
823 BinOpTypeAssignMinusSat,
821824 BinOpTypeAssignMinusWrap,
822825 BinOpTypeAssignBitShiftLeft,
826 BinOpTypeAssignBitShiftLeftSat,
823827 BinOpTypeAssignBitShiftRight,
824828 BinOpTypeAssignBitAnd,
825829 BinOpTypeAssignBitXor,
......@@ -836,12 +840,16 @@ enum BinOpType {
836840 BinOpTypeBinXor,
837841 BinOpTypeBinAnd,
838842 BinOpTypeBitShiftLeft,
843 BinOpTypeBitShiftLeftSat,
839844 BinOpTypeBitShiftRight,
840845 BinOpTypeAdd,
846 BinOpTypeAddSat,
841847 BinOpTypeAddWrap,
842848 BinOpTypeSub,
849 BinOpTypeSubSat,
843850 BinOpTypeSubWrap,
844851 BinOpTypeMult,
852 BinOpTypeMultSat,
845853 BinOpTypeMultWrap,
846854 BinOpTypeDiv,
847855 BinOpTypeMod,
......@@ -2958,10 +2966,10 @@ enum IrBinOp {
29582966 IrBinOpArrayMult,
29592967 IrBinOpMaximum,
29602968 IrBinOpMinimum,
2961 IrBinOpSatAdd,
2962 IrBinOpSatSub,
2963 IrBinOpSatMul,
2964 IrBinOpSatShl,
2969 IrBinOpAddSat,
2970 IrBinOpSubSat,
2971 IrBinOpMultSat,
2972 IrBinOpShlSat,
29652973};
29662974
29672975struct Stage1ZirInstBinOp {
src/stage1/astgen.cpp+20-4
......@@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
36723672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
36733673 case BinOpTypeAssignTimesWrap:
36743674 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
3675 case BinOpTypeAssignTimesSat:
3676 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultSat), lval, result_loc);
36753677 case BinOpTypeAssignDiv:
36763678 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
36773679 case BinOpTypeAssignMod:
......@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
36803682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
36813683 case BinOpTypeAssignPlusWrap:
36823684 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
3685 case BinOpTypeAssignPlusSat:
3686 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddSat), lval, result_loc);
36833687 case BinOpTypeAssignMinus:
36843688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
36853689 case BinOpTypeAssignMinusWrap:
36863690 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
3691 case BinOpTypeAssignMinusSat:
3692 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubSat), lval, result_loc);
36873693 case BinOpTypeAssignBitShiftLeft:
36883694 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
3695 case BinOpTypeAssignBitShiftLeftSat:
3696 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpShlSat), lval, result_loc);
36893697 case BinOpTypeAssignBitShiftRight:
36903698 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
36913699 case BinOpTypeAssignBitAnd:
......@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
37183726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
37193727 case BinOpTypeBitShiftLeft:
37203728 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
3729 case BinOpTypeBitShiftLeftSat:
3730 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpShlSat), lval, result_loc);
37213731 case BinOpTypeBitShiftRight:
37223732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
37233733 case BinOpTypeAdd:
37243734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
37253735 case BinOpTypeAddWrap:
37263736 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
3737 case BinOpTypeAddSat:
3738 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddSat), lval, result_loc);
37273739 case BinOpTypeSub:
37283740 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
37293741 case BinOpTypeSubWrap:
37303742 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
3743 case BinOpTypeSubSat:
3744 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubSat), lval, result_loc);
37313745 case BinOpTypeMult:
37323746 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
37333747 case BinOpTypeMultWrap:
37343748 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
3749 case BinOpTypeMultSat:
3750 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultSat), lval, result_loc);
37353751 case BinOpTypeDiv:
37363752 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
37373753 case BinOpTypeMod:
......@@ -4716,7 +4732,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47164732 if (arg1_value == ag->codegen->invalid_inst_src)
47174733 return arg1_value;
47184734
4719 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatAdd, arg0_value, arg1_value, true);
4735 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpAddSat, arg0_value, arg1_value, true);
47204736 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47214737 }
47224738 case BuiltinFnIdSatSub:
......@@ -4731,7 +4747,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47314747 if (arg1_value == ag->codegen->invalid_inst_src)
47324748 return arg1_value;
47334749
4734 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatSub, arg0_value, arg1_value, true);
4750 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSubSat, arg0_value, arg1_value, true);
47354751 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47364752 }
47374753 case BuiltinFnIdSatMul:
......@@ -4746,7 +4762,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47464762 if (arg1_value == ag->codegen->invalid_inst_src)
47474763 return arg1_value;
47484764
4749 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatMul, arg0_value, arg1_value, true);
4765 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMultSat, arg0_value, arg1_value, true);
47504766 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47514767 }
47524768 case BuiltinFnIdSatShl:
......@@ -4761,7 +4777,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47614777 if (arg1_value == ag->codegen->invalid_inst_src)
47624778 return arg1_value;
47634779
4764 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatShl, arg0_value, arg1_value, true);
4780 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpShlSat, arg0_value, arg1_value, true);
47654781 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47664782 }
47674783 case BuiltinFnIdMemcpy:
src/stage1/codegen.cpp+4-4
......@@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33333333 } else {
33343334 zig_unreachable();
33353335 }
3336 case IrBinOpSatAdd:
3336 case IrBinOpAddSat:
33373337 if (scalar_type->id == ZigTypeIdInt) {
33383338 if (scalar_type->data.integral.is_signed) {
33393339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
......@@ -3343,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33433343 } else {
33443344 zig_unreachable();
33453345 }
3346 case IrBinOpSatSub:
3346 case IrBinOpSubSat:
33473347 if (scalar_type->id == ZigTypeIdInt) {
33483348 if (scalar_type->data.integral.is_signed) {
33493349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
......@@ -3353,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33533353 } else {
33543354 zig_unreachable();
33553355 }
3356 case IrBinOpSatMul:
3356 case IrBinOpMultSat:
33573357 if (scalar_type->id == ZigTypeIdInt) {
33583358 if (scalar_type->data.integral.is_signed) {
33593359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
......@@ -3363,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33633363 } else {
33643364 zig_unreachable();
33653365 }
3366 case IrBinOpSatShl:
3366 case IrBinOpShlSat:
33673367 if (scalar_type->id == ZigTypeIdInt) {
33683368 if (scalar_type->data.integral.is_signed) {
33693369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");
src/stage1/ir.cpp+12-12
......@@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
98209820 float_min(out_val, op1_val, op2_val);
98219821 }
98229822 break;
9823 case IrBinOpSatAdd:
9823 case IrBinOpAddSat:
98249824 if (is_int) {
98259825 bigint_add_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98269826 } else {
98279827 zig_unreachable();
98289828 }
98299829 break;
9830 case IrBinOpSatSub:
9830 case IrBinOpSubSat:
98319831 if (is_int) {
98329832 bigint_sub_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98339833 } else {
98349834 zig_unreachable();
98359835 }
98369836 break;
9837 case IrBinOpSatMul:
9837 case IrBinOpMultSat:
98389838 if (is_int) {
98399839 bigint_mul_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98409840 } else {
98419841 zig_unreachable();
98429842 }
98439843 break;
9844 case IrBinOpSatShl:
9844 case IrBinOpShlSat:
98459845 if (is_int) {
98469846 bigint_shl_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98479847 } else {
......@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
1006910069 case IrBinOpBitShiftRightExact:
1007010070 case IrBinOpAddWrap:
1007110071 case IrBinOpSubWrap:
10072 case IrBinOpSatAdd:
10073 case IrBinOpSatSub:
10074 case IrBinOpSatMul:
10075 case IrBinOpSatShl:
10072 case IrBinOpAddSat:
10073 case IrBinOpSubSat:
10074 case IrBinOpMultSat:
10075 case IrBinOpShlSat:
1007610076 case IrBinOpMultWrap:
1007710077 case IrBinOpArrayCat:
1007810078 case IrBinOpArrayMult:
......@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
1104611046 case IrBinOpRemMod:
1104711047 case IrBinOpMaximum:
1104811048 case IrBinOpMinimum:
11049 case IrBinOpSatAdd:
11050 case IrBinOpSatSub:
11051 case IrBinOpSatMul:
11052 case IrBinOpSatShl:
11049 case IrBinOpAddSat:
11050 case IrBinOpSubSat:
11051 case IrBinOpMultSat:
11052 case IrBinOpShlSat:
1105311053 return ir_analyze_bin_op_math(ira, bin_op_instruction);
1105411054 case IrBinOpArrayCat:
1105511055 return ir_analyze_array_cat(ira, bin_op_instruction);
src/stage1/ir_print.cpp+4-4
......@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
737737 return "@maximum";
738738 case IrBinOpMinimum:
739739 return "@minimum";
740 case IrBinOpSatAdd:
740 case IrBinOpAddSat:
741741 return "@addWithSaturation";
742 case IrBinOpSatSub:
742 case IrBinOpSubSat:
743743 return "@subWithSaturation";
744 case IrBinOpSatMul:
744 case IrBinOpMultSat:
745745 return "@mulWithSaturation";
746 case IrBinOpSatShl:
746 case IrBinOpShlSat:
747747 return "@shlWithSaturation";
748748 }
749749 zig_unreachable();
src/stage1/parser.cpp+16
......@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23812381// / PLUSEQUAL
23822382// / MINUSEQUAL
23832383// / LARROW2EQUAL
2384// / LARROW2PIPEEQUAL
23842385// / RARROW2EQUAL
23852386// / AMPERSANDEQUAL
23862387// / CARETEQUAL
......@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23882389// / ASTERISKPERCENTEQUAL
23892390// / PLUSPERCENTEQUAL
23902391// / MINUSPERCENTEQUAL
2392// / ASTERISKPIPEEQUAL
2393// / PLUSPIPEEQUAL
2394// / MINUSPIPEEQUAL
23912395// / EQUAL
23922396static AstNode *ast_parse_assign_op(ParseContext *pc) {
23932397 // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't
......@@ -2396,17 +2400,21 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) {
23962400 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
23972401 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
23982402 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
2403 table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
23992404 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
24002405 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
24012406 table[TokenIdDivEq] = BinOpTypeAssignDiv;
24022407 table[TokenIdEq] = BinOpTypeAssign;
24032408 table[TokenIdMinusEq] = BinOpTypeAssignMinus;
24042409 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
2410 table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
24052411 table[TokenIdModEq] = BinOpTypeAssignMod;
24062412 table[TokenIdPlusEq] = BinOpTypeAssignPlus;
24072413 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
2414 table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
24082415 table[TokenIdTimesEq] = BinOpTypeAssignTimes;
24092416 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
2417 table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
24102418
24112419 BinOpType op = table[pc->token_ids[pc->current_token]];
24122420 if (op != BinOpTypeInvalid) {
......@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
24832491
24842492// BitShiftOp
24852493// <- LARROW2
2494// / LARROW2PIPE
24862495// / RARROW2
24872496static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
24882497 BinOpType table[TokenIdCount] = {};
24892498 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
2499 table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
24902500 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
24912501
24922502 BinOpType op = table[pc->token_ids[pc->current_token]];
......@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
25062516// / PLUS2
25072517// / PLUSPERCENT
25082518// / MINUSPERCENT
2519// / PLUSPIPE
2520// / MINUSPIPE
25092521static AstNode *ast_parse_addition_op(ParseContext *pc) {
25102522 BinOpType table[TokenIdCount] = {};
25112523 table[TokenIdPlus] = BinOpTypeAdd;
......@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25132525 table[TokenIdPlusPlus] = BinOpTypeArrayCat;
25142526 table[TokenIdPlusPercent] = BinOpTypeAddWrap;
25152527 table[TokenIdMinusPercent] = BinOpTypeSubWrap;
2528 table[TokenIdPlusPipe] = BinOpTypeAddSat;
2529 table[TokenIdMinusPipe] = BinOpTypeSubSat;
25162530
25172531 BinOpType op = table[pc->token_ids[pc->current_token]];
25182532 if (op != BinOpTypeInvalid) {
......@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25322546// / PERCENT
25332547// / ASTERISK2
25342548// / ASTERISKPERCENT
2549// / ASTERISKPIPE
25352550static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25362551 BinOpType table[TokenIdCount] = {};
25372552 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
......@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25402555 table[TokenIdPercent] = BinOpTypeMod;
25412556 table[TokenIdStarStar] = BinOpTypeArrayMult;
25422557 table[TokenIdTimesPercent] = BinOpTypeMultWrap;
2558 table[TokenIdTimesPipe] = BinOpTypeMultSat;
25432559
25442560 BinOpType op = table[pc->token_ids[pc->current_token]];
25452561 if (op != BinOpTypeInvalid) {
src/stage1/tokenizer.cpp+85
......@@ -226,8 +226,10 @@ enum TokenizeState {
226226 TokenizeState_pipe,
227227 TokenizeState_minus,
228228 TokenizeState_minus_percent,
229 TokenizeState_minus_pipe,
229230 TokenizeState_asterisk,
230231 TokenizeState_asterisk_percent,
232 TokenizeState_asterisk_pipe,
231233 TokenizeState_slash,
232234 TokenizeState_line_comment_start,
233235 TokenizeState_line_comment,
......@@ -257,8 +259,10 @@ enum TokenizeState {
257259 TokenizeState_percent,
258260 TokenizeState_plus,
259261 TokenizeState_plus_percent,
262 TokenizeState_plus_pipe,
260263 TokenizeState_angle_bracket_left,
261264 TokenizeState_angle_bracket_angle_bracket_left,
265 TokenizeState_angle_bracket_angle_bracket_left_pipe,
262266 TokenizeState_angle_bracket_right,
263267 TokenizeState_angle_bracket_angle_bracket_right,
264268 TokenizeState_period,
......@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
548552 case '%':
549553 t.state = TokenizeState_asterisk_percent;
550554 break;
555 case '|':
556 t.state = TokenizeState_asterisk_pipe;
557 break;
551558 default:
552559 t.state = TokenizeState_start;
553560 continue;
......@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
568575 continue;
569576 }
570577 break;
578 case TokenizeState_asterisk_pipe:
579 switch (c) {
580 case 0:
581 t.out->ids.last() = TokenIdTimesPipe;
582 goto eof;
583 case '=':
584 t.out->ids.last() = TokenIdTimesPipeEq;
585 t.state = TokenizeState_start;
586 break;
587 default:
588 t.out->ids.last() = TokenIdTimesPipe;
589 t.state = TokenizeState_start;
590 continue;
591 }
592 break;
571593 case TokenizeState_percent:
572594 switch (c) {
573595 case 0:
......@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
596618 case '%':
597619 t.state = TokenizeState_plus_percent;
598620 break;
621 case '|':
622 t.state = TokenizeState_plus_pipe;
623 break;
599624 default:
600625 t.state = TokenizeState_start;
601626 continue;
......@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
616641 continue;
617642 }
618643 break;
644 case TokenizeState_plus_pipe:
645 switch (c) {
646 case 0:
647 t.out->ids.last() = TokenIdPlusPipe;
648 goto eof;
649 case '=':
650 t.out->ids.last() = TokenIdPlusPipeEq;
651 t.state = TokenizeState_start;
652 break;
653 default:
654 t.out->ids.last() = TokenIdPlusPipe;
655 t.state = TokenizeState_start;
656 continue;
657 }
658 break;
619659 case TokenizeState_caret:
620660 switch (c) {
621661 case 0:
......@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
891931 case '%':
892932 t.state = TokenizeState_minus_percent;
893933 break;
934 case '|':
935 t.state = TokenizeState_minus_pipe;
936 break;
894937 default:
895938 t.state = TokenizeState_start;
896939 continue;
......@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
911954 continue;
912955 }
913956 break;
957 case TokenizeState_minus_pipe:
958 switch (c) {
959 case 0:
960 t.out->ids.last() = TokenIdMinusPipe;
961 goto eof;
962 case '=':
963 t.out->ids.last() = TokenIdMinusPipeEq;
964 t.state = TokenizeState_start;
965 break;
966 default:
967 t.out->ids.last() = TokenIdMinusPipe;
968 t.state = TokenizeState_start;
969 continue;
970 }
971 break;
914972 case TokenizeState_angle_bracket_left:
915973 switch (c) {
916974 case 0:
......@@ -936,12 +994,31 @@ void tokenize(const char *source, Tokenization *out) {
936994 t.out->ids.last() = TokenIdBitShiftLeftEq;
937995 t.state = TokenizeState_start;
938996 break;
997 case '|':
998 // t.out->ids.last() = TokenIdBitShiftLeftPipe;
999 t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
1000 break;
9391001 default:
9401002 t.out->ids.last() = TokenIdBitShiftLeft;
9411003 t.state = TokenizeState_start;
9421004 continue;
9431005 }
9441006 break;
1007 case TokenizeState_angle_bracket_angle_bracket_left_pipe:
1008 switch (c) {
1009 case 0:
1010 t.out->ids.last() = TokenIdBitShiftLeftPipe;
1011 goto eof;
1012 case '=':
1013 t.out->ids.last() = TokenIdBitShiftLeftPipeEq;
1014 t.state = TokenizeState_start;
1015 break;
1016 default:
1017 t.out->ids.last() = TokenIdBitShiftLeftPipe;
1018 t.state = TokenizeState_start;
1019 continue;
1020 }
1021 break;
9451022 case TokenizeState_angle_bracket_right:
9461023 switch (c) {
9471024 case 0:
......@@ -1437,6 +1514,8 @@ const char * token_name(TokenId id) {
14371514 case TokenIdBitOrEq: return "|=";
14381515 case TokenIdBitShiftLeft: return "<<";
14391516 case TokenIdBitShiftLeftEq: return "<<=";
1517 case TokenIdBitShiftLeftPipe: return "<<|";
1518 case TokenIdBitShiftLeftPipeEq: return "<<|=";
14401519 case TokenIdBitShiftRight: return ">>";
14411520 case TokenIdBitShiftRightEq: return ">>=";
14421521 case TokenIdBitXorEq: return "^=";
......@@ -1521,12 +1600,16 @@ const char * token_name(TokenId id) {
15211600 case TokenIdMinusEq: return "-=";
15221601 case TokenIdMinusPercent: return "-%";
15231602 case TokenIdMinusPercentEq: return "-%=";
1603 case TokenIdMinusPipe: return "-|";
1604 case TokenIdMinusPipeEq: return "-|=";
15241605 case TokenIdModEq: return "%=";
15251606 case TokenIdPercent: return "%";
15261607 case TokenIdPlus: return "+";
15271608 case TokenIdPlusEq: return "+=";
15281609 case TokenIdPlusPercent: return "+%";
15291610 case TokenIdPlusPercentEq: return "+%=";
1611 case TokenIdPlusPipe: return "+|";
1612 case TokenIdPlusPipeEq: return "+|=";
15301613 case TokenIdPlusPlus: return "++";
15311614 case TokenIdRBrace: return "}";
15321615 case TokenIdRBracket: return "]";
......@@ -1542,6 +1625,8 @@ const char * token_name(TokenId id) {
15421625 case TokenIdTimesEq: return "*=";
15431626 case TokenIdTimesPercent: return "*%";
15441627 case TokenIdTimesPercentEq: return "*%=";
1628 case TokenIdTimesPipe: return "*|";
1629 case TokenIdTimesPipeEq: return "*|=";
15451630 case TokenIdBuiltin: return "Builtin";
15461631 case TokenIdCount:
15471632 zig_unreachable();
src/stage1/tokenizer.hpp+8
......@@ -23,6 +23,8 @@ enum TokenId : uint8_t {
2323 TokenIdBitOrEq,
2424 TokenIdBitShiftLeft,
2525 TokenIdBitShiftLeftEq,
26 TokenIdBitShiftLeftPipe,
27 TokenIdBitShiftLeftPipeEq,
2628 TokenIdBitShiftRight,
2729 TokenIdBitShiftRightEq,
2830 TokenIdBitXorEq,
......@@ -108,12 +110,16 @@ enum TokenId : uint8_t {
108110 TokenIdMinusEq,
109111 TokenIdMinusPercent,
110112 TokenIdMinusPercentEq,
113 TokenIdMinusPipe,
114 TokenIdMinusPipeEq,
111115 TokenIdModEq,
112116 TokenIdPercent,
113117 TokenIdPlus,
114118 TokenIdPlusEq,
115119 TokenIdPlusPercent,
116120 TokenIdPlusPercentEq,
121 TokenIdPlusPipe,
122 TokenIdPlusPipeEq,
117123 TokenIdPlusPlus,
118124 TokenIdRBrace,
119125 TokenIdRBracket,
......@@ -129,6 +135,8 @@ enum TokenId : uint8_t {
129135 TokenIdTimesEq,
130136 TokenIdTimesPercent,
131137 TokenIdTimesPercentEq,
138 TokenIdTimesPipe,
139 TokenIdTimesPipeEq,
132140
133141 TokenIdCount,
134142};
test/behavior/saturating_arithmetic.zig+28-7
......@@ -11,13 +11,34 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
1111 const a = test_data[0];
1212 const b = test_data[1];
1313 const expected = test_data[2];
14 const actual = switch (op) {
15 .add => @addWithSaturation(a, b),
16 .sub => @subWithSaturation(a, b),
17 .mul => @mulWithSaturation(a, b),
18 .shl => @shlWithSaturation(a, b),
19 };
20 try expectEqual(expected, actual);
14 {
15 const actual = switch (op) {
16 .add => @addWithSaturation(a, b),
17 .sub => @subWithSaturation(a, b),
18 .mul => @mulWithSaturation(a, b),
19 .shl => @shlWithSaturation(a, b),
20 };
21 try expectEqual(expected, actual);
22 }
23 {
24 const actual = switch (op) {
25 .add => a +| b,
26 .sub => a -| b,
27 .mul => a *| b,
28 .shl => a <<| b,
29 };
30 try expectEqual(expected, actual);
31 }
32 {
33 var actual = a;
34 switch (op) {
35 .add => actual +|= b,
36 .sub => actual -|= b,
37 .mul => actual *|= b,
38 .shl => actual <<|= b,
39 }
40 try expectEqual(expected, actual);
41 }
2142}
2243
2344test "@addWithSaturation" {