@@ -5,7 +5,7 @@ use crate::back::write::{
55 compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm,
66 submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType , OngoingCodegen ,
77} ;
8- use crate :: common:: { IntPredicate , RealPredicate , TypeKind } ;
8+ use crate :: common:: { self , IntPredicate , RealPredicate , TypeKind } ;
99use crate :: errors;
1010use crate :: meth;
1111use crate :: mir;
@@ -33,7 +33,7 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
3333use rustc_middle:: query:: Providers ;
3434use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf , TyAndLayout } ;
3535use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt } ;
36- use rustc_session:: config:: { self , CrateType , EntryFnType , OutputType } ;
36+ use rustc_session:: config:: { self , CrateType , EntryFnType , OptLevel , OutputType } ;
3737use rustc_session:: Session ;
3838use rustc_span:: symbol:: sym;
3939use rustc_span:: Symbol ;
@@ -300,14 +300,35 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
300300 }
301301}
302302
303- pub fn cast_shift_expr_rhs < ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > (
303+ /// Returns `rhs` sufficiently masked, truncated, and/or extended so that
304+ /// it can be used to shift `lhs`.
305+ ///
306+ /// Shifts in MIR are all allowed to have mismatched LHS & RHS types.
307+ /// The shift methods in `BuilderMethods`, however, are fully homogeneous
308+ /// (both parameters and the return type are all the same type).
309+ ///
310+ /// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds,
311+ /// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts.
312+ /// For 32- and 64-bit types, this matches the semantics
313+ /// of Java. (See related discussion on #1877 and #10183.)
314+ ///
315+ /// If `is_unchecked` is true, this does no masking, and adds sufficient `assume`
316+ /// calls or operation flags to preserve as much freedom to optimize as possible.
317+ pub fn build_shift_expr_rhs < ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > (
304318 bx : & mut Bx ,
305319 lhs : Bx :: Value ,
306- rhs : Bx :: Value ,
320+ mut rhs : Bx :: Value ,
321+ is_unchecked : bool ,
307322) -> Bx :: Value {
308323 // Shifts may have any size int on the rhs
309324 let mut rhs_llty = bx. cx ( ) . val_ty ( rhs) ;
310325 let mut lhs_llty = bx. cx ( ) . val_ty ( lhs) ;
326+
327+ let mask = common:: shift_mask_val ( bx, lhs_llty, rhs_llty, false ) ;
328+ if !is_unchecked {
329+ rhs = bx. and ( rhs, mask) ;
330+ }
331+
311332 if bx. cx ( ) . type_kind ( rhs_llty) == TypeKind :: Vector {
312333 rhs_llty = bx. cx ( ) . element_type ( rhs_llty)
313334 }
@@ -317,6 +338,12 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
317338 let rhs_sz = bx. cx ( ) . int_width ( rhs_llty) ;
318339 let lhs_sz = bx. cx ( ) . int_width ( lhs_llty) ;
319340 if lhs_sz < rhs_sz {
341+ if is_unchecked && bx. sess ( ) . opts . optimize != OptLevel :: No {
342+ // FIXME: Use `trunc nuw` once that's available
343+ let inrange = bx. icmp ( IntPredicate :: IntULE , rhs, mask) ;
344+ bx. assume ( inrange) ;
345+ }
346+
320347 bx. trunc ( rhs, lhs_llty)
321348 } else if lhs_sz > rhs_sz {
322349 // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the
0 commit comments