@@ -1770,7 +1770,6 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1770
1770
} ;
1771
1771
let is_float = ty:: type_is_fp ( intype) ;
1772
1772
let is_signed = ty:: type_is_signed ( intype) ;
1773
- let rhs = base:: cast_shift_expr_rhs ( bcx, op, lhs, rhs) ;
1774
1773
let info = expr_info ( binop_expr) ;
1775
1774
1776
1775
let binop_debug_loc = binop_expr. debug_loc ( ) ;
@@ -1843,13 +1842,17 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1843
1842
ast:: BiBitOr => Or ( bcx, lhs, rhs, binop_debug_loc) ,
1844
1843
ast:: BiBitAnd => And ( bcx, lhs, rhs, binop_debug_loc) ,
1845
1844
ast:: BiBitXor => Xor ( bcx, lhs, rhs, binop_debug_loc) ,
1846
- ast:: BiShl => Shl ( bcx, lhs, rhs, binop_debug_loc) ,
1845
+ ast:: BiShl => {
1846
+ let ( newbcx, res) = with_overflow_check (
1847
+ bcx, OverflowOp :: Shl , info, lhs_t, lhs, rhs, binop_debug_loc) ;
1848
+ bcx = newbcx;
1849
+ res
1850
+ }
1847
1851
ast:: BiShr => {
1848
- if is_signed {
1849
- AShr ( bcx, lhs, rhs, binop_debug_loc)
1850
- } else {
1851
- LShr ( bcx, lhs, rhs, binop_debug_loc)
1852
- }
1852
+ let ( newbcx, res) = with_overflow_check (
1853
+ bcx, OverflowOp :: Shr , info, lhs_t, lhs, rhs, binop_debug_loc) ;
1854
+ bcx = newbcx;
1855
+ res
1853
1856
}
1854
1857
ast:: BiEq | ast:: BiNe | ast:: BiLt | ast:: BiGe | ast:: BiLe | ast:: BiGt => {
1855
1858
if is_simd {
@@ -2389,9 +2392,38 @@ enum OverflowOp {
2389
2392
Add ,
2390
2393
Sub ,
2391
2394
Mul ,
2395
+ Shl ,
2396
+ Shr ,
2392
2397
}
2393
2398
2394
2399
impl OverflowOp {
2400
+ fn codegen_strategy ( & self ) -> OverflowCodegen {
2401
+ use self :: OverflowCodegen :: { ViaIntrinsic , ViaInputCheck } ;
2402
+ match * self {
2403
+ OverflowOp :: Add => ViaIntrinsic ( OverflowOpViaIntrinsic :: Add ) ,
2404
+ OverflowOp :: Sub => ViaIntrinsic ( OverflowOpViaIntrinsic :: Sub ) ,
2405
+ OverflowOp :: Mul => ViaIntrinsic ( OverflowOpViaIntrinsic :: Mul ) ,
2406
+
2407
+ OverflowOp :: Shl => ViaInputCheck ( OverflowOpViaInputCheck :: Shl ) ,
2408
+ OverflowOp :: Shr => ViaInputCheck ( OverflowOpViaInputCheck :: Shr ) ,
2409
+ }
2410
+ }
2411
+ }
2412
+
2413
+ enum OverflowCodegen {
2414
+ ViaIntrinsic ( OverflowOpViaIntrinsic ) ,
2415
+ ViaInputCheck ( OverflowOpViaInputCheck ) ,
2416
+ }
2417
+
2418
+ enum OverflowOpViaInputCheck { Shl , Shr , }
2419
+
2420
+ enum OverflowOpViaIntrinsic { Add , Sub , Mul , }
2421
+
2422
+ impl OverflowOpViaIntrinsic {
2423
+ fn to_intrinsic < ' blk , ' tcx > ( & self , bcx : Block < ' blk , ' tcx > , lhs_ty : Ty ) -> ValueRef {
2424
+ let name = self . to_intrinsic_name ( bcx. tcx ( ) , lhs_ty) ;
2425
+ bcx. ccx ( ) . get_intrinsic ( & name)
2426
+ }
2395
2427
fn to_intrinsic_name ( & self , tcx : & ty:: ctxt , ty : Ty ) -> & ' static str {
2396
2428
use syntax:: ast:: IntTy :: * ;
2397
2429
use syntax:: ast:: UintTy :: * ;
@@ -2413,7 +2445,7 @@ impl OverflowOp {
2413
2445
} ;
2414
2446
2415
2447
match * self {
2416
- OverflowOp :: Add => match new_sty {
2448
+ OverflowOpViaIntrinsic :: Add => match new_sty {
2417
2449
ty_int( TyI8 ) => "llvm.sadd.with.overflow.i8" ,
2418
2450
ty_int( TyI16 ) => "llvm.sadd.with.overflow.i16" ,
2419
2451
ty_int( TyI32 ) => "llvm.sadd.with.overflow.i32" ,
@@ -2426,7 +2458,7 @@ impl OverflowOp {
2426
2458
2427
2459
_ => unreachable ! ( ) ,
2428
2460
} ,
2429
- OverflowOp :: Sub => match new_sty {
2461
+ OverflowOpViaIntrinsic :: Sub => match new_sty {
2430
2462
ty_int( TyI8 ) => "llvm.ssub.with.overflow.i8" ,
2431
2463
ty_int( TyI16 ) => "llvm.ssub.with.overflow.i16" ,
2432
2464
ty_int( TyI32 ) => "llvm.ssub.with.overflow.i32" ,
@@ -2439,7 +2471,7 @@ impl OverflowOp {
2439
2471
2440
2472
_ => unreachable ! ( ) ,
2441
2473
} ,
2442
- OverflowOp :: Mul => match new_sty {
2474
+ OverflowOpViaIntrinsic :: Mul => match new_sty {
2443
2475
ty_int( TyI8 ) => "llvm.smul.with.overflow.i8" ,
2444
2476
ty_int( TyI16 ) => "llvm.smul.with.overflow.i16" ,
2445
2477
ty_int( TyI32 ) => "llvm.smul.with.overflow.i32" ,
@@ -2454,16 +2486,14 @@ impl OverflowOp {
2454
2486
} ,
2455
2487
}
2456
2488
}
2457
- }
2458
2489
2459
-
2460
- fn with_overflow_check < ' a , ' b > ( bcx : Block < ' a , ' b > , oop : OverflowOp , info : NodeIdAndSpan ,
2461
- lhs_t : Ty , lhs : ValueRef , rhs : ValueRef , binop_debug_loc : DebugLoc )
2462
- -> ( Block < ' a , ' b > , ValueRef ) {
2463
- if bcx. unreachable . get ( ) { return ( bcx, _Undef ( lhs) ) ; }
2464
- if bcx. ccx ( ) . check_overflow ( ) {
2465
- let name = oop. to_intrinsic_name ( bcx. tcx ( ) , lhs_t) ;
2466
- let llfn = bcx. ccx ( ) . get_intrinsic ( & name) ;
2490
+ fn build_intrinsic_call < ' blk , ' tcx > ( & self , bcx : Block < ' blk , ' tcx > ,
2491
+ info : NodeIdAndSpan ,
2492
+ lhs_t : Ty < ' tcx > , lhs : ValueRef ,
2493
+ rhs : ValueRef ,
2494
+ binop_debug_loc : DebugLoc )
2495
+ -> ( Block < ' blk , ' tcx > , ValueRef ) {
2496
+ let llfn = self . to_intrinsic ( bcx, lhs_t) ;
2467
2497
2468
2498
let val = Call ( bcx, llfn, & [ lhs, rhs] , None , binop_debug_loc) ;
2469
2499
let result = ExtractValue ( bcx, val, 0 ) ; // iN operation result
@@ -2482,11 +2512,118 @@ fn with_overflow_check<'a, 'b>(bcx: Block<'a, 'b>, oop: OverflowOp, info: NodeId
2482
2512
InternedString :: new ( "arithmetic operation overflowed" ) ) ) ;
2483
2513
2484
2514
( bcx, result)
2515
+ }
2516
+ }
2517
+
2518
+ impl OverflowOpViaInputCheck {
2519
+ fn build_with_input_check < ' blk , ' tcx > ( & self ,
2520
+ bcx : Block < ' blk , ' tcx > ,
2521
+ info : NodeIdAndSpan ,
2522
+ lhs_t : Ty < ' tcx > ,
2523
+ lhs : ValueRef ,
2524
+ rhs : ValueRef ,
2525
+ binop_debug_loc : DebugLoc )
2526
+ -> ( Block < ' blk , ' tcx > , ValueRef )
2527
+ {
2528
+ let lhs_llty = val_ty ( lhs) ;
2529
+ let rhs_llty = val_ty ( rhs) ;
2530
+
2531
+ // Panic if any bits are set outside of bits that we always
2532
+ // mask in.
2533
+ //
2534
+ // Note that the mask's value is derived from the LHS type
2535
+ // (since that is where the 32/64 distinction is relevant) but
2536
+ // the mask's type must match the RHS type (since they will
2537
+ // both be fed into a and-binop)
2538
+ let invert_mask = !shift_mask_val ( lhs_llty) ;
2539
+ let invert_mask = C_integral ( rhs_llty, invert_mask, true ) ;
2540
+
2541
+ let outer_bits = And ( bcx, rhs, invert_mask, binop_debug_loc) ;
2542
+ let cond = ICmp ( bcx, llvm:: IntNE , outer_bits,
2543
+ C_integral ( rhs_llty, 0 , false ) , binop_debug_loc) ;
2544
+ let result = match * self {
2545
+ OverflowOpViaInputCheck :: Shl =>
2546
+ build_unchecked_lshift ( bcx, lhs, rhs, binop_debug_loc) ,
2547
+ OverflowOpViaInputCheck :: Shr =>
2548
+ build_unchecked_rshift ( bcx, lhs_t, lhs, rhs, binop_debug_loc) ,
2549
+ } ;
2550
+ let bcx =
2551
+ base:: with_cond ( bcx, cond, |bcx|
2552
+ controlflow:: trans_fail ( bcx, info,
2553
+ InternedString :: new ( "shift operation overflowed" ) ) ) ;
2554
+
2555
+ ( bcx, result)
2556
+ }
2557
+ }
2558
+
2559
+ fn shift_mask_val ( llty : Type ) -> u64 {
2560
+ // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc.
2561
+ llty. int_width ( ) - 1
2562
+ }
2563
+
2564
+ // To avoid UB from LLVM, these two functions mask RHS with an
2565
+ // appropriate mask unconditionally (i.e. the fallback behavior for
2566
+ // all shifts). For 32- and 64-bit types, this matches the semantics
2567
+ // of Java. (See related discussion on #1877 and #10183.)
2568
+
2569
+ fn build_unchecked_lshift < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > ,
2570
+ lhs : ValueRef ,
2571
+ rhs : ValueRef ,
2572
+ binop_debug_loc : DebugLoc ) -> ValueRef {
2573
+ let rhs = base:: cast_shift_expr_rhs ( bcx, ast:: BinOp_ :: BiShl , lhs, rhs) ;
2574
+ // #1877, #10183: Ensure that input is always valid
2575
+ let rhs = shift_mask_rhs ( bcx, rhs, binop_debug_loc) ;
2576
+ Shl ( bcx, lhs, rhs, binop_debug_loc)
2577
+ }
2578
+
2579
+ fn build_unchecked_rshift < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > ,
2580
+ lhs_t : Ty < ' tcx > ,
2581
+ lhs : ValueRef ,
2582
+ rhs : ValueRef ,
2583
+ binop_debug_loc : DebugLoc ) -> ValueRef {
2584
+ let rhs = base:: cast_shift_expr_rhs ( bcx, ast:: BinOp_ :: BiShr , lhs, rhs) ;
2585
+ // #1877, #10183: Ensure that input is always valid
2586
+ let rhs = shift_mask_rhs ( bcx, rhs, binop_debug_loc) ;
2587
+ let is_signed = ty:: type_is_signed ( lhs_t) ;
2588
+ if is_signed {
2589
+ AShr ( bcx, lhs, rhs, binop_debug_loc)
2590
+ } else {
2591
+ LShr ( bcx, lhs, rhs, binop_debug_loc)
2592
+ }
2593
+ }
2594
+
2595
+ fn shift_mask_rhs < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > ,
2596
+ rhs : ValueRef ,
2597
+ debug_loc : DebugLoc ) -> ValueRef {
2598
+ let rhs_llty = val_ty ( rhs) ;
2599
+ let mask = shift_mask_val ( rhs_llty) ;
2600
+ And ( bcx, rhs, C_integral ( rhs_llty, mask, false ) , debug_loc)
2601
+ }
2602
+
2603
+ fn with_overflow_check < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > , oop : OverflowOp , info : NodeIdAndSpan ,
2604
+ lhs_t : Ty < ' tcx > , lhs : ValueRef ,
2605
+ rhs : ValueRef ,
2606
+ binop_debug_loc : DebugLoc )
2607
+ -> ( Block < ' blk , ' tcx > , ValueRef ) {
2608
+ if bcx. unreachable . get ( ) { return ( bcx, _Undef ( lhs) ) ; }
2609
+ if bcx. ccx ( ) . check_overflow ( ) {
2610
+
2611
+ match oop. codegen_strategy ( ) {
2612
+ OverflowCodegen :: ViaIntrinsic ( oop) =>
2613
+ oop. build_intrinsic_call ( bcx, info, lhs_t, lhs, rhs, binop_debug_loc) ,
2614
+ OverflowCodegen :: ViaInputCheck ( oop) =>
2615
+ oop. build_with_input_check ( bcx, info, lhs_t, lhs, rhs, binop_debug_loc) ,
2616
+ }
2485
2617
} else {
2486
2618
let res = match oop {
2487
2619
OverflowOp :: Add => Add ( bcx, lhs, rhs, binop_debug_loc) ,
2488
2620
OverflowOp :: Sub => Sub ( bcx, lhs, rhs, binop_debug_loc) ,
2489
2621
OverflowOp :: Mul => Mul ( bcx, lhs, rhs, binop_debug_loc) ,
2622
+
2623
+ OverflowOp :: Shl =>
2624
+ build_unchecked_lshift ( bcx, lhs, rhs, binop_debug_loc) ,
2625
+ OverflowOp :: Shr =>
2626
+ build_unchecked_rshift ( bcx, lhs_t, lhs, rhs, binop_debug_loc) ,
2490
2627
} ;
2491
2628
( bcx, res)
2492
2629
}
0 commit comments