Skip to content

Commit bd71c26

Browse files
committed
Promote unchecked_add/sub/mul/shl/shr to mir::BinOp
1 parent 9eee230 commit bd71c26

File tree

42 files changed

+433
-228
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+433
-228
lines changed

compiler/rustc_codegen_cranelift/src/codegen_i128.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ pub(crate) fn maybe_codegen<'tcx>(
2222

2323
match bin_op {
2424
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None,
25-
BinOp::Add | BinOp::Sub => None,
26-
BinOp::Mul => {
25+
BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None,
26+
BinOp::Mul | BinOp::MulUnchecked => {
2727
let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
2828
let ret_val = fx.lib_call(
2929
"__multi3",
@@ -69,7 +69,7 @@ pub(crate) fn maybe_codegen<'tcx>(
6969
}
7070
}
7171
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
72-
BinOp::Shl | BinOp::Shr => None,
72+
BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
7373
}
7474
}
7575

@@ -131,9 +131,10 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
131131
fx.lib_call(name, param_types, vec![], &args);
132132
Some(out_place.to_cvalue(fx))
133133
}
134+
BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
134135
BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
135136
BinOp::Div | BinOp::Rem => unreachable!(),
136137
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
137-
BinOp::Shl | BinOp::Shr => unreachable!(),
138+
BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
138139
}
139140
}

compiler/rustc_codegen_cranelift/src/num.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,11 @@ pub(crate) fn codegen_int_binop<'tcx>(
128128
let rhs = in_rhs.load_scalar(fx);
129129

130130
let b = fx.bcx.ins();
131+
// FIXME trap on overflow for the Unchecked versions
131132
let val = match bin_op {
132-
BinOp::Add => b.iadd(lhs, rhs),
133-
BinOp::Sub => b.isub(lhs, rhs),
134-
BinOp::Mul => b.imul(lhs, rhs),
133+
BinOp::Add | BinOp::AddUnchecked => b.iadd(lhs, rhs),
134+
BinOp::Sub | BinOp::SubUnchecked => b.isub(lhs, rhs),
135+
BinOp::Mul | BinOp::MulUnchecked => b.imul(lhs, rhs),
135136
BinOp::Div => {
136137
if signed {
137138
b.sdiv(lhs, rhs)
@@ -149,16 +150,19 @@ pub(crate) fn codegen_int_binop<'tcx>(
149150
BinOp::BitXor => b.bxor(lhs, rhs),
150151
BinOp::BitAnd => b.band(lhs, rhs),
151152
BinOp::BitOr => b.bor(lhs, rhs),
152-
BinOp::Shl => b.ishl(lhs, rhs),
153-
BinOp::Shr => {
153+
BinOp::Shl | BinOp::ShlUnchecked => b.ishl(lhs, rhs),
154+
BinOp::Shr | BinOp::ShrUnchecked => {
154155
if signed {
155156
b.sshr(lhs, rhs)
156157
} else {
157158
b.ushr(lhs, rhs)
158159
}
159160
}
161+
BinOp::Offset => unreachable!("Offset is not an integer operation"),
160162
// Compare binops handles by `codegen_binop`.
161-
_ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
163+
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
164+
unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
165+
}
162166
};
163167

164168
CValue::by_val(val, in_lhs.layout())

compiler/rustc_codegen_ssa/src/common.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
132132
// all shifts). For 32- and 64-bit types, this matches the semantics
133133
// of Java. (See related discussion on #1877 and #10183.)
134134

135-
pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
135+
pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
136136
bx: &mut Bx,
137137
lhs: Bx::Value,
138138
rhs: Bx::Value,
@@ -143,7 +143,7 @@ pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
143143
bx.shl(lhs, rhs)
144144
}
145145

146-
pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
146+
pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
147147
bx: &mut Bx,
148148
lhs_t: Ty<'tcx>,
149149
lhs: Bx::Value,

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -798,20 +798,41 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
798798
bx.add(lhs, rhs)
799799
}
800800
}
801+
mir::BinOp::AddUnchecked => {
802+
if is_signed {
803+
bx.unchecked_sadd(lhs, rhs)
804+
} else {
805+
bx.unchecked_uadd(lhs, rhs)
806+
}
807+
}
801808
mir::BinOp::Sub => {
802809
if is_float {
803810
bx.fsub(lhs, rhs)
804811
} else {
805812
bx.sub(lhs, rhs)
806813
}
807814
}
815+
mir::BinOp::SubUnchecked => {
816+
if is_signed {
817+
bx.unchecked_ssub(lhs, rhs)
818+
} else {
819+
bx.unchecked_usub(lhs, rhs)
820+
}
821+
}
808822
mir::BinOp::Mul => {
809823
if is_float {
810824
bx.fmul(lhs, rhs)
811825
} else {
812826
bx.mul(lhs, rhs)
813827
}
814828
}
829+
mir::BinOp::MulUnchecked => {
830+
if is_signed {
831+
bx.unchecked_smul(lhs, rhs)
832+
} else {
833+
bx.unchecked_umul(lhs, rhs)
834+
}
835+
}
815836
mir::BinOp::Div => {
816837
if is_float {
817838
bx.fdiv(lhs, rhs)
@@ -848,8 +869,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
848869
bx.inbounds_gep(llty, lhs, &[rhs])
849870
}
850871
}
851-
mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs),
852-
mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs),
872+
mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs),
873+
mir::BinOp::ShlUnchecked => {
874+
let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
875+
bx.shl(lhs, rhs)
876+
}
877+
mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs),
878+
mir::BinOp::ShrUnchecked => {
879+
let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
880+
if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
881+
}
853882
mir::BinOp::Ne
854883
| mir::BinOp::Lt
855884
| mir::BinOp::Gt

compiler/rustc_const_eval/src/interpret/operator.rs

+44-22
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ use rustc_middle::mir;
33
use rustc_middle::mir::interpret::{InterpResult, Scalar};
44
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
55
use rustc_middle::ty::{self, FloatTy, Ty};
6+
use rustc_span::symbol::sym;
67
use rustc_target::abi::Abi;
78

89
use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
910

11+
use crate::fluent_generated as fluent;
12+
1013
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1114
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
1215
/// and a boolean signifying the potential overflow to the destination.
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
139142
) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
140143
use rustc_middle::mir::BinOp::*;
141144

145+
let throw_ub_on_overflow = match bin_op {
146+
AddUnchecked => Some(sym::unchecked_add),
147+
SubUnchecked => Some(sym::unchecked_sub),
148+
MulUnchecked => Some(sym::unchecked_mul),
149+
ShlUnchecked => Some(sym::unchecked_shl),
150+
ShrUnchecked => Some(sym::unchecked_shr),
151+
_ => None,
152+
};
153+
142154
// Shift ops can have an RHS with a different numeric type.
143-
if bin_op == Shl || bin_op == Shr {
155+
if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
144156
let size = u128::from(left_layout.size.bits());
145157
// Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
146158
// zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
155167
// integers are maximally 128bits wide, so negative shifts *always* overflow and we have
156168
// consistent results for the same value represented at different bit widths.
157169
assert!(size <= 128);
170+
let original_r = r;
158171
let overflow = r >= size;
159172
// The shift offset is implicitly masked to the type size, to make sure this operation
160173
// is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
166179
let result = if left_layout.abi.is_signed() {
167180
let l = self.sign_extend(l, left_layout) as i128;
168181
let result = match bin_op {
169-
Shl => l.checked_shl(r).unwrap(),
170-
Shr => l.checked_shr(r).unwrap(),
182+
Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
183+
Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
171184
_ => bug!(),
172185
};
173186
result as u128
174187
} else {
175188
match bin_op {
176-
Shl => l.checked_shl(r).unwrap(),
177-
Shr => l.checked_shr(r).unwrap(),
189+
Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
190+
Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
178191
_ => bug!(),
179192
}
180193
};
181194
let truncated = self.truncate(result, left_layout);
195+
196+
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
197+
throw_ub_custom!(
198+
fluent::const_eval_overflow_shift,
199+
val = original_r,
200+
name = intrinsic_name
201+
);
202+
}
203+
182204
return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
183205
}
184206

@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
216238
Rem if r == 0 => throw_ub!(RemainderByZero),
217239
Div => Some(i128::overflowing_div),
218240
Rem => Some(i128::overflowing_rem),
219-
Add => Some(i128::overflowing_add),
220-
Sub => Some(i128::overflowing_sub),
221-
Mul => Some(i128::overflowing_mul),
241+
Add | AddUnchecked => Some(i128::overflowing_add),
242+
Sub | SubUnchecked => Some(i128::overflowing_sub),
243+
Mul | MulUnchecked => Some(i128::overflowing_mul),
222244
_ => None,
223245
};
224246
if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
242264
// If that truncation loses any information, we have an overflow.
243265
let result = result as u128;
244266
let truncated = self.truncate(result, left_layout);
245-
return Ok((
246-
Scalar::from_uint(truncated, size),
247-
oflo || self.sign_extend(truncated, left_layout) != result,
248-
left_layout.ty,
249-
));
267+
let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
268+
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
269+
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
270+
}
271+
return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
250272
}
251273
}
252274

@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
263285
BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
264286
BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
265287

266-
Add | Sub | Mul | Rem | Div => {
288+
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
267289
assert!(!left_layout.abi.is_signed());
268290
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
269-
Add => u128::overflowing_add,
270-
Sub => u128::overflowing_sub,
271-
Mul => u128::overflowing_mul,
291+
Add | AddUnchecked => u128::overflowing_add,
292+
Sub | SubUnchecked => u128::overflowing_sub,
293+
Mul | MulUnchecked => u128::overflowing_mul,
272294
Div if r == 0 => throw_ub!(DivisionByZero),
273295
Rem if r == 0 => throw_ub!(RemainderByZero),
274296
Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279301
// Truncate to target type.
280302
// If that truncation loses any information, we have an overflow.
281303
let truncated = self.truncate(result, left_layout);
282-
return Ok((
283-
Scalar::from_uint(truncated, size),
284-
oflo || truncated != result,
285-
left_layout.ty,
286-
));
304+
let overflow = oflo || truncated != result;
305+
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
306+
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
307+
}
308+
return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
287309
}
288310

289311
_ => span_bug!(

compiler/rustc_const_eval/src/interpret/step.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use super::{ImmTy, InterpCx, Machine};
1616
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
1717
use rustc_middle::mir::BinOp::*;
1818
match op {
19-
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
19+
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
20+
| BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
2021
Eq | Ne | Lt | Le | Gt | Ge => false,
2122
}
2223
}
@@ -26,8 +27,9 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool {
2627
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
2728
use rustc_middle::mir::BinOp::*;
2829
match op {
29-
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
30-
Offset | Shl | Shr => false,
30+
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
31+
| BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
32+
Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
3133
}
3234
}
3335

compiler/rustc_const_eval/src/transform/promote_consts.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,18 @@ impl<'tcx> Validator<'_, 'tcx> {
572572
| BinOp::Gt
573573
| BinOp::Offset
574574
| BinOp::Add
575+
| BinOp::AddUnchecked
575576
| BinOp::Sub
577+
| BinOp::SubUnchecked
576578
| BinOp::Mul
579+
| BinOp::MulUnchecked
577580
| BinOp::BitXor
578581
| BinOp::BitAnd
579582
| BinOp::BitOr
580583
| BinOp::Shl
581-
| BinOp::Shr => {}
584+
| BinOp::ShlUnchecked
585+
| BinOp::Shr
586+
| BinOp::ShrUnchecked => {}
582587
}
583588

584589
self.validate_operand(lhs)?;

compiler/rustc_const_eval/src/transform/validate.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
556556
);
557557
}
558558
}
559-
Shl | Shr => {
559+
Shl | ShlUnchecked | Shr | ShrUnchecked => {
560560
for x in [a, b] {
561561
check_kinds!(
562562
x,
@@ -601,6 +601,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
601601
);
602602
}
603603
}
604+
AddUnchecked | SubUnchecked | MulUnchecked => {
605+
for x in [a, b] {
606+
check_kinds!(
607+
x,
608+
"Cannot perform unchecked arithmetic on type {:?}",
609+
ty::Uint(..) | ty::Int(..)
610+
)
611+
}
612+
if a != b {
613+
self.fail(
614+
location,
615+
format!(
616+
"Cannot perform unchecked arithmetic on unequal types {:?} and {:?}",
617+
a, b
618+
),
619+
);
620+
}
621+
}
604622
}
605623
}
606624
Rvalue::CheckedBinaryOp(op, vals) => {

compiler/rustc_middle/src/mir/syntax.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1267,10 +1267,16 @@ pub enum UnOp {
12671267
pub enum BinOp {
12681268
/// The `+` operator (addition)
12691269
Add,
1270+
/// Like `Add`, but with UB on overflow. (Integers only.)
1271+
AddUnchecked,
12701272
/// The `-` operator (subtraction)
12711273
Sub,
1274+
/// Like `Sub`, but with UB on overflow. (Integers only.)
1275+
SubUnchecked,
12721276
/// The `*` operator (multiplication)
12731277
Mul,
1278+
/// Like `Mul`, but with UB on overflow. (Integers only.)
1279+
MulUnchecked,
12741280
/// The `/` operator (division)
12751281
///
12761282
/// For integer types, division by zero is UB, as is `MIN / -1` for signed.
@@ -1296,10 +1302,17 @@ pub enum BinOp {
12961302
///
12971303
/// The offset is truncated to the size of the first operand before shifting.
12981304
Shl,
1305+
/// Like `Shl`, but is UB if the RHS >= LHS::BITS
1306+
ShlUnchecked,
12991307
/// The `>>` operator (shift right)
13001308
///
13011309
/// The offset is truncated to the size of the first operand before shifting.
1310+
///
1311+
/// This is an arithmetic shift if the LHS is signed
1312+
/// and a logical shift if the LHS is unsigned.
13021313
Shr,
1314+
/// Like `Shl`, but is UB if the RHS >= LHS::BITS
1315+
ShrUnchecked,
13031316
/// The `==` operator (equality)
13041317
Eq,
13051318
/// The `<` operator (less than)

0 commit comments

Comments
 (0)