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
This commit is contained in:
Travis Staloch 2021-09-02 13:50:24 -07:00 committed by Andrew Kelley
parent 79bc5891c1
commit 29f41896ed
21 changed files with 556 additions and 54 deletions

View file

@ -396,6 +396,7 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
.assign_add, .assign_add,
.assign_sub, .assign_sub,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_and, .assign_bit_and,
.assign_bit_xor, .assign_bit_xor,
@ -403,6 +404,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
.assign_mul_wrap, .assign_mul_wrap,
.assign_add_wrap, .assign_add_wrap,
.assign_sub_wrap, .assign_sub_wrap,
.assign_mul_sat,
.assign_add_sat,
.assign_sub_sat,
.assign, .assign,
.merge_error_sets, .merge_error_sets,
.mul, .mul,
@ -410,12 +414,16 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
.mod, .mod,
.array_mult, .array_mult,
.mul_wrap, .mul_wrap,
.mul_sat,
.add, .add,
.sub, .sub,
.array_cat, .array_cat,
.add_wrap, .add_wrap,
.sub_wrap, .sub_wrap,
.add_sat,
.sub_sat,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_and, .bit_and,
.bit_xor, .bit_xor,
@ -652,6 +660,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
.assign_add, .assign_add,
.assign_sub, .assign_sub,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_and, .assign_bit_and,
.assign_bit_xor, .assign_bit_xor,
@ -659,6 +668,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
.assign_mul_wrap, .assign_mul_wrap,
.assign_add_wrap, .assign_add_wrap,
.assign_sub_wrap, .assign_sub_wrap,
.assign_mul_sat,
.assign_add_sat,
.assign_sub_sat,
.assign, .assign,
.merge_error_sets, .merge_error_sets,
.mul, .mul,
@ -666,12 +678,16 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
.mod, .mod,
.array_mult, .array_mult,
.mul_wrap, .mul_wrap,
.mul_sat,
.add, .add,
.sub, .sub,
.array_cat, .array_cat,
.add_wrap, .add_wrap,
.sub_wrap, .sub_wrap,
.add_sat,
.sub_sat,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_and, .bit_and,
.bit_xor, .bit_xor,
@ -2525,6 +2541,8 @@ pub const Node = struct {
assign_sub, assign_sub,
/// `lhs <<= rhs`. main_token is op. /// `lhs <<= rhs`. main_token is op.
assign_bit_shift_left, assign_bit_shift_left,
/// `lhs <<|= rhs`. main_token is op.
assign_bit_shift_left_sat,
/// `lhs >>= rhs`. main_token is op. /// `lhs >>= rhs`. main_token is op.
assign_bit_shift_right, assign_bit_shift_right,
/// `lhs &= rhs`. main_token is op. /// `lhs &= rhs`. main_token is op.
@ -2539,6 +2557,12 @@ pub const Node = struct {
assign_add_wrap, assign_add_wrap,
/// `lhs -%= rhs`. main_token is op. /// `lhs -%= rhs`. main_token is op.
assign_sub_wrap, assign_sub_wrap,
/// `lhs *|= rhs`. main_token is op.
assign_mul_sat,
/// `lhs +|= rhs`. main_token is op.
assign_add_sat,
/// `lhs -|= rhs`. main_token is op.
assign_sub_sat,
/// `lhs = rhs`. main_token is op. /// `lhs = rhs`. main_token is op.
assign, assign,
/// `lhs || rhs`. main_token is the `||`. /// `lhs || rhs`. main_token is the `||`.
@ -2553,6 +2577,8 @@ pub const Node = struct {
array_mult, array_mult,
/// `lhs *% rhs`. main_token is the `*%`. /// `lhs *% rhs`. main_token is the `*%`.
mul_wrap, mul_wrap,
/// `lhs *| rhs`. main_token is the `*%`.
mul_sat,
/// `lhs + rhs`. main_token is the `+`. /// `lhs + rhs`. main_token is the `+`.
add, add,
/// `lhs - rhs`. main_token is the `-`. /// `lhs - rhs`. main_token is the `-`.
@ -2563,8 +2589,14 @@ pub const Node = struct {
add_wrap, add_wrap,
/// `lhs -% rhs`. main_token is the `-%`. /// `lhs -% rhs`. main_token is the `-%`.
sub_wrap, sub_wrap,
/// `lhs +| rhs`. main_token is the `+|`.
add_sat,
/// `lhs -| rhs`. main_token is the `-|`.
sub_sat,
/// `lhs << rhs`. main_token is the `<<`. /// `lhs << rhs`. main_token is the `<<`.
bit_shift_left, bit_shift_left,
/// `lhs <<| rhs`. main_token is the `<<|`.
bit_shift_left_sat,
/// `lhs >> rhs`. main_token is the `>>`. /// `lhs >> rhs`. main_token is the `>>`.
bit_shift_right, bit_shift_right,
/// `lhs & rhs`. main_token is the `&`. /// `lhs & rhs`. main_token is the `&`.

View file

@ -1269,6 +1269,7 @@ const Parser = struct {
.plus_equal => .assign_add, .plus_equal => .assign_add,
.minus_equal => .assign_sub, .minus_equal => .assign_sub,
.angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
.angle_bracket_angle_bracket_left_pipe_equal => .assign_bit_shift_left_sat,
.angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
.ampersand_equal => .assign_bit_and, .ampersand_equal => .assign_bit_and,
.caret_equal => .assign_bit_xor, .caret_equal => .assign_bit_xor,
@ -1276,6 +1277,9 @@ const Parser = struct {
.asterisk_percent_equal => .assign_mul_wrap, .asterisk_percent_equal => .assign_mul_wrap,
.plus_percent_equal => .assign_add_wrap, .plus_percent_equal => .assign_add_wrap,
.minus_percent_equal => .assign_sub_wrap, .minus_percent_equal => .assign_sub_wrap,
.asterisk_pipe_equal => .assign_mul_sat,
.plus_pipe_equal => .assign_add_sat,
.minus_pipe_equal => .assign_sub_sat,
.equal => .assign, .equal => .assign,
else => return expr, else => return expr,
}; };
@ -1343,6 +1347,7 @@ const Parser = struct {
.keyword_catch = .{ .prec = 40, .tag = .@"catch" }, .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
.angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left }, .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },
.angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .bit_shift_left_sat },
.angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right }, .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },
.plus = .{ .prec = 60, .tag = .add }, .plus = .{ .prec = 60, .tag = .add },
@ -1350,6 +1355,8 @@ const Parser = struct {
.plus_plus = .{ .prec = 60, .tag = .array_cat }, .plus_plus = .{ .prec = 60, .tag = .array_cat },
.plus_percent = .{ .prec = 60, .tag = .add_wrap }, .plus_percent = .{ .prec = 60, .tag = .add_wrap },
.minus_percent = .{ .prec = 60, .tag = .sub_wrap }, .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
.plus_pipe = .{ .prec = 60, .tag = .add_sat },
.minus_pipe = .{ .prec = 60, .tag = .sub_sat },
.pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets }, .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
.asterisk = .{ .prec = 70, .tag = .mul }, .asterisk = .{ .prec = 70, .tag = .mul },
@ -1357,6 +1364,7 @@ const Parser = struct {
.percent = .{ .prec = 70, .tag = .mod }, .percent = .{ .prec = 70, .tag = .mod },
.asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
.asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
.asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
}); });
fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index { fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {

View file

@ -333,26 +333,32 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
.add, .add,
.add_wrap, .add_wrap,
.add_sat,
.array_cat, .array_cat,
.array_mult, .array_mult,
.assign, .assign,
.assign_bit_and, .assign_bit_and,
.assign_bit_or, .assign_bit_or,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_xor, .assign_bit_xor,
.assign_div, .assign_div,
.assign_sub, .assign_sub,
.assign_sub_wrap, .assign_sub_wrap,
.assign_sub_sat,
.assign_mod, .assign_mod,
.assign_add, .assign_add,
.assign_add_wrap, .assign_add_wrap,
.assign_add_sat,
.assign_mul, .assign_mul,
.assign_mul_wrap, .assign_mul_wrap,
.assign_mul_sat,
.bang_equal, .bang_equal,
.bit_and, .bit_and,
.bit_or, .bit_or,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_xor, .bit_xor,
.bool_and, .bool_and,
@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
.mod, .mod,
.mul, .mul,
.mul_wrap, .mul_wrap,
.mul_sat,
.sub, .sub,
.sub_wrap, .sub_wrap,
.sub_sat,
.@"orelse", .@"orelse",
=> { => {
const infix = datas[node]; const infix = datas[node];

View file

@ -103,15 +103,21 @@ pub const Token = struct {
plus_equal, plus_equal,
plus_percent, plus_percent,
plus_percent_equal, plus_percent_equal,
plus_pipe,
plus_pipe_equal,
minus, minus,
minus_equal, minus_equal,
minus_percent, minus_percent,
minus_percent_equal, minus_percent_equal,
minus_pipe,
minus_pipe_equal,
asterisk, asterisk,
asterisk_equal, asterisk_equal,
asterisk_asterisk, asterisk_asterisk,
asterisk_percent, asterisk_percent,
asterisk_percent_equal, asterisk_percent_equal,
asterisk_pipe,
asterisk_pipe_equal,
arrow, arrow,
colon, colon,
slash, slash,
@ -124,6 +130,8 @@ pub const Token = struct {
angle_bracket_left_equal, angle_bracket_left_equal,
angle_bracket_angle_bracket_left, angle_bracket_angle_bracket_left,
angle_bracket_angle_bracket_left_equal, angle_bracket_angle_bracket_left_equal,
angle_bracket_angle_bracket_left_pipe,
angle_bracket_angle_bracket_left_pipe_equal,
angle_bracket_right, angle_bracket_right,
angle_bracket_right_equal, angle_bracket_right_equal,
angle_bracket_angle_bracket_right, angle_bracket_angle_bracket_right,
@ -227,15 +235,21 @@ pub const Token = struct {
.plus_equal => "+=", .plus_equal => "+=",
.plus_percent => "+%", .plus_percent => "+%",
.plus_percent_equal => "+%=", .plus_percent_equal => "+%=",
.plus_pipe => "+|",
.plus_pipe_equal => "+|=",
.minus => "-", .minus => "-",
.minus_equal => "-=", .minus_equal => "-=",
.minus_percent => "-%", .minus_percent => "-%",
.minus_percent_equal => "-%=", .minus_percent_equal => "-%=",
.minus_pipe => "-|",
.minus_pipe_equal => "-|=",
.asterisk => "*", .asterisk => "*",
.asterisk_equal => "*=", .asterisk_equal => "*=",
.asterisk_asterisk => "**", .asterisk_asterisk => "**",
.asterisk_percent => "*%", .asterisk_percent => "*%",
.asterisk_percent_equal => "*%=", .asterisk_percent_equal => "*%=",
.asterisk_pipe => "*|",
.asterisk_pipe_equal => "*|=",
.arrow => "->", .arrow => "->",
.colon => ":", .colon => ":",
.slash => "/", .slash => "/",
@ -248,6 +262,8 @@ pub const Token = struct {
.angle_bracket_left_equal => "<=", .angle_bracket_left_equal => "<=",
.angle_bracket_angle_bracket_left => "<<", .angle_bracket_angle_bracket_left => "<<",
.angle_bracket_angle_bracket_left_equal => "<<=", .angle_bracket_angle_bracket_left_equal => "<<=",
.angle_bracket_angle_bracket_left_pipe => "<<|",
.angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
.angle_bracket_right => ">", .angle_bracket_right => ">",
.angle_bracket_right_equal => ">=", .angle_bracket_right_equal => ">=",
.angle_bracket_angle_bracket_right => ">>", .angle_bracket_angle_bracket_right => ">>",
@ -352,8 +368,10 @@ pub const Tokenizer = struct {
pipe, pipe,
minus, minus,
minus_percent, minus_percent,
minus_pipe,
asterisk, asterisk,
asterisk_percent, asterisk_percent,
asterisk_pipe,
slash, slash,
line_comment_start, line_comment_start,
line_comment, line_comment,
@ -382,8 +400,10 @@ pub const Tokenizer = struct {
percent, percent,
plus, plus,
plus_percent, plus_percent,
plus_pipe,
angle_bracket_left, angle_bracket_left,
angle_bracket_angle_bracket_left, angle_bracket_angle_bracket_left,
angle_bracket_angle_bracket_left_pipe,
angle_bracket_right, angle_bracket_right,
angle_bracket_angle_bracket_right, angle_bracket_angle_bracket_right,
period, period,
@ -584,6 +604,9 @@ pub const Tokenizer = struct {
'%' => { '%' => {
state = .asterisk_percent; state = .asterisk_percent;
}, },
'|' => {
state = .asterisk_pipe;
},
else => { else => {
result.tag = .asterisk; result.tag = .asterisk;
break; break;
@ -602,6 +625,18 @@ pub const Tokenizer = struct {
}, },
}, },
.asterisk_pipe => switch (c) {
'=' => {
result.tag = .asterisk_pipe_equal;
self.index += 1;
break;
},
else => {
result.tag = .asterisk_pipe;
break;
},
},
.percent => switch (c) { .percent => switch (c) {
'=' => { '=' => {
result.tag = .percent_equal; result.tag = .percent_equal;
@ -628,6 +663,9 @@ pub const Tokenizer = struct {
'%' => { '%' => {
state = .plus_percent; state = .plus_percent;
}, },
'|' => {
state = .plus_pipe;
},
else => { else => {
result.tag = .plus; result.tag = .plus;
break; break;
@ -646,6 +684,18 @@ pub const Tokenizer = struct {
}, },
}, },
.plus_pipe => switch (c) {
'=' => {
result.tag = .plus_pipe_equal;
self.index += 1;
break;
},
else => {
result.tag = .plus_pipe;
break;
},
},
.caret => switch (c) { .caret => switch (c) {
'=' => { '=' => {
result.tag = .caret_equal; result.tag = .caret_equal;
@ -903,6 +953,9 @@ pub const Tokenizer = struct {
'%' => { '%' => {
state = .minus_percent; state = .minus_percent;
}, },
'|' => {
state = .minus_pipe;
},
else => { else => {
result.tag = .minus; result.tag = .minus;
break; break;
@ -920,6 +973,17 @@ pub const Tokenizer = struct {
break; break;
}, },
}, },
.minus_pipe => switch (c) {
'=' => {
result.tag = .minus_pipe_equal;
self.index += 1;
break;
},
else => {
result.tag = .minus_pipe;
break;
},
},
.angle_bracket_left => switch (c) { .angle_bracket_left => switch (c) {
'<' => { '<' => {
@ -942,12 +1006,27 @@ pub const Tokenizer = struct {
self.index += 1; self.index += 1;
break; break;
}, },
'|' => {
result.tag = .angle_bracket_angle_bracket_left_pipe;
},
else => { else => {
result.tag = .angle_bracket_angle_bracket_left; result.tag = .angle_bracket_angle_bracket_left;
break; break;
}, },
}, },
.angle_bracket_angle_bracket_left_pipe => switch (c) {
'=' => {
result.tag = .angle_bracket_angle_bracket_left_pipe_equal;
self.index += 1;
break;
},
else => {
result.tag = .angle_bracket_angle_bracket_left_pipe;
break;
},
},
.angle_bracket_right => switch (c) { .angle_bracket_right => switch (c) {
'>' => { '>' => {
state = .angle_bracket_angle_bracket_right; state = .angle_bracket_angle_bracket_right;

View file

@ -44,6 +44,11 @@ pub const Inst = struct {
/// is the same as both operands. /// is the same as both operands.
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
addwrap, addwrap,
/// Saturating integer addition.
/// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands.
/// Uses the `bin_op` field.
addsat,
/// Float or integer subtraction. For integers, wrapping is undefined behavior. /// Float or integer subtraction. For integers, wrapping is undefined behavior.
/// Both operands are guaranteed to be the same type, and the result type /// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands. /// is the same as both operands.
@ -54,6 +59,11 @@ pub const Inst = struct {
/// is the same as both operands. /// is the same as both operands.
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
subwrap, subwrap,
/// Saturating integer subtraction.
/// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands.
/// Uses the `bin_op` field.
subsat,
/// Float or integer multiplication. For integers, wrapping is undefined behavior. /// Float or integer multiplication. For integers, wrapping is undefined behavior.
/// Both operands are guaranteed to be the same type, and the result type /// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands. /// is the same as both operands.
@ -64,6 +74,11 @@ pub const Inst = struct {
/// is the same as both operands. /// is the same as both operands.
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
mulwrap, mulwrap,
/// Saturating integer multiplication.
/// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands.
/// Uses the `bin_op` field.
mulsat,
/// Integer or float division. For integers, wrapping is undefined behavior. /// Integer or float division. For integers, wrapping is undefined behavior.
/// Both operands are guaranteed to be the same type, and the result type /// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands. /// is the same as both operands.
@ -110,6 +125,9 @@ pub const Inst = struct {
/// Shift left. `<<` /// Shift left. `<<`
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
shl, shl,
/// Shift left saturating. `<<|`
/// Uses the `bin_op` field.
shl_sat,
/// Bitwise XOR. `^` /// Bitwise XOR. `^`
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
xor, xor,
@ -568,10 +586,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.add, .add,
.addwrap, .addwrap,
.addsat,
.sub, .sub,
.subwrap, .subwrap,
.subsat,
.mul, .mul,
.mulwrap, .mulwrap,
.mulsat,
.div, .div,
.rem, .rem,
.mod, .mod,
@ -582,6 +603,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.ptr_sub, .ptr_sub,
.shr, .shr,
.shl, .shl,
.shl_sat,
=> return air.typeOf(datas[inst].bin_op.lhs), => return air.typeOf(datas[inst].bin_op.lhs),
.cmp_lt, .cmp_lt,

View file

@ -318,27 +318,35 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
.assign_bit_and, .assign_bit_and,
.assign_bit_or, .assign_bit_or,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_xor, .assign_bit_xor,
.assign_div, .assign_div,
.assign_sub, .assign_sub,
.assign_sub_wrap, .assign_sub_wrap,
.assign_sub_sat,
.assign_mod, .assign_mod,
.assign_add, .assign_add,
.assign_add_wrap, .assign_add_wrap,
.assign_add_sat,
.assign_mul, .assign_mul,
.assign_mul_wrap, .assign_mul_wrap,
.assign_mul_sat,
.add, .add,
.add_wrap, .add_wrap,
.add_sat,
.sub, .sub,
.sub_wrap, .sub_wrap,
.sub_sat,
.mul, .mul,
.mul_wrap, .mul_wrap,
.mul_sat,
.div, .div,
.mod, .mod,
.bit_and, .bit_and,
.bit_or, .bit_or,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_xor, .bit_xor,
.bang_equal, .bang_equal,
@ -526,6 +534,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
try assignShift(gz, scope, node, .shl); try assignShift(gz, scope, node, .shl);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
}, },
.assign_bit_shift_left_sat => {
try assignBinOpExt(gz, scope, node, .shl_with_saturation);
return rvalue(gz, rl, .void_value, node);
},
.assign_bit_shift_right => { .assign_bit_shift_right => {
try assignShift(gz, scope, node, .shr); try assignShift(gz, scope, node, .shr);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
@ -555,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
try assignOp(gz, scope, node, .subwrap); try assignOp(gz, scope, node, .subwrap);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
}, },
.assign_sub_sat => {
try assignBinOpExt(gz, scope, node, .sub_with_saturation);
return rvalue(gz, rl, .void_value, node);
},
.assign_mod => { .assign_mod => {
try assignOp(gz, scope, node, .mod_rem); try assignOp(gz, scope, node, .mod_rem);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
@ -567,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
try assignOp(gz, scope, node, .addwrap); try assignOp(gz, scope, node, .addwrap);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
}, },
.assign_add_sat => {
try assignBinOpExt(gz, scope, node, .add_with_saturation);
return rvalue(gz, rl, .void_value, node);
},
.assign_mul => { .assign_mul => {
try assignOp(gz, scope, node, .mul); try assignOp(gz, scope, node, .mul);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
@ -575,17 +595,25 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
try assignOp(gz, scope, node, .mulwrap); try assignOp(gz, scope, node, .mulwrap);
return rvalue(gz, rl, .void_value, node); return rvalue(gz, rl, .void_value, node);
}, },
.assign_mul_sat => {
try assignBinOpExt(gz, scope, node, .mul_with_saturation);
return rvalue(gz, rl, .void_value, node);
},
// zig fmt: off // zig fmt: off
.bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
.bit_shift_left_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation),
.bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
.add => return simpleBinOp(gz, scope, rl, node, .add), .add => return simpleBinOp(gz, scope, rl, node, .add),
.add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap), .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
.add_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation),
.sub => return simpleBinOp(gz, scope, rl, node, .sub), .sub => return simpleBinOp(gz, scope, rl, node, .sub),
.sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap), .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
.sub_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation),
.mul => return simpleBinOp(gz, scope, rl, node, .mul), .mul => return simpleBinOp(gz, scope, rl, node, .mul),
.mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap), .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
.mul_sat => return binOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation),
.div => return simpleBinOp(gz, scope, rl, node, .div), .div => return simpleBinOp(gz, scope, rl, node, .div),
.mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
.bit_and => { .bit_and => {
@ -2685,6 +2713,31 @@ fn assignOp(
_ = try gz.addBin(.store, lhs_ptr, result); _ = try gz.addBin(.store, lhs_ptr, result);
} }
// TODO: is there an existing method to accomplish this?
// TODO: likely rename this to indicate rhs type coercion or add more params to make it more general
fn assignBinOpExt(
gz: *GenZir,
scope: *Scope,
infix_node: Ast.Node.Index,
op_inst_tag: Zir.Inst.Extended,
) InnerError!void {
try emitDbgNode(gz, infix_node);
const astgen = gz.astgen;
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);
const result = try gz.addExtendedPayload(op_inst_tag, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(infix_node),
.lhs = lhs,
.rhs = rhs,
});
_ = try gz.addBin(.store, lhs_ptr, result);
}
fn assignShift( fn assignShift(
gz: *GenZir, gz: *GenZir,
scope: *Scope, scope: *Scope,
@ -2708,6 +2761,29 @@ fn assignShift(
_ = try gz.addBin(.store, lhs_ptr, result); _ = try gz.addBin(.store, lhs_ptr, result);
} }
fn assignShiftSat(
gz: *GenZir,
scope: *Scope,
infix_node: ast.Node.Index,
op_inst_tag: Zir.Inst.Tag,
) InnerError!void {
try emitDbgNode(gz, infix_node);
const astgen = gz.astgen;
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
const rhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);
const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
.lhs = lhs,
.rhs = rhs,
});
_ = try gz.addBin(.store, lhs_ptr, result);
}
fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen; const astgen = gz.astgen;
const tree = astgen.tree; const tree = astgen.tree;
@ -7827,6 +7903,26 @@ fn shiftOp(
return rvalue(gz, rl, result, node); return rvalue(gz, rl, result, node);
} }
// TODO: is there an existing way to do this?
// TODO: likely rename this to reflect result_loc == .none or add more params to make it more general
fn binOpExt(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: Ast.Node.Index,
lhs_node: Ast.Node.Index,
rhs_node: Ast.Node.Index,
tag: Zir.Inst.Extended,
) InnerError!Zir.Inst.Ref {
const lhs = try expr(gz, scope, .none, lhs_node);
const rhs = try expr(gz, scope, .none, rhs_node);
const result = try gz.addExtendedPayload(tag, Zir.Inst.Bin{
.lhs = lhs,
.rhs = rhs,
});
return rvalue(gz, rl, result, node);
}
fn cImport( fn cImport(
gz: *GenZir, gz: *GenZir,
scope: *Scope, scope: *Scope,
@ -8119,26 +8215,32 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
.asm_simple, .asm_simple,
.add, .add,
.add_wrap, .add_wrap,
.add_sat,
.array_cat, .array_cat,
.array_mult, .array_mult,
.assign, .assign,
.assign_bit_and, .assign_bit_and,
.assign_bit_or, .assign_bit_or,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_xor, .assign_bit_xor,
.assign_div, .assign_div,
.assign_sub, .assign_sub,
.assign_sub_wrap, .assign_sub_wrap,
.assign_sub_sat,
.assign_mod, .assign_mod,
.assign_add, .assign_add,
.assign_add_wrap, .assign_add_wrap,
.assign_add_sat,
.assign_mul, .assign_mul,
.assign_mul_wrap, .assign_mul_wrap,
.assign_mul_sat,
.bang_equal, .bang_equal,
.bit_and, .bit_and,
.bit_or, .bit_or,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_xor, .bit_xor,
.bool_and, .bool_and,
@ -8154,10 +8256,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
.mod, .mod,
.mul, .mul,
.mul_wrap, .mul_wrap,
.mul_sat,
.switch_range, .switch_range,
.field_access, .field_access,
.sub, .sub,
.sub_wrap, .sub_wrap,
.sub_sat,
.slice, .slice,
.slice_open, .slice_open,
.slice_sentinel, .slice_sentinel,
@ -8352,26 +8456,32 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
.tagged_union_enum_tag_trailing, .tagged_union_enum_tag_trailing,
.add, .add,
.add_wrap, .add_wrap,
.add_sat,
.array_cat, .array_cat,
.array_mult, .array_mult,
.assign, .assign,
.assign_bit_and, .assign_bit_and,
.assign_bit_or, .assign_bit_or,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_xor, .assign_bit_xor,
.assign_div, .assign_div,
.assign_sub, .assign_sub,
.assign_sub_wrap, .assign_sub_wrap,
.assign_sub_sat,
.assign_mod, .assign_mod,
.assign_add, .assign_add,
.assign_add_wrap, .assign_add_wrap,
.assign_add_sat,
.assign_mul, .assign_mul,
.assign_mul_wrap, .assign_mul_wrap,
.assign_mul_sat,
.bang_equal, .bang_equal,
.bit_and, .bit_and,
.bit_or, .bit_or,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_xor, .bit_xor,
.bool_and, .bool_and,
@ -8387,9 +8497,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
.mod, .mod,
.mul, .mul,
.mul_wrap, .mul_wrap,
.mul_sat,
.switch_range, .switch_range,
.sub, .sub,
.sub_wrap, .sub_wrap,
.sub_sat,
.slice, .slice,
.slice_open, .slice_open,
.slice_sentinel, .slice_sentinel,
@ -8524,26 +8636,32 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
.asm_simple, .asm_simple,
.add, .add,
.add_wrap, .add_wrap,
.add_sat,
.array_cat, .array_cat,
.array_mult, .array_mult,
.assign, .assign,
.assign_bit_and, .assign_bit_and,
.assign_bit_or, .assign_bit_or,
.assign_bit_shift_left, .assign_bit_shift_left,
.assign_bit_shift_left_sat,
.assign_bit_shift_right, .assign_bit_shift_right,
.assign_bit_xor, .assign_bit_xor,
.assign_div, .assign_div,
.assign_sub, .assign_sub,
.assign_sub_wrap, .assign_sub_wrap,
.assign_sub_sat,
.assign_mod, .assign_mod,
.assign_add, .assign_add,
.assign_add_wrap, .assign_add_wrap,
.assign_add_sat,
.assign_mul, .assign_mul,
.assign_mul_wrap, .assign_mul_wrap,
.assign_mul_sat,
.bang_equal, .bang_equal,
.bit_and, .bit_and,
.bit_or, .bit_or,
.bit_shift_left, .bit_shift_left,
.bit_shift_left_sat,
.bit_shift_right, .bit_shift_right,
.bit_xor, .bit_xor,
.bool_and, .bool_and,
@ -8559,10 +8677,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
.mod, .mod,
.mul, .mul,
.mul_wrap, .mul_wrap,
.mul_sat,
.switch_range, .switch_range,
.field_access, .field_access,
.sub, .sub,
.sub_wrap, .sub_wrap,
.sub_sat,
.slice, .slice,
.slice_open, .slice_open,
.slice_sentinel, .slice_sentinel,

View file

@ -226,10 +226,13 @@ fn analyzeInst(
switch (inst_tags[inst]) { switch (inst_tags[inst]) {
.add, .add,
.addwrap, .addwrap,
.addsat,
.sub, .sub,
.subwrap, .subwrap,
.subsat,
.mul, .mul,
.mulwrap, .mulwrap,
.mulsat,
.div, .div,
.rem, .rem,
.mod, .mod,
@ -252,6 +255,7 @@ fn analyzeInst(
.ptr_elem_val, .ptr_elem_val,
.ptr_ptr_elem_val, .ptr_ptr_elem_val,
.shl, .shl,
.shl_sat,
.shr, .shr,
.atomic_store_unordered, .atomic_store_unordered,
.atomic_store_monotonic, .atomic_store_monotonic,

View file

@ -826,10 +826,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// zig fmt: off // zig fmt: off
.add, .ptr_add => try self.airAdd(inst), .add, .ptr_add => try self.airAdd(inst),
.addwrap => try self.airAddWrap(inst), .addwrap => try self.airAddWrap(inst),
.addsat => try self.airArithmeticOpSat(inst, "addsat"),
.sub, .ptr_sub => try self.airSub(inst), .sub, .ptr_sub => try self.airSub(inst),
.subwrap => try self.airSubWrap(inst), .subwrap => try self.airSubWrap(inst),
.subsat => try self.airArithmeticOpSat(inst, "subsat"),
.mul => try self.airMul(inst), .mul => try self.airMul(inst),
.mulwrap => try self.airMulWrap(inst), .mulwrap => try self.airMulWrap(inst),
.mulsat => try self.airArithmeticOpSat(inst, "mulsat"),
.div => try self.airDiv(inst), .div => try self.airDiv(inst),
.rem => try self.airRem(inst), .rem => try self.airRem(inst),
.mod => try self.airMod(inst), .mod => try self.airMod(inst),
@ -848,6 +851,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.xor => try self.airXor(inst), .xor => try self.airXor(inst),
.shr => try self.airShr(inst), .shr => try self.airShr(inst),
.shl => try self.airShl(inst), .shl => try self.airShl(inst),
.shl_sat => try self.airArithmeticOpSat(inst, "shl_sat"),
.alloc => try self.airAlloc(inst), .alloc => try self.airAlloc(inst),
.arg => try self.airArg(inst), .arg => try self.airArg(inst),
@ -1320,6 +1324,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
} }
fn airArithmeticOpSat(self: *Self, inst: Air.Inst.Index, comptime name: []const u8) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
else => return self.fail("TODO implement " ++ name ++ " for {}", .{self.target.cpu.arch}),
};
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airMul(self: *Self, inst: Air.Inst.Index) !void { fn airMul(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op; const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {

View file

@ -885,14 +885,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
// that wrapping is UB. // that wrapping is UB.
.add, .ptr_add => try airBinOp( f, inst, " + "), .add, .ptr_add => try airBinOp( f, inst, " + "),
.addwrap => try airWrapOp(f, inst, " + ", "addw_"), .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
.addsat => return o.dg.fail("TODO: C backend: implement codegen for addsat", .{}),
// TODO use a different strategy for sub that communicates to the optimizer // TODO use a different strategy for sub that communicates to the optimizer
// that wrapping is UB. // that wrapping is UB.
.sub, .ptr_sub => try airBinOp( f, inst, " - "), .sub, .ptr_sub => try airBinOp( f, inst, " - "),
.subwrap => try airWrapOp(f, inst, " - ", "subw_"), .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
.subsat => return o.dg.fail("TODO: C backend: implement codegen for subsat", .{}),
// TODO use a different strategy for mul that communicates to the optimizer // TODO use a different strategy for mul that communicates to the optimizer
// that wrapping is UB. // that wrapping is UB.
.mul => try airBinOp( f, inst, " * "), .mul => try airBinOp( f, inst, " * "),
.mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
.mulsat => return o.dg.fail("TODO: C backend: implement codegen for mulsat", .{}),
// TODO use a different strategy for div that communicates to the optimizer // TODO use a different strategy for div that communicates to the optimizer
// that wrapping is UB. // that wrapping is UB.
.div => try airBinOp( f, inst, " / "), .div => try airBinOp( f, inst, " / "),

View file

@ -1236,12 +1236,15 @@ pub const FuncGen = struct {
for (body) |inst| { for (body) |inst| {
const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
// zig fmt: off // zig fmt: off
.add => try self.airAdd(inst, false), .add => try self.airAdd(inst, .standard),
.addwrap => try self.airAdd(inst, true), .addwrap => try self.airAdd(inst, .wrapping),
.sub => try self.airSub(inst, false), .addsat => try self.airAdd(inst, .saturated),
.subwrap => try self.airSub(inst, true), .sub => try self.airSub(inst, .standard),
.mul => try self.airMul(inst, false), .subwrap => try self.airSub(inst, .wrapping),
.mulwrap => try self.airMul(inst, true), .subsat => try self.airSub(inst, .saturated),
.mul => try self.airMul(inst, .standard),
.mulwrap => try self.airMul(inst, .wrapping),
.mulsat => try self.airMul(inst, .saturated),
.div => try self.airDiv(inst), .div => try self.airDiv(inst),
.rem => try self.airRem(inst), .rem => try self.airRem(inst),
.mod => try self.airMod(inst), .mod => try self.airMod(inst),
@ -1252,7 +1255,8 @@ pub const FuncGen = struct {
.bit_or, .bool_or => try self.airOr(inst), .bit_or, .bool_or => try self.airOr(inst),
.xor => try self.airXor(inst), .xor => try self.airXor(inst),
.shl => try self.airShl(inst), .shl => try self.airShl(inst, false),
.shl_sat => try self.airShl(inst, true),
.shr => try self.airShr(inst), .shr => try self.airShr(inst),
.cmp_eq => try self.airCmp(inst, .eq), .cmp_eq => try self.airCmp(inst, .eq),
@ -2024,7 +2028,8 @@ pub const FuncGen = struct {
return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{}); return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
} }
fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { const ArithmeticType = enum { standard, wrapping, saturated };
fn airAdd(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) if (self.liveness.isUnused(inst))
return null; return null;
@ -2033,13 +2038,20 @@ pub const FuncGen = struct {
const rhs = try self.resolveInst(bin_op.rhs); const rhs = try self.resolveInst(bin_op.rhs);
const inst_ty = self.air.typeOfIndex(inst); const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, ""); if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");
if (wrap) return self.builder.buildAdd(lhs, rhs, ""); if (ty == .wrapping)
return self.builder.buildAdd(lhs, rhs, "")
else if (ty == .saturated) {
if (inst_ty.isSignedInt())
return self.builder.buildSAddSat(lhs, rhs, "")
else
return self.builder.buildUAddSat(lhs, rhs, "");
}
if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
return self.builder.buildNUWAdd(lhs, rhs, ""); return self.builder.buildNUWAdd(lhs, rhs, "");
} }
fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { fn airSub(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) if (self.liveness.isUnused(inst))
return null; return null;
@ -2048,13 +2060,20 @@ pub const FuncGen = struct {
const rhs = try self.resolveInst(bin_op.rhs); const rhs = try self.resolveInst(bin_op.rhs);
const inst_ty = self.air.typeOfIndex(inst); const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, ""); if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");
if (wrap) return self.builder.buildSub(lhs, rhs, ""); if (ty == .wrapping)
return self.builder.buildSub(lhs, rhs, "")
else if (ty == .saturated) {
if (inst_ty.isSignedInt())
return self.builder.buildSSubSat(lhs, rhs, "")
else
return self.builder.buildUSubSat(lhs, rhs, "");
}
if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
return self.builder.buildNUWSub(lhs, rhs, ""); return self.builder.buildNUWSub(lhs, rhs, "");
} }
fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { fn airMul(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) if (self.liveness.isUnused(inst))
return null; return null;
@ -2063,8 +2082,15 @@ pub const FuncGen = struct {
const rhs = try self.resolveInst(bin_op.rhs); const rhs = try self.resolveInst(bin_op.rhs);
const inst_ty = self.air.typeOfIndex(inst); const inst_ty = self.air.typeOfIndex(inst);
if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, ""); if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");
if (wrap) return self.builder.buildMul(lhs, rhs, ""); if (ty == .wrapping)
return self.builder.buildMul(lhs, rhs, "")
else if (ty == .saturated) {
if (inst_ty.isSignedInt())
return self.builder.buildSMulFixSat(lhs, rhs, "")
else
return self.builder.buildUMulFixSat(lhs, rhs, "");
}
if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
return self.builder.buildNUWMul(lhs, rhs, ""); return self.builder.buildNUWMul(lhs, rhs, "");
} }
@ -2174,7 +2200,7 @@ pub const FuncGen = struct {
return self.builder.buildXor(lhs, rhs, ""); return self.builder.buildXor(lhs, rhs, "");
} }
fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { fn airShl(self: *FuncGen, inst: Air.Inst.Index, sat: bool) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) if (self.liveness.isUnused(inst))
return null; return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op; const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -2186,6 +2212,12 @@ pub const FuncGen = struct {
self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
else else
rhs; rhs;
if (sat) {
return if (lhs_type.isSignedInt())
self.builder.buildSShlSat(lhs, casted_rhs, "")
else
self.builder.buildUShlSat(lhs, casted_rhs, "");
}
return self.builder.buildShl(lhs, casted_rhs, ""); return self.builder.buildShl(lhs, casted_rhs, "");
} }

View file

@ -397,6 +397,12 @@ pub const Builder = opaque {
pub const buildNUWAdd = LLVMBuildNUWAdd; pub const buildNUWAdd = LLVMBuildNUWAdd;
extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildSAddSat = ZigLLVMBuildSAddSat;
extern fn ZigLLVMBuildSAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildUAddSat = ZigLLVMBuildUAddSat;
extern fn ZigLLVMBuildUAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildFSub = LLVMBuildFSub; pub const buildFSub = LLVMBuildFSub;
extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@ -409,6 +415,12 @@ pub const Builder = opaque {
pub const buildNUWSub = LLVMBuildNUWSub; pub const buildNUWSub = LLVMBuildNUWSub;
extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildSSubSat = ZigLLVMBuildSSubSat;
extern fn ZigLLVMBuildSSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildUSubSat = ZigLLVMBuildUSubSat;
extern fn ZigLLVMBuildUSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildFMul = LLVMBuildFMul; pub const buildFMul = LLVMBuildFMul;
extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@ -421,6 +433,12 @@ pub const Builder = opaque {
pub const buildNUWMul = LLVMBuildNUWMul; pub const buildNUWMul = LLVMBuildNUWMul;
extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildSMulFixSat = ZigLLVMBuildSMulFixSat;
extern fn ZigLLVMBuildSMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildUMulFixSat = ZigLLVMBuildUMulFixSat;
extern fn ZigLLVMBuildUMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildUDiv = LLVMBuildUDiv; pub const buildUDiv = LLVMBuildUDiv;
extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
@ -451,6 +469,12 @@ pub const Builder = opaque {
pub const buildShl = LLVMBuildShl; pub const buildShl = LLVMBuildShl;
extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildSShlSat = ZigLLVMBuildSShlSat;
extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildUShlSat = ZigLLVMBuildUShlSat;
extern fn ZigLLVMBuildUShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
pub const buildOr = LLVMBuildOr; pub const buildOr = LLVMBuildOr;
extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;

View file

@ -104,10 +104,13 @@ const Writer = struct {
.add, .add,
.addwrap, .addwrap,
.addsat,
.sub, .sub,
.subwrap, .subwrap,
.subsat,
.mul, .mul,
.mulwrap, .mulwrap,
.mulsat,
.div, .div,
.rem, .rem,
.mod, .mod,
@ -130,6 +133,7 @@ const Writer = struct {
.ptr_elem_val, .ptr_elem_val,
.ptr_ptr_elem_val, .ptr_ptr_elem_val,
.shl, .shl,
.shl_sat,
.shr, .shr,
.set_union_tag, .set_union_tag,
=> try w.writeBinOp(s, inst), => try w.writeBinOp(s, inst),

View file

@ -812,14 +812,18 @@ enum BinOpType {
BinOpTypeInvalid, BinOpTypeInvalid,
BinOpTypeAssign, BinOpTypeAssign,
BinOpTypeAssignTimes, BinOpTypeAssignTimes,
BinOpTypeAssignTimesSat,
BinOpTypeAssignTimesWrap, BinOpTypeAssignTimesWrap,
BinOpTypeAssignDiv, BinOpTypeAssignDiv,
BinOpTypeAssignMod, BinOpTypeAssignMod,
BinOpTypeAssignPlus, BinOpTypeAssignPlus,
BinOpTypeAssignPlusSat,
BinOpTypeAssignPlusWrap, BinOpTypeAssignPlusWrap,
BinOpTypeAssignMinus, BinOpTypeAssignMinus,
BinOpTypeAssignMinusSat,
BinOpTypeAssignMinusWrap, BinOpTypeAssignMinusWrap,
BinOpTypeAssignBitShiftLeft, BinOpTypeAssignBitShiftLeft,
BinOpTypeAssignBitShiftLeftSat,
BinOpTypeAssignBitShiftRight, BinOpTypeAssignBitShiftRight,
BinOpTypeAssignBitAnd, BinOpTypeAssignBitAnd,
BinOpTypeAssignBitXor, BinOpTypeAssignBitXor,
@ -836,12 +840,16 @@ enum BinOpType {
BinOpTypeBinXor, BinOpTypeBinXor,
BinOpTypeBinAnd, BinOpTypeBinAnd,
BinOpTypeBitShiftLeft, BinOpTypeBitShiftLeft,
BinOpTypeBitShiftLeftSat,
BinOpTypeBitShiftRight, BinOpTypeBitShiftRight,
BinOpTypeAdd, BinOpTypeAdd,
BinOpTypeAddSat,
BinOpTypeAddWrap, BinOpTypeAddWrap,
BinOpTypeSub, BinOpTypeSub,
BinOpTypeSubSat,
BinOpTypeSubWrap, BinOpTypeSubWrap,
BinOpTypeMult, BinOpTypeMult,
BinOpTypeMultSat,
BinOpTypeMultWrap, BinOpTypeMultWrap,
BinOpTypeDiv, BinOpTypeDiv,
BinOpTypeMod, BinOpTypeMod,
@ -2958,10 +2966,10 @@ enum IrBinOp {
IrBinOpArrayMult, IrBinOpArrayMult,
IrBinOpMaximum, IrBinOpMaximum,
IrBinOpMinimum, IrBinOpMinimum,
IrBinOpSatAdd, IrBinOpAddSat,
IrBinOpSatSub, IrBinOpSubSat,
IrBinOpSatMul, IrBinOpMultSat,
IrBinOpSatShl, IrBinOpShlSat,
}; };
struct Stage1ZirInstBinOp { struct Stage1ZirInstBinOp {

View file

@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeAssignTimesWrap: case BinOpTypeAssignTimesWrap:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeAssignTimesSat:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultSat), lval, result_loc);
case BinOpTypeAssignDiv: case BinOpTypeAssignDiv:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeAssignMod: case BinOpTypeAssignMod:
@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAssignPlusWrap: case BinOpTypeAssignPlusWrap:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeAssignPlusSat:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddSat), lval, result_loc);
case BinOpTypeAssignMinus: case BinOpTypeAssignMinus:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeAssignMinusWrap: case BinOpTypeAssignMinusWrap:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeAssignMinusSat:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubSat), lval, result_loc);
case BinOpTypeAssignBitShiftLeft: case BinOpTypeAssignBitShiftLeft:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeAssignBitShiftLeftSat:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpShlSat), lval, result_loc);
case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitShiftRight:
return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitAnd:
@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
case BinOpTypeBitShiftLeft: case BinOpTypeBitShiftLeft:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeBitShiftLeftSat:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpShlSat), lval, result_loc);
case BinOpTypeBitShiftRight: case BinOpTypeBitShiftRight:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAdd: case BinOpTypeAdd:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAddWrap: case BinOpTypeAddWrap:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeAddSat:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddSat), lval, result_loc);
case BinOpTypeSub: case BinOpTypeSub:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeSubWrap: case BinOpTypeSubWrap:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeSubSat:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubSat), lval, result_loc);
case BinOpTypeMult: case BinOpTypeMult:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeMultWrap: case BinOpTypeMultWrap:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeMultSat:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultSat), lval, result_loc);
case BinOpTypeDiv: case BinOpTypeDiv:
return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc); return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeMod: case BinOpTypeMod:
@ -4716,7 +4732,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
if (arg1_value == ag->codegen->invalid_inst_src) if (arg1_value == ag->codegen->invalid_inst_src)
return arg1_value; return arg1_value;
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatAdd, arg0_value, arg1_value, true); Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpAddSat, arg0_value, arg1_value, true);
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
} }
case BuiltinFnIdSatSub: case BuiltinFnIdSatSub:
@ -4731,7 +4747,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
if (arg1_value == ag->codegen->invalid_inst_src) if (arg1_value == ag->codegen->invalid_inst_src)
return arg1_value; return arg1_value;
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatSub, arg0_value, arg1_value, true); Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSubSat, arg0_value, arg1_value, true);
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
} }
case BuiltinFnIdSatMul: case BuiltinFnIdSatMul:
@ -4746,7 +4762,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
if (arg1_value == ag->codegen->invalid_inst_src) if (arg1_value == ag->codegen->invalid_inst_src)
return arg1_value; return arg1_value;
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatMul, arg0_value, arg1_value, true); Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMultSat, arg0_value, arg1_value, true);
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
} }
case BuiltinFnIdSatShl: case BuiltinFnIdSatShl:
@ -4761,7 +4777,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
if (arg1_value == ag->codegen->invalid_inst_src) if (arg1_value == ag->codegen->invalid_inst_src)
return arg1_value; return arg1_value;
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatShl, arg0_value, arg1_value, true); Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpShlSat, arg0_value, arg1_value, true);
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
} }
case BuiltinFnIdMemcpy: case BuiltinFnIdMemcpy:

View file

@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
} else { } else {
zig_unreachable(); zig_unreachable();
} }
case IrBinOpSatAdd: case IrBinOpAddSat:
if (scalar_type->id == ZigTypeIdInt) { if (scalar_type->id == ZigTypeIdInt) {
if (scalar_type->data.integral.is_signed) { if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, ""); return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
@ -3343,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
} else { } else {
zig_unreachable(); zig_unreachable();
} }
case IrBinOpSatSub: case IrBinOpSubSat:
if (scalar_type->id == ZigTypeIdInt) { if (scalar_type->id == ZigTypeIdInt) {
if (scalar_type->data.integral.is_signed) { if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, ""); return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
@ -3353,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
} else { } else {
zig_unreachable(); zig_unreachable();
} }
case IrBinOpSatMul: case IrBinOpMultSat:
if (scalar_type->id == ZigTypeIdInt) { if (scalar_type->id == ZigTypeIdInt) {
if (scalar_type->data.integral.is_signed) { if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, ""); return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
@ -3363,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
} else { } else {
zig_unreachable(); zig_unreachable();
} }
case IrBinOpSatShl: case IrBinOpShlSat:
if (scalar_type->id == ZigTypeIdInt) { if (scalar_type->id == ZigTypeIdInt) {
if (scalar_type->data.integral.is_signed) { if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, ""); return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");

View file

@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
float_min(out_val, op1_val, op2_val); float_min(out_val, op1_val, op2_val);
} }
break; break;
case IrBinOpSatAdd: case IrBinOpAddSat:
if (is_int) { if (is_int) {
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); 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);
} else { } else {
zig_unreachable(); zig_unreachable();
} }
break; break;
case IrBinOpSatSub: case IrBinOpSubSat:
if (is_int) { if (is_int) {
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); 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);
} else { } else {
zig_unreachable(); zig_unreachable();
} }
break; break;
case IrBinOpSatMul: case IrBinOpMultSat:
if (is_int) { if (is_int) {
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); 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);
} else { } else {
zig_unreachable(); zig_unreachable();
} }
break; break;
case IrBinOpSatShl: case IrBinOpShlSat:
if (is_int) { if (is_int) {
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); 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);
} else { } else {
@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
case IrBinOpBitShiftRightExact: case IrBinOpBitShiftRightExact:
case IrBinOpAddWrap: case IrBinOpAddWrap:
case IrBinOpSubWrap: case IrBinOpSubWrap:
case IrBinOpSatAdd: case IrBinOpAddSat:
case IrBinOpSatSub: case IrBinOpSubSat:
case IrBinOpSatMul: case IrBinOpMultSat:
case IrBinOpSatShl: case IrBinOpShlSat:
case IrBinOpMultWrap: case IrBinOpMultWrap:
case IrBinOpArrayCat: case IrBinOpArrayCat:
case IrBinOpArrayMult: case IrBinOpArrayMult:
@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
case IrBinOpRemMod: case IrBinOpRemMod:
case IrBinOpMaximum: case IrBinOpMaximum:
case IrBinOpMinimum: case IrBinOpMinimum:
case IrBinOpSatAdd: case IrBinOpAddSat:
case IrBinOpSatSub: case IrBinOpSubSat:
case IrBinOpSatMul: case IrBinOpMultSat:
case IrBinOpSatShl: case IrBinOpShlSat:
return ir_analyze_bin_op_math(ira, bin_op_instruction); return ir_analyze_bin_op_math(ira, bin_op_instruction);
case IrBinOpArrayCat: case IrBinOpArrayCat:
return ir_analyze_array_cat(ira, bin_op_instruction); return ir_analyze_array_cat(ira, bin_op_instruction);

View file

@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "@maximum"; return "@maximum";
case IrBinOpMinimum: case IrBinOpMinimum:
return "@minimum"; return "@minimum";
case IrBinOpSatAdd: case IrBinOpAddSat:
return "@addWithSaturation"; return "@addWithSaturation";
case IrBinOpSatSub: case IrBinOpSubSat:
return "@subWithSaturation"; return "@subWithSaturation";
case IrBinOpSatMul: case IrBinOpMultSat:
return "@mulWithSaturation"; return "@mulWithSaturation";
case IrBinOpSatShl: case IrBinOpShlSat:
return "@shlWithSaturation"; return "@shlWithSaturation";
} }
zig_unreachable(); zig_unreachable();

View file

@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
// / PLUSEQUAL // / PLUSEQUAL
// / MINUSEQUAL // / MINUSEQUAL
// / LARROW2EQUAL // / LARROW2EQUAL
// / LARROW2PIPEEQUAL
// / RARROW2EQUAL // / RARROW2EQUAL
// / AMPERSANDEQUAL // / AMPERSANDEQUAL
// / CARETEQUAL // / CARETEQUAL
@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
// / ASTERISKPERCENTEQUAL // / ASTERISKPERCENTEQUAL
// / PLUSPERCENTEQUAL // / PLUSPERCENTEQUAL
// / MINUSPERCENTEQUAL // / MINUSPERCENTEQUAL
// / ASTERISKPIPEEQUAL
// / PLUSPIPEEQUAL
// / MINUSPIPEEQUAL
// / EQUAL // / EQUAL
static AstNode *ast_parse_assign_op(ParseContext *pc) { static AstNode *ast_parse_assign_op(ParseContext *pc) {
// In C, we have `T arr[N] = {[i] = T{}};` but it doesn't // 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) {
table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
table[TokenIdDivEq] = BinOpTypeAssignDiv; table[TokenIdDivEq] = BinOpTypeAssignDiv;
table[TokenIdEq] = BinOpTypeAssign; table[TokenIdEq] = BinOpTypeAssign;
table[TokenIdMinusEq] = BinOpTypeAssignMinus; table[TokenIdMinusEq] = BinOpTypeAssignMinus;
table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
table[TokenIdModEq] = BinOpTypeAssignMod; table[TokenIdModEq] = BinOpTypeAssignMod;
table[TokenIdPlusEq] = BinOpTypeAssignPlus; table[TokenIdPlusEq] = BinOpTypeAssignPlus;
table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
table[TokenIdTimesEq] = BinOpTypeAssignTimes; table[TokenIdTimesEq] = BinOpTypeAssignTimes;
table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
BinOpType op = table[pc->token_ids[pc->current_token]]; BinOpType op = table[pc->token_ids[pc->current_token]];
if (op != BinOpTypeInvalid) { if (op != BinOpTypeInvalid) {
@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
// BitShiftOp // BitShiftOp
// <- LARROW2 // <- LARROW2
// / LARROW2PIPE
// / RARROW2 // / RARROW2
static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {}; BinOpType table[TokenIdCount] = {};
table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
BinOpType op = table[pc->token_ids[pc->current_token]]; BinOpType op = table[pc->token_ids[pc->current_token]];
@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
// / PLUS2 // / PLUS2
// / PLUSPERCENT // / PLUSPERCENT
// / MINUSPERCENT // / MINUSPERCENT
// / PLUSPIPE
// / MINUSPIPE
static AstNode *ast_parse_addition_op(ParseContext *pc) { static AstNode *ast_parse_addition_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {}; BinOpType table[TokenIdCount] = {};
table[TokenIdPlus] = BinOpTypeAdd; table[TokenIdPlus] = BinOpTypeAdd;
@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
table[TokenIdPlusPlus] = BinOpTypeArrayCat; table[TokenIdPlusPlus] = BinOpTypeArrayCat;
table[TokenIdPlusPercent] = BinOpTypeAddWrap; table[TokenIdPlusPercent] = BinOpTypeAddWrap;
table[TokenIdMinusPercent] = BinOpTypeSubWrap; table[TokenIdMinusPercent] = BinOpTypeSubWrap;
table[TokenIdPlusPipe] = BinOpTypeAddSat;
table[TokenIdMinusPipe] = BinOpTypeSubSat;
BinOpType op = table[pc->token_ids[pc->current_token]]; BinOpType op = table[pc->token_ids[pc->current_token]];
if (op != BinOpTypeInvalid) { if (op != BinOpTypeInvalid) {
@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
// / PERCENT // / PERCENT
// / ASTERISK2 // / ASTERISK2
// / ASTERISKPERCENT // / ASTERISKPERCENT
// / ASTERISKPIPE
static AstNode *ast_parse_multiply_op(ParseContext *pc) { static AstNode *ast_parse_multiply_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {}; BinOpType table[TokenIdCount] = {};
table[TokenIdBarBar] = BinOpTypeMergeErrorSets; table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
table[TokenIdPercent] = BinOpTypeMod; table[TokenIdPercent] = BinOpTypeMod;
table[TokenIdStarStar] = BinOpTypeArrayMult; table[TokenIdStarStar] = BinOpTypeArrayMult;
table[TokenIdTimesPercent] = BinOpTypeMultWrap; table[TokenIdTimesPercent] = BinOpTypeMultWrap;
table[TokenIdTimesPipe] = BinOpTypeMultSat;
BinOpType op = table[pc->token_ids[pc->current_token]]; BinOpType op = table[pc->token_ids[pc->current_token]];
if (op != BinOpTypeInvalid) { if (op != BinOpTypeInvalid) {

View file

@ -226,8 +226,10 @@ enum TokenizeState {
TokenizeState_pipe, TokenizeState_pipe,
TokenizeState_minus, TokenizeState_minus,
TokenizeState_minus_percent, TokenizeState_minus_percent,
TokenizeState_minus_pipe,
TokenizeState_asterisk, TokenizeState_asterisk,
TokenizeState_asterisk_percent, TokenizeState_asterisk_percent,
TokenizeState_asterisk_pipe,
TokenizeState_slash, TokenizeState_slash,
TokenizeState_line_comment_start, TokenizeState_line_comment_start,
TokenizeState_line_comment, TokenizeState_line_comment,
@ -257,8 +259,10 @@ enum TokenizeState {
TokenizeState_percent, TokenizeState_percent,
TokenizeState_plus, TokenizeState_plus,
TokenizeState_plus_percent, TokenizeState_plus_percent,
TokenizeState_plus_pipe,
TokenizeState_angle_bracket_left, TokenizeState_angle_bracket_left,
TokenizeState_angle_bracket_angle_bracket_left, TokenizeState_angle_bracket_angle_bracket_left,
TokenizeState_angle_bracket_angle_bracket_left_pipe,
TokenizeState_angle_bracket_right, TokenizeState_angle_bracket_right,
TokenizeState_angle_bracket_angle_bracket_right, TokenizeState_angle_bracket_angle_bracket_right,
TokenizeState_period, TokenizeState_period,
@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
case '%': case '%':
t.state = TokenizeState_asterisk_percent; t.state = TokenizeState_asterisk_percent;
break; break;
case '|':
t.state = TokenizeState_asterisk_pipe;
break;
default: default:
t.state = TokenizeState_start; t.state = TokenizeState_start;
continue; continue;
@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
continue; continue;
} }
break; break;
case TokenizeState_asterisk_pipe:
switch (c) {
case 0:
t.out->ids.last() = TokenIdTimesPipe;
goto eof;
case '=':
t.out->ids.last() = TokenIdTimesPipeEq;
t.state = TokenizeState_start;
break;
default:
t.out->ids.last() = TokenIdTimesPipe;
t.state = TokenizeState_start;
continue;
}
break;
case TokenizeState_percent: case TokenizeState_percent:
switch (c) { switch (c) {
case 0: case 0:
@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
case '%': case '%':
t.state = TokenizeState_plus_percent; t.state = TokenizeState_plus_percent;
break; break;
case '|':
t.state = TokenizeState_plus_pipe;
break;
default: default:
t.state = TokenizeState_start; t.state = TokenizeState_start;
continue; continue;
@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
continue; continue;
} }
break; break;
case TokenizeState_plus_pipe:
switch (c) {
case 0:
t.out->ids.last() = TokenIdPlusPipe;
goto eof;
case '=':
t.out->ids.last() = TokenIdPlusPipeEq;
t.state = TokenizeState_start;
break;
default:
t.out->ids.last() = TokenIdPlusPipe;
t.state = TokenizeState_start;
continue;
}
break;
case TokenizeState_caret: case TokenizeState_caret:
switch (c) { switch (c) {
case 0: case 0:
@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
case '%': case '%':
t.state = TokenizeState_minus_percent; t.state = TokenizeState_minus_percent;
break; break;
case '|':
t.state = TokenizeState_minus_pipe;
break;
default: default:
t.state = TokenizeState_start; t.state = TokenizeState_start;
continue; continue;
@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
continue; continue;
} }
break; break;
case TokenizeState_minus_pipe:
switch (c) {
case 0:
t.out->ids.last() = TokenIdMinusPipe;
goto eof;
case '=':
t.out->ids.last() = TokenIdMinusPipeEq;
t.state = TokenizeState_start;
break;
default:
t.out->ids.last() = TokenIdMinusPipe;
t.state = TokenizeState_start;
continue;
}
break;
case TokenizeState_angle_bracket_left: case TokenizeState_angle_bracket_left:
switch (c) { switch (c) {
case 0: case 0:
@ -936,12 +994,31 @@ void tokenize(const char *source, Tokenization *out) {
t.out->ids.last() = TokenIdBitShiftLeftEq; t.out->ids.last() = TokenIdBitShiftLeftEq;
t.state = TokenizeState_start; t.state = TokenizeState_start;
break; break;
case '|':
// t.out->ids.last() = TokenIdBitShiftLeftPipe;
t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
break;
default: default:
t.out->ids.last() = TokenIdBitShiftLeft; t.out->ids.last() = TokenIdBitShiftLeft;
t.state = TokenizeState_start; t.state = TokenizeState_start;
continue; continue;
} }
break; break;
case TokenizeState_angle_bracket_angle_bracket_left_pipe:
switch (c) {
case 0:
t.out->ids.last() = TokenIdBitShiftLeftPipe;
goto eof;
case '=':
t.out->ids.last() = TokenIdBitShiftLeftPipeEq;
t.state = TokenizeState_start;
break;
default:
t.out->ids.last() = TokenIdBitShiftLeftPipe;
t.state = TokenizeState_start;
continue;
}
break;
case TokenizeState_angle_bracket_right: case TokenizeState_angle_bracket_right:
switch (c) { switch (c) {
case 0: case 0:
@ -1437,6 +1514,8 @@ const char * token_name(TokenId id) {
case TokenIdBitOrEq: return "|="; case TokenIdBitOrEq: return "|=";
case TokenIdBitShiftLeft: return "<<"; case TokenIdBitShiftLeft: return "<<";
case TokenIdBitShiftLeftEq: return "<<="; case TokenIdBitShiftLeftEq: return "<<=";
case TokenIdBitShiftLeftPipe: return "<<|";
case TokenIdBitShiftLeftPipeEq: return "<<|=";
case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRight: return ">>";
case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^="; case TokenIdBitXorEq: return "^=";
@ -1521,12 +1600,16 @@ const char * token_name(TokenId id) {
case TokenIdMinusEq: return "-="; case TokenIdMinusEq: return "-=";
case TokenIdMinusPercent: return "-%"; case TokenIdMinusPercent: return "-%";
case TokenIdMinusPercentEq: return "-%="; case TokenIdMinusPercentEq: return "-%=";
case TokenIdMinusPipe: return "-|";
case TokenIdMinusPipeEq: return "-|=";
case TokenIdModEq: return "%="; case TokenIdModEq: return "%=";
case TokenIdPercent: return "%"; case TokenIdPercent: return "%";
case TokenIdPlus: return "+"; case TokenIdPlus: return "+";
case TokenIdPlusEq: return "+="; case TokenIdPlusEq: return "+=";
case TokenIdPlusPercent: return "+%"; case TokenIdPlusPercent: return "+%";
case TokenIdPlusPercentEq: return "+%="; case TokenIdPlusPercentEq: return "+%=";
case TokenIdPlusPipe: return "+|";
case TokenIdPlusPipeEq: return "+|=";
case TokenIdPlusPlus: return "++"; case TokenIdPlusPlus: return "++";
case TokenIdRBrace: return "}"; case TokenIdRBrace: return "}";
case TokenIdRBracket: return "]"; case TokenIdRBracket: return "]";
@ -1542,6 +1625,8 @@ const char * token_name(TokenId id) {
case TokenIdTimesEq: return "*="; case TokenIdTimesEq: return "*=";
case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercent: return "*%";
case TokenIdTimesPercentEq: return "*%="; case TokenIdTimesPercentEq: return "*%=";
case TokenIdTimesPipe: return "*|";
case TokenIdTimesPipeEq: return "*|=";
case TokenIdBuiltin: return "Builtin"; case TokenIdBuiltin: return "Builtin";
case TokenIdCount: case TokenIdCount:
zig_unreachable(); zig_unreachable();

View file

@ -23,6 +23,8 @@ enum TokenId : uint8_t {
TokenIdBitOrEq, TokenIdBitOrEq,
TokenIdBitShiftLeft, TokenIdBitShiftLeft,
TokenIdBitShiftLeftEq, TokenIdBitShiftLeftEq,
TokenIdBitShiftLeftPipe,
TokenIdBitShiftLeftPipeEq,
TokenIdBitShiftRight, TokenIdBitShiftRight,
TokenIdBitShiftRightEq, TokenIdBitShiftRightEq,
TokenIdBitXorEq, TokenIdBitXorEq,
@ -108,12 +110,16 @@ enum TokenId : uint8_t {
TokenIdMinusEq, TokenIdMinusEq,
TokenIdMinusPercent, TokenIdMinusPercent,
TokenIdMinusPercentEq, TokenIdMinusPercentEq,
TokenIdMinusPipe,
TokenIdMinusPipeEq,
TokenIdModEq, TokenIdModEq,
TokenIdPercent, TokenIdPercent,
TokenIdPlus, TokenIdPlus,
TokenIdPlusEq, TokenIdPlusEq,
TokenIdPlusPercent, TokenIdPlusPercent,
TokenIdPlusPercentEq, TokenIdPlusPercentEq,
TokenIdPlusPipe,
TokenIdPlusPipeEq,
TokenIdPlusPlus, TokenIdPlusPlus,
TokenIdRBrace, TokenIdRBrace,
TokenIdRBracket, TokenIdRBracket,
@ -129,6 +135,8 @@ enum TokenId : uint8_t {
TokenIdTimesEq, TokenIdTimesEq,
TokenIdTimesPercent, TokenIdTimesPercent,
TokenIdTimesPercentEq, TokenIdTimesPercentEq,
TokenIdTimesPipe,
TokenIdTimesPipeEq,
TokenIdCount, TokenIdCount,
}; };

View file

@ -11,6 +11,7 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
const a = test_data[0]; const a = test_data[0];
const b = test_data[1]; const b = test_data[1];
const expected = test_data[2]; const expected = test_data[2];
{
const actual = switch (op) { const actual = switch (op) {
.add => @addWithSaturation(a, b), .add => @addWithSaturation(a, b),
.sub => @subWithSaturation(a, b), .sub => @subWithSaturation(a, b),
@ -18,6 +19,26 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
.shl => @shlWithSaturation(a, b), .shl => @shlWithSaturation(a, b),
}; };
try expectEqual(expected, actual); try expectEqual(expected, actual);
}
{
const actual = switch (op) {
.add => a +| b,
.sub => a -| b,
.mul => a *| b,
.shl => a <<| b,
};
try expectEqual(expected, actual);
}
{
var actual = a;
switch (op) {
.add => actual +|= b,
.sub => actual -|= b,
.mul => actual *|= b,
.shl => actual <<|= b,
}
try expectEqual(expected, actual);
}
} }
test "@addWithSaturation" { test "@addWithSaturation" {