about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_cranelift/src/codegen_i128.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs49
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs33
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs31
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs66
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs28
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs59
-rw-r--r--compiler/rustc_const_eval/src/util/mod.rs26
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs13
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs18
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs12
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs5
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs5
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs5
18 files changed, 217 insertions, 189 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
index f751d8c179d..13568b198db 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
@@ -22,8 +22,8 @@ pub(crate) fn maybe_codegen<'tcx>(
 
     match bin_op {
         BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None,
-        BinOp::Add | BinOp::Sub => None,
-        BinOp::Mul => {
+        BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None,
+        BinOp::Mul | BinOp::MulUnchecked => {
             let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
             let ret_val = fx.lib_call(
                 "__multi3",
@@ -69,7 +69,7 @@ pub(crate) fn maybe_codegen<'tcx>(
             }
         }
         BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
-        BinOp::Shl | BinOp::Shr => None,
+        BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
     }
 }
 
@@ -131,9 +131,10 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
             fx.lib_call(name, param_types, vec![], &args);
             Some(out_place.to_cvalue(fx))
         }
+        BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
         BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
         BinOp::Div | BinOp::Rem => unreachable!(),
         BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
-        BinOp::Shl | BinOp::Shr => unreachable!(),
+        BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 1e83c30bd67..5862f18299e 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -472,25 +472,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
         }
 
-        sym::unchecked_add
-        | sym::unchecked_sub
-        | sym::unchecked_mul
-        | sym::exact_div
-        | sym::unchecked_shl
-        | sym::unchecked_shr => {
+        sym::exact_div => {
             intrinsic_args!(fx, args => (x, y); intrinsic);
 
-            // FIXME trap on overflow
-            let bin_op = match intrinsic {
-                sym::unchecked_add => BinOp::Add,
-                sym::unchecked_sub => BinOp::Sub,
-                sym::unchecked_mul => BinOp::Mul,
-                sym::exact_div => BinOp::Div,
-                sym::unchecked_shl => BinOp::Shl,
-                sym::unchecked_shr => BinOp::Shr,
-                _ => unreachable!(),
-            };
-            let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
+            // FIXME trap on inexact
+            let res = crate::num::codegen_int_binop(fx, BinOp::Div, x, y);
             ret.write_cvalue(fx, res);
         }
         sym::saturating_add | sym::saturating_sub => {
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index ba53e01c7a2..ac1a6cce096 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -128,10 +128,11 @@ pub(crate) fn codegen_int_binop<'tcx>(
     let rhs = in_rhs.load_scalar(fx);
 
     let b = fx.bcx.ins();
+    // FIXME trap on overflow for the Unchecked versions
     let val = match bin_op {
-        BinOp::Add => b.iadd(lhs, rhs),
-        BinOp::Sub => b.isub(lhs, rhs),
-        BinOp::Mul => b.imul(lhs, rhs),
+        BinOp::Add | BinOp::AddUnchecked => b.iadd(lhs, rhs),
+        BinOp::Sub | BinOp::SubUnchecked => b.isub(lhs, rhs),
+        BinOp::Mul | BinOp::MulUnchecked => b.imul(lhs, rhs),
         BinOp::Div => {
             if signed {
                 b.sdiv(lhs, rhs)
@@ -149,16 +150,19 @@ pub(crate) fn codegen_int_binop<'tcx>(
         BinOp::BitXor => b.bxor(lhs, rhs),
         BinOp::BitAnd => b.band(lhs, rhs),
         BinOp::BitOr => b.bor(lhs, rhs),
-        BinOp::Shl => b.ishl(lhs, rhs),
-        BinOp::Shr => {
+        BinOp::Shl | BinOp::ShlUnchecked => b.ishl(lhs, rhs),
+        BinOp::Shr | BinOp::ShrUnchecked => {
             if signed {
                 b.sshr(lhs, rhs)
             } else {
                 b.ushr(lhs, rhs)
             }
         }
+        BinOp::Offset => unreachable!("Offset is not an integer operation"),
         // Compare binops handles by `codegen_binop`.
-        _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
+        BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
+            unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
+        }
     };
 
     CValue::by_val(val, in_lhs.layout())
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index e1abb73a504..5a68075991f 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -132,7 +132,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 // all shifts). For 32- and 64-bit types, this matches the semantics
 // of Java. (See related discussion on #1877 and #10183.)
 
-pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     lhs: Bx::Value,
     rhs: Bx::Value,
@@ -143,7 +143,7 @@ pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx.shl(lhs, rhs)
 }
 
-pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     lhs_t: Ty<'tcx>,
     lhs: Bx::Value,
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 9ac2424e76b..8a65dd593b8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -211,52 +211,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 args[1].val.unaligned_volatile_store(bx, dst);
                 return;
             }
-            | sym::unchecked_shl
-            | sym::unchecked_shr
-            | sym::unchecked_add
-            | sym::unchecked_sub
-            | sym::unchecked_mul
-            | sym::exact_div => {
+            sym::exact_div => {
                 let ty = arg_tys[0];
                 match int_type_width_signed(ty, bx.tcx()) {
-                    Some((_width, signed)) => match name {
-                        sym::exact_div => {
-                            if signed {
-                                bx.exactsdiv(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.exactudiv(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()),
-                        sym::unchecked_shr => {
-                            if signed {
-                                bx.ashr(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.lshr(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_add => {
-                            if signed {
-                                bx.unchecked_sadd(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_uadd(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_sub => {
-                            if signed {
-                                bx.unchecked_ssub(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_usub(args[0].immediate(), args[1].immediate())
-                            }
-                        }
-                        sym::unchecked_mul => {
-                            if signed {
-                                bx.unchecked_smul(args[0].immediate(), args[1].immediate())
-                            } else {
-                                bx.unchecked_umul(args[0].immediate(), args[1].immediate())
-                            }
+                    Some((_width, signed)) => {
+                        if signed {
+                            bx.exactsdiv(args[0].immediate(), args[1].immediate())
+                        } else {
+                            bx.exactudiv(args[0].immediate(), args[1].immediate())
                         }
-                        _ => bug!(),
                     },
                     None => {
                         bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 5241a5aee00..2d3d0ec68b8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -798,6 +798,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.add(lhs, rhs)
                 }
             }
+            mir::BinOp::AddUnchecked => {
+                if is_signed {
+                    bx.unchecked_sadd(lhs, rhs)
+                } else {
+                    bx.unchecked_uadd(lhs, rhs)
+                }
+            }
             mir::BinOp::Sub => {
                 if is_float {
                     bx.fsub(lhs, rhs)
@@ -805,6 +812,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.sub(lhs, rhs)
                 }
             }
+            mir::BinOp::SubUnchecked => {
+                if is_signed {
+                    bx.unchecked_ssub(lhs, rhs)
+                } else {
+                    bx.unchecked_usub(lhs, rhs)
+                }
+            }
             mir::BinOp::Mul => {
                 if is_float {
                     bx.fmul(lhs, rhs)
@@ -812,6 +826,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.mul(lhs, rhs)
                 }
             }
+            mir::BinOp::MulUnchecked => {
+                if is_signed {
+                    bx.unchecked_smul(lhs, rhs)
+                } else {
+                    bx.unchecked_umul(lhs, rhs)
+                }
+            }
             mir::BinOp::Div => {
                 if is_float {
                     bx.fdiv(lhs, rhs)
@@ -848,8 +869,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.inbounds_gep(llty, lhs, &[rhs])
                 }
             }
-            mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs),
-            mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs),
+            mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs),
+            mir::BinOp::ShlUnchecked => {
+                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+                bx.shl(lhs, rhs)
+            }
+            mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs),
+            mir::BinOp::ShrUnchecked => {
+                let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+                if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
+            }
             mir::BinOp::Ne
             | mir::BinOp::Lt
             | mir::BinOp::Gt
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 7192bbc00d5..8f4cf23770e 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -234,37 +234,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let r = self.read_immediate(&args[1])?;
                 self.exact_div(&l, &r, dest)?;
             }
-            sym::unchecked_shl
-            | sym::unchecked_shr
-            | sym::unchecked_add
-            | sym::unchecked_sub
-            | sym::unchecked_mul => {
-                let l = self.read_immediate(&args[0])?;
-                let r = self.read_immediate(&args[1])?;
-                let bin_op = match intrinsic_name {
-                    sym::unchecked_shl => BinOp::Shl,
-                    sym::unchecked_shr => BinOp::Shr,
-                    sym::unchecked_add => BinOp::Add,
-                    sym::unchecked_sub => BinOp::Sub,
-                    sym::unchecked_mul => BinOp::Mul,
-                    _ => bug!(),
-                };
-                let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
-                if overflowed {
-                    let layout = self.layout_of(substs.type_at(0))?;
-                    let r_val = r.to_scalar().to_bits(layout.size)?;
-                    if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
-                        throw_ub_custom!(
-                            fluent::const_eval_overflow_shift,
-                            val = r_val,
-                            name = intrinsic_name
-                        );
-                    } else {
-                        throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
-                    }
-                }
-                self.write_scalar(val, dest)?;
-            }
             sym::rotate_left | sym::rotate_right => {
                 // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
                 // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7186148daf0..7bca7efdf5a 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -3,10 +3,13 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::symbol::sym;
 use rustc_target::abi::Abi;
 
 use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
 
+use crate::fluent_generated as fluent;
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Applies the binary operation `op` to the two operands and writes a tuple of the result
     /// 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> {
     ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         use rustc_middle::mir::BinOp::*;
 
+        let throw_ub_on_overflow = match bin_op {
+            AddUnchecked => Some(sym::unchecked_add),
+            SubUnchecked => Some(sym::unchecked_sub),
+            MulUnchecked => Some(sym::unchecked_mul),
+            ShlUnchecked => Some(sym::unchecked_shl),
+            ShrUnchecked => Some(sym::unchecked_shr),
+            _ => None,
+        };
+
         // Shift ops can have an RHS with a different numeric type.
-        if bin_op == Shl || bin_op == Shr {
+        if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
             let size = u128::from(left_layout.size.bits());
             // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
             // zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
             // consistent results for the same value represented at different bit widths.
             assert!(size <= 128);
+            let original_r = r;
             let overflow = r >= size;
             // The shift offset is implicitly masked to the type size, to make sure this operation
             // 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> {
             let result = if left_layout.abi.is_signed() {
                 let l = self.sign_extend(l, left_layout) as i128;
                 let result = match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 };
                 result as u128
             } else {
                 match bin_op {
-                    Shl => l.checked_shl(r).unwrap(),
-                    Shr => l.checked_shr(r).unwrap(),
+                    Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+                    Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
                     _ => bug!(),
                 }
             };
             let truncated = self.truncate(result, left_layout);
+
+            if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                throw_ub_custom!(
+                    fluent::const_eval_overflow_shift,
+                    val = original_r,
+                    name = intrinsic_name
+                );
+            }
+
             return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
         }
 
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Rem if r == 0 => throw_ub!(RemainderByZero),
                 Div => Some(i128::overflowing_div),
                 Rem => Some(i128::overflowing_rem),
-                Add => Some(i128::overflowing_add),
-                Sub => Some(i128::overflowing_sub),
-                Mul => Some(i128::overflowing_mul),
+                Add | AddUnchecked => Some(i128::overflowing_add),
+                Sub | SubUnchecked => Some(i128::overflowing_sub),
+                Mul | MulUnchecked => Some(i128::overflowing_mul),
                 _ => None,
             };
             if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // If that truncation loses any information, we have an overflow.
                 let result = result as u128;
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || self.sign_extend(truncated, left_layout) != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
         }
 
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
             BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
 
-            Add | Sub | Mul | Rem | Div => {
+            Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
                 assert!(!left_layout.abi.is_signed());
                 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
-                    Add => u128::overflowing_add,
-                    Sub => u128::overflowing_sub,
-                    Mul => u128::overflowing_mul,
+                    Add | AddUnchecked => u128::overflowing_add,
+                    Sub | SubUnchecked => u128::overflowing_sub,
+                    Mul | MulUnchecked => u128::overflowing_mul,
                     Div if r == 0 => throw_ub!(DivisionByZero),
                     Rem if r == 0 => throw_ub!(RemainderByZero),
                     Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Truncate to target type.
                 // If that truncation loses any information, we have an overflow.
                 let truncated = self.truncate(result, left_layout);
-                return Ok((
-                    Scalar::from_uint(truncated, size),
-                    oflo || truncated != result,
-                    left_layout.ty,
-                ));
+                let overflow = oflo || truncated != result;
+                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                }
+                return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
             }
 
             _ => span_bug!(
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1e60a1e72ea..619da8abb7d 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -9,27 +9,7 @@ use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::LayoutOf;
 
 use super::{ImmTy, InterpCx, Machine};
-
-/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
-/// same type as the result.
-#[inline]
-fn binop_left_homogeneous(op: mir::BinOp) -> bool {
-    use rustc_middle::mir::BinOp::*;
-    match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
-        Eq | Ne | Lt | Le | Gt | Ge => false,
-    }
-}
-/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
-/// same type as the LHS.
-#[inline]
-fn binop_right_homogeneous(op: mir::BinOp) -> bool {
-    use rustc_middle::mir::BinOp::*;
-    match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
-        Offset | Shl | Shr => false,
-    }
-}
+use crate::util;
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Returns `true` as long as there are more things to do.
@@ -179,9 +159,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
 
             BinaryOp(bin_op, box (ref left, ref right)) => {
-                let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
+                let layout = util::binop_left_homogeneous(bin_op).then_some(dest.layout);
                 let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
-                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+                let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
                 let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                 self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
             }
@@ -189,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
                 // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                 let left = self.read_immediate(&self.eval_operand(left, None)?)?;
-                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+                let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
                 let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                 self.binop_with_overflow(bin_op, &left, &right, &dest)?;
             }
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 44b143c77f3..3802df5d3fc 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -569,13 +569,18 @@ impl<'tcx> Validator<'_, 'tcx> {
                     | BinOp::Gt
                     | BinOp::Offset
                     | BinOp::Add
+                    | BinOp::AddUnchecked
                     | BinOp::Sub
+                    | BinOp::SubUnchecked
                     | BinOp::Mul
+                    | BinOp::MulUnchecked
                     | BinOp::BitXor
                     | BinOp::BitAnd
                     | BinOp::BitOr
                     | BinOp::Shl
-                    | BinOp::Shr => {}
+                    | BinOp::ShlUnchecked
+                    | BinOp::Shr
+                    | BinOp::ShrUnchecked => {}
                 }
 
                 self.validate_operand(lhs)?;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3c350e25ba6..f197541da5b 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -498,8 +498,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         macro_rules! check_kinds {
-            ($t:expr, $text:literal, $($patterns:tt)*) => {
-                if !matches!(($t).kind(), $($patterns)*) {
+            ($t:expr, $text:literal, $typat:pat) => {
+                if !matches!(($t).kind(), $typat) {
                     self.fail(location, format!($text, $t));
                 }
             };
@@ -527,6 +527,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 use BinOp::*;
                 let a = vals.0.ty(&self.body.local_decls, self.tcx);
                 let b = vals.1.ty(&self.body.local_decls, self.tcx);
+                if crate::util::binop_right_homogeneous(*op) {
+                    if let Eq | Lt | Le | Ne | Ge | Gt = op {
+                        // The function pointer types can have lifetimes
+                        if !self.mir_assign_valid_types(a, b) {
+                            self.fail(
+                                location,
+                                format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"),
+                            );
+                        }
+                    } else if a != b {
+                        self.fail(
+                            location,
+                            format!(
+                                "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}"
+                            ),
+                        );
+                    }
+                }
+
                 match op {
                     Offset => {
                         check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
@@ -538,7 +557,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot compare type {:?}",
+                                "Cannot {op:?} compare type {:?}",
                                 ty::Bool
                                     | ty::Char
                                     | ty::Int(..)
@@ -548,19 +567,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                                     | ty::FnPtr(..)
                             )
                         }
-                        // The function pointer types can have lifetimes
-                        if !self.mir_assign_valid_types(a, b) {
-                            self.fail(
-                                location,
-                                format!("Cannot compare unequal types {:?} and {:?}", a, b),
-                            );
-                        }
                     }
-                    Shl | Shr => {
+                    AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
+                    | ShrUnchecked => {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot shift non-integer type {:?}",
+                                "Cannot {op:?} non-integer type {:?}",
                                 ty::Uint(..) | ty::Int(..)
                             )
                         }
@@ -569,37 +582,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot perform bitwise op on type {:?}",
+                                "Cannot perform bitwise op {op:?} on type {:?}",
                                 ty::Uint(..) | ty::Int(..) | ty::Bool
                             )
                         }
-                        if a != b {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Cannot perform bitwise op on unequal types {:?} and {:?}",
-                                    a, b
-                                ),
-                            );
-                        }
                     }
                     Add | Sub | Mul | Div | Rem => {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot perform arithmetic on type {:?}",
+                                "Cannot perform arithmetic {op:?} on type {:?}",
                                 ty::Uint(..) | ty::Int(..) | ty::Float(..)
                             )
                         }
-                        if a != b {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Cannot perform arithmetic on unequal types {:?} and {:?}",
-                                    a, b
-                                ),
-                            );
-                        }
                     }
                 }
             }
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 7641f560714..289e3422595 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -1,3 +1,5 @@
+use rustc_middle::mir;
+
 mod alignment;
 mod check_validity_requirement;
 mod compare_types;
@@ -7,3 +9,27 @@ pub use self::alignment::is_disaligned;
 pub use self::check_validity_requirement::check_validity_requirement;
 pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
 pub use self::type_name::type_name;
+
+/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
+/// same type as the result.
+#[inline]
+pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
+    use rustc_middle::mir::BinOp::*;
+    match op {
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
+        Eq | Ne | Lt | Le | Gt | Ge => false,
+    }
+}
+
+/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
+/// same type as the LHS.
+#[inline]
+pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool {
+    use rustc_middle::mir::BinOp::*;
+    match op {
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+        Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 1a65f74f4fe..ddc9fdc9a42 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1267,10 +1267,16 @@ pub enum UnOp {
 pub enum BinOp {
     /// The `+` operator (addition)
     Add,
+    /// Like `Add`, but with UB on overflow.  (Integers only.)
+    AddUnchecked,
     /// The `-` operator (subtraction)
     Sub,
+    /// Like `Sub`, but with UB on overflow.  (Integers only.)
+    SubUnchecked,
     /// The `*` operator (multiplication)
     Mul,
+    /// Like `Mul`, but with UB on overflow.  (Integers only.)
+    MulUnchecked,
     /// The `/` operator (division)
     ///
     /// For integer types, division by zero is UB, as is `MIN / -1` for signed.
@@ -1296,10 +1302,17 @@ pub enum BinOp {
     ///
     /// The offset is truncated to the size of the first operand before shifting.
     Shl,
+    /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+    ShlUnchecked,
     /// The `>>` operator (shift right)
     ///
     /// The offset is truncated to the size of the first operand before shifting.
+    ///
+    /// This is an arithmetic shift if the LHS is signed
+    /// and a logical shift if the LHS is unsigned.
     Shr,
+    /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+    ShrUnchecked,
     /// The `==` operator (equality)
     Eq,
     /// The `<` operator (less than)
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 5ca82413448..4a16abdd4e3 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -235,8 +235,11 @@ impl<'tcx> BinOp {
         // FIXME: handle SIMD correctly
         match self {
             &BinOp::Add
+            | &BinOp::AddUnchecked
             | &BinOp::Sub
+            | &BinOp::SubUnchecked
             | &BinOp::Mul
+            | &BinOp::MulUnchecked
             | &BinOp::Div
             | &BinOp::Rem
             | &BinOp::BitXor
@@ -246,7 +249,11 @@ impl<'tcx> BinOp {
                 assert_eq!(lhs_ty, rhs_ty);
                 lhs_ty
             }
-            &BinOp::Shl | &BinOp::Shr | &BinOp::Offset => {
+            &BinOp::Shl
+            | &BinOp::ShlUnchecked
+            | &BinOp::Shr
+            | &BinOp::ShrUnchecked
+            | &BinOp::Offset => {
                 lhs_ty // lhs_ty can be != rhs_ty
             }
             &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
@@ -293,7 +300,14 @@ impl BinOp {
             BinOp::Gt => hir::BinOpKind::Gt,
             BinOp::Le => hir::BinOpKind::Le,
             BinOp::Ge => hir::BinOpKind::Ge,
-            BinOp::Offset => unreachable!(),
+            BinOp::AddUnchecked
+            | BinOp::SubUnchecked
+            | BinOp::MulUnchecked
+            | BinOp::ShlUnchecked
+            | BinOp::ShrUnchecked
+            | BinOp::Offset => {
+                unreachable!()
+            }
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 3a7d58f7125..ce98e9b0c84 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -85,8 +85,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                     sym::wrapping_add
                     | sym::wrapping_sub
                     | sym::wrapping_mul
+                    | sym::unchecked_add
+                    | sym::unchecked_sub
+                    | sym::unchecked_mul
                     | sym::unchecked_div
-                    | sym::unchecked_rem => {
+                    | sym::unchecked_rem
+                    | sym::unchecked_shl
+                    | sym::unchecked_shr => {
                         let target = target.unwrap();
                         let lhs;
                         let rhs;
@@ -99,8 +104,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                             sym::wrapping_add => BinOp::Add,
                             sym::wrapping_sub => BinOp::Sub,
                             sym::wrapping_mul => BinOp::Mul,
+                            sym::unchecked_add => BinOp::AddUnchecked,
+                            sym::unchecked_sub => BinOp::SubUnchecked,
+                            sym::unchecked_mul => BinOp::MulUnchecked,
                             sym::unchecked_div => BinOp::Div,
                             sym::unchecked_rem => BinOp::Rem,
+                            sym::unchecked_shl => BinOp::ShlUnchecked,
+                            sym::unchecked_shr => BinOp::ShrUnchecked,
                             _ => bug!("unexpected intrinsic"),
                         };
                         block.statements.push(Statement {
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 6bd030b13d1..fd108eda5e8 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -250,15 +250,20 @@ impl Stable for mir::BinOp {
         use mir::BinOp;
         match self {
             BinOp::Add => stable_mir::mir::BinOp::Add,
+            BinOp::AddUnchecked => stable_mir::mir::BinOp::AddUnchecked,
             BinOp::Sub => stable_mir::mir::BinOp::Sub,
+            BinOp::SubUnchecked => stable_mir::mir::BinOp::SubUnchecked,
             BinOp::Mul => stable_mir::mir::BinOp::Mul,
+            BinOp::MulUnchecked => stable_mir::mir::BinOp::MulUnchecked,
             BinOp::Div => stable_mir::mir::BinOp::Div,
             BinOp::Rem => stable_mir::mir::BinOp::Rem,
             BinOp::BitXor => stable_mir::mir::BinOp::BitXor,
             BinOp::BitAnd => stable_mir::mir::BinOp::BitAnd,
             BinOp::BitOr => stable_mir::mir::BinOp::BitOr,
             BinOp::Shl => stable_mir::mir::BinOp::Shl,
+            BinOp::ShlUnchecked => stable_mir::mir::BinOp::ShlUnchecked,
             BinOp::Shr => stable_mir::mir::BinOp::Shr,
+            BinOp::ShrUnchecked => stable_mir::mir::BinOp::ShrUnchecked,
             BinOp::Eq => stable_mir::mir::BinOp::Eq,
             BinOp::Lt => stable_mir::mir::BinOp::Lt,
             BinOp::Le => stable_mir::mir::BinOp::Le,
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index 9df7b4945b7..468e915d1a0 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -88,15 +88,20 @@ pub enum AssertMessage {
 #[derive(Clone, Debug)]
 pub enum BinOp {
     Add,
+    AddUnchecked,
     Sub,
+    SubUnchecked,
     Mul,
+    MulUnchecked,
     Div,
     Rem,
     BitXor,
     BitAnd,
     BitOr,
     Shl,
+    ShlUnchecked,
     Shr,
+    ShrUnchecked,
     Eq,
     Lt,
     Le,
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index ce77df0df5d..387adda8f57 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -78,8 +78,9 @@ pub(crate) fn destructure_const<'tcx>(
 fn check_binop(op: mir::BinOp) -> bool {
     use mir::BinOp::*;
     match op {
-        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
-        | Ge | Gt => true,
+        Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+        | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge
+        | Gt => true,
         Offset => false,
     }
 }