Skip to content

Commit 5460f92

Browse files
Rollup merge of #112168 - scottmcm:lower-div-rem-unchecked-to-mir, r=oli-obk
Lower `unchecked_div`/`_rem` to MIR's `BinOp::Div`/`Rem` As described in <https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.BinOp.html#variant.Div>, the ordinary `BinOp`s for these are already UB for division by zero ([or overflow](https://llvm.org/docs/LangRef.html#sdiv-instruction), [demo](https://rust.godbolt.org/z/71e7P7Exh)), as MIR building is responsible for inserting code to panic for those cases regardless of whether the overflow checks are enabled. So we can lower these in the same arm that lowers `wrapping_add` to MIR `BinOp::Add` and such, as all these cases turn into ordinary `Rvalue::BinaryOp`s.
2 parents dd09f4d + adb37d4 commit 5460f92

27 files changed

+134
-81
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
475475
sym::unchecked_add
476476
| sym::unchecked_sub
477477
| sym::unchecked_mul
478-
| sym::unchecked_div
479478
| sym::exact_div
480-
| sym::unchecked_rem
481479
| sym::unchecked_shl
482480
| sym::unchecked_shr => {
483481
intrinsic_args!(fx, args => (x, y); intrinsic);
@@ -487,8 +485,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
487485
sym::unchecked_add => BinOp::Add,
488486
sym::unchecked_sub => BinOp::Sub,
489487
sym::unchecked_mul => BinOp::Mul,
490-
sym::unchecked_div | sym::exact_div => BinOp::Div,
491-
sym::unchecked_rem => BinOp::Rem,
488+
sym::exact_div => BinOp::Div,
492489
sym::unchecked_shl => BinOp::Shl,
493490
sym::unchecked_shr => BinOp::Shr,
494491
_ => unreachable!(),

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

-16
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
211211
args[1].val.unaligned_volatile_store(bx, dst);
212212
return;
213213
}
214-
| sym::unchecked_div
215-
| sym::unchecked_rem
216214
| sym::unchecked_shl
217215
| sym::unchecked_shr
218216
| sym::unchecked_add
@@ -229,20 +227,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
229227
bx.exactudiv(args[0].immediate(), args[1].immediate())
230228
}
231229
}
232-
sym::unchecked_div => {
233-
if signed {
234-
bx.sdiv(args[0].immediate(), args[1].immediate())
235-
} else {
236-
bx.udiv(args[0].immediate(), args[1].immediate())
237-
}
238-
}
239-
sym::unchecked_rem => {
240-
if signed {
241-
bx.srem(args[0].immediate(), args[1].immediate())
242-
} else {
243-
bx.urem(args[0].immediate(), args[1].immediate())
244-
}
245-
}
246230
sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()),
247231
sym::unchecked_shr => {
248232
if signed {

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
238238
| sym::unchecked_shr
239239
| sym::unchecked_add
240240
| sym::unchecked_sub
241-
| sym::unchecked_mul
242-
| sym::unchecked_div
243-
| sym::unchecked_rem => {
241+
| sym::unchecked_mul => {
244242
let l = self.read_immediate(&args[0])?;
245243
let r = self.read_immediate(&args[1])?;
246244
let bin_op = match intrinsic_name {
@@ -249,8 +247,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
249247
sym::unchecked_add => BinOp::Add,
250248
sym::unchecked_sub => BinOp::Sub,
251249
sym::unchecked_mul => BinOp::Mul,
252-
sym::unchecked_div => BinOp::Div,
253-
sym::unchecked_rem => BinOp::Rem,
254250
_ => bug!(),
255251
};
256252
let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;

compiler/rustc_middle/src/mir/syntax.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1273,13 +1273,18 @@ pub enum BinOp {
12731273
Mul,
12741274
/// The `/` operator (division)
12751275
///
1276-
/// Division by zero is UB, because the compiler should have inserted checks
1277-
/// prior to this.
1276+
/// For integer types, division by zero is UB, as is `MIN / -1` for signed.
1277+
/// The compiler should have inserted checks prior to this.
1278+
///
1279+
/// Floating-point division by zero is safe, and does not need guards.
12781280
Div,
12791281
/// The `%` operator (modulus)
12801282
///
1281-
/// Using zero as the modulus (second operand) is UB, because the compiler
1282-
/// should have inserted checks prior to this.
1283+
/// For integer types, using zero as the modulus (second operand) is UB,
1284+
/// as is `MIN % -1` for signed.
1285+
/// The compiler should have inserted checks prior to this.
1286+
///
1287+
/// Floating-point remainder by zero is safe, and does not need guards.
12831288
Rem,
12841289
/// The `^` operator (bitwise xor)
12851290
BitXor,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

+28-23
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,35 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
8282
drop(args);
8383
terminator.kind = TerminatorKind::Goto { target };
8484
}
85-
sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
86-
if let Some(target) = *target {
87-
let lhs;
88-
let rhs;
89-
{
90-
let mut args = args.drain(..);
91-
lhs = args.next().unwrap();
92-
rhs = args.next().unwrap();
93-
}
94-
let bin_op = match intrinsic_name {
95-
sym::wrapping_add => BinOp::Add,
96-
sym::wrapping_sub => BinOp::Sub,
97-
sym::wrapping_mul => BinOp::Mul,
98-
_ => bug!("unexpected intrinsic"),
99-
};
100-
block.statements.push(Statement {
101-
source_info: terminator.source_info,
102-
kind: StatementKind::Assign(Box::new((
103-
*destination,
104-
Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
105-
))),
106-
});
107-
terminator.kind = TerminatorKind::Goto { target };
85+
sym::wrapping_add
86+
| sym::wrapping_sub
87+
| sym::wrapping_mul
88+
| sym::unchecked_div
89+
| sym::unchecked_rem => {
90+
let target = target.unwrap();
91+
let lhs;
92+
let rhs;
93+
{
94+
let mut args = args.drain(..);
95+
lhs = args.next().unwrap();
96+
rhs = args.next().unwrap();
10897
}
98+
let bin_op = match intrinsic_name {
99+
sym::wrapping_add => BinOp::Add,
100+
sym::wrapping_sub => BinOp::Sub,
101+
sym::wrapping_mul => BinOp::Mul,
102+
sym::unchecked_div => BinOp::Div,
103+
sym::unchecked_rem => BinOp::Rem,
104+
_ => bug!("unexpected intrinsic"),
105+
};
106+
block.statements.push(Statement {
107+
source_info: terminator.source_info,
108+
kind: StatementKind::Assign(Box::new((
109+
*destination,
110+
Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
111+
))),
112+
});
113+
terminator.kind = TerminatorKind::Goto { target };
109114
}
110115
sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
111116
if let Some(target) = *target {

tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
bb0: {
88
- _0 = std::intrinsics::min_align_of::<T>() -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42
99
- // mir::Constant
10-
- // + span: $DIR/lower_intrinsics.rs:21:5: 21:40
10+
- // + span: $DIR/lower_intrinsics.rs:27:5: 27:40
1111
- // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::min_align_of::<T>}, val: Value(<ZST>) }
1212
+ _0 = AlignOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42
1313
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42

tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
StorageLive(_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38
1212
- _1 = std::intrinsics::assume(const true) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38
1313
- // mir::Constant
14-
- // + span: $DIR/lower_intrinsics.rs:106:9: 106:32
14+
- // + span: $DIR/lower_intrinsics.rs:112:9: 112:32
1515
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(bool) {std::intrinsics::assume}, val: Value(<ZST>) }
1616
+ assume(const true); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38
1717
+ goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38

tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff

+7-7
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
_3 = &(*_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:42: +1:44
3232
- _2 = discriminant_value::<T>(move _3) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
3333
- // mir::Constant
34-
- // + span: $DIR/lower_intrinsics.rs:82:5: 82:41
34+
- // + span: $DIR/lower_intrinsics.rs:88:5: 88:41
3535
- // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a T) -> <T as DiscriminantKind>::Discriminant {discriminant_value::<T>}, val: Value(<ZST>) }
3636
+ _2 = discriminant((*_3)); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
3737
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45
@@ -46,13 +46,13 @@
4646
StorageLive(_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
4747
_19 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
4848
// mir::Constant
49-
// + span: $DIR/lower_intrinsics.rs:83:42: 83:44
49+
// + span: $DIR/lower_intrinsics.rs:89:42: 89:44
5050
// + literal: Const { ty: &i32, val: Unevaluated(discriminant, [T], Some(promoted[2])) }
5151
_7 = &(*_19); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
5252
_6 = &(*_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44
5353
- _5 = discriminant_value::<i32>(move _6) -> [return: bb2, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
5454
- // mir::Constant
55-
- // + span: $DIR/lower_intrinsics.rs:83:5: 83:41
55+
- // + span: $DIR/lower_intrinsics.rs:89:5: 89:41
5656
- // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a i32) -> <i32 as DiscriminantKind>::Discriminant {discriminant_value::<i32>}, val: Value(<ZST>) }
5757
+ _5 = discriminant((*_6)); // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
5858
+ goto -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45
@@ -67,13 +67,13 @@
6767
StorageLive(_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
6868
_18 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
6969
// mir::Constant
70-
// + span: $DIR/lower_intrinsics.rs:84:42: 84:45
70+
// + span: $DIR/lower_intrinsics.rs:90:42: 90:45
7171
// + literal: Const { ty: &(), val: Unevaluated(discriminant, [T], Some(promoted[1])) }
7272
_11 = &(*_18); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
7373
_10 = &(*_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45
7474
- _9 = discriminant_value::<()>(move _10) -> [return: bb3, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
7575
- // mir::Constant
76-
- // + span: $DIR/lower_intrinsics.rs:84:5: 84:41
76+
- // + span: $DIR/lower_intrinsics.rs:90:5: 90:41
7777
- // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a ()) -> <() as DiscriminantKind>::Discriminant {discriminant_value::<()>}, val: Value(<ZST>) }
7878
+ _9 = discriminant((*_10)); // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
7979
+ goto -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46
@@ -88,13 +88,13 @@
8888
StorageLive(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
8989
_17 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
9090
// mir::Constant
91-
// + span: $DIR/lower_intrinsics.rs:85:42: 85:47
91+
// + span: $DIR/lower_intrinsics.rs:91:42: 91:47
9292
// + literal: Const { ty: &E, val: Unevaluated(discriminant, [T], Some(promoted[0])) }
9393
_15 = &(*_17); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
9494
_14 = &(*_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47
9595
- _13 = discriminant_value::<E>(move _14) -> [return: bb4, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48
9696
- // mir::Constant
97-
- // + span: $DIR/lower_intrinsics.rs:85:5: 85:41
97+
- // + span: $DIR/lower_intrinsics.rs:91:5: 91:41
9898
- // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a E) -> <E as DiscriminantKind>::Discriminant {discriminant_value::<E>}, val: Value(<ZST>) }
9999
+ _13 = discriminant((*_14)); // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48
100100
+ goto -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48

tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
StorageDead(_9); // scope 3 at $DIR/lower_intrinsics.rs:+4:90: +4:91
5050
- _3 = copy_nonoverlapping::<i32>(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95
5151
- // mir::Constant
52-
- // + span: $DIR/lower_intrinsics.rs:99:9: 99:28
52+
- // + span: $DIR/lower_intrinsics.rs:105:9: 105:28
5353
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, *mut i32, usize) {copy_nonoverlapping::<i32>}, val: Value(<ZST>) }
5454
+ copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95
5555
+ goto -> bb1; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95

tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
_2 = move _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:30: +1:31
1212
- _0 = std::intrinsics::forget::<T>(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32
1313
- // mir::Constant
14-
- // + span: $DIR/lower_intrinsics.rs:26:5: 26:29
14+
- // + span: $DIR/lower_intrinsics.rs:32:5: 32:29
1515
- // + literal: Const { ty: extern "rust-intrinsic" fn(T) {std::intrinsics::forget::<T>}, val: Value(<ZST>) }
1616
+ _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32
1717
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32

tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+2:9: +2:18
1414
_1 = std::intrinsics::size_of::<T>; // scope 0 at $DIR/lower_intrinsics.rs:+2:21: +2:51
1515
// mir::Constant
16-
// + span: $DIR/lower_intrinsics.rs:37:21: 37:51
16+
// + span: $DIR/lower_intrinsics.rs:43:21: 43:51
1717
// + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::<T>}, val: Value(<ZST>) }
1818
StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14
1919
_2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14

tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
_4 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:55: +2:56
2525
- _3 = option_payload_ptr::<usize>(move _4) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57
2626
- // mir::Constant
27-
- // + span: $DIR/lower_intrinsics.rs:137:18: 137:54
27+
- // + span: $DIR/lower_intrinsics.rs:143:18: 143:54
2828
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option<usize>) -> *const usize {option_payload_ptr::<usize>}, val: Value(<ZST>) }
2929
+ _3 = &raw const (((*_4) as Some).0: usize); // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57
3030
+ goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57
@@ -37,7 +37,7 @@
3737
_6 = &raw const (*_2); // scope 2 at $DIR/lower_intrinsics.rs:+3:55: +3:56
3838
- _5 = option_payload_ptr::<String>(move _6) -> [return: bb2, unwind unreachable]; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57
3939
- // mir::Constant
40-
- // + span: $DIR/lower_intrinsics.rs:138:18: 138:54
40+
- // + span: $DIR/lower_intrinsics.rs:144:18: 144:54
4141
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option<String>) -> *const String {option_payload_ptr::<String>}, val: Value(<ZST>) }
4242
+ _5 = &raw const (((*_6) as Some).0: std::string::String); // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57
4343
+ goto -> bb2; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57

tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
_4 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:33: +1:34
1616
- _0 = offset::<*const i32, isize>(move _3, move _4) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35
1717
- // mir::Constant
18-
- // + span: $DIR/lower_intrinsics.rs:144:5: 144:29
18+
- // + span: $DIR/lower_intrinsics.rs:150:5: 150:29
1919
- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, isize) -> *const i32 {offset::<*const i32, isize>}, val: Value(<ZST>) }
2020
+ _0 = Offset(move _3, move _4); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35
2121
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35

0 commit comments

Comments
 (0)