@@ -80,6 +80,7 @@ use libc::{c_uint, uint64_t};
80
80
use std:: c_str:: ToCStr ;
81
81
use std:: cell:: { Cell , RefCell } ;
82
82
use std:: rc:: Rc ;
83
+ use std:: { i8, i16, i32, i64} ;
83
84
use syntax:: abi:: { X86 , X86_64 , Arm , Mips , Rust , RustIntrinsic } ;
84
85
use syntax:: ast_util:: { local_def, is_local} ;
85
86
use syntax:: attr:: AttrMetaMethods ;
@@ -777,35 +778,77 @@ pub fn cast_shift_rhs(op: ast::BinOp,
777
778
}
778
779
}
779
780
780
- pub fn fail_if_zero < ' a > (
781
+ pub fn fail_if_zero_or_overflows < ' a > (
781
782
cx : & ' a Block < ' a > ,
782
783
span : Span ,
783
784
divrem : ast:: BinOp ,
785
+ lhs : ValueRef ,
784
786
rhs : ValueRef ,
785
787
rhs_t : ty:: t )
786
788
-> & ' a Block < ' a > {
787
- let text = if divrem == ast:: BiDiv {
788
- "attempted to divide by zero"
789
+ let ( zero_text, overflow_text) = if divrem == ast:: BiDiv {
790
+ ( "attempted to divide by zero" ,
791
+ "attempted to divide with overflow" )
789
792
} else {
790
- "attempted remainder with a divisor of zero"
793
+ ( "attempted remainder with a divisor of zero" ,
794
+ "attempted remainder with overflow" )
791
795
} ;
792
- let is_zero = match ty:: get ( rhs_t) . sty {
793
- ty:: ty_int( t) => {
794
- let zero = C_integral ( Type :: int_from_ty ( cx. ccx ( ) , t) , 0u64 , false ) ;
795
- ICmp ( cx, lib:: llvm:: IntEQ , rhs, zero)
796
- }
797
- ty:: ty_uint( t) => {
798
- let zero = C_integral ( Type :: uint_from_ty ( cx. ccx ( ) , t) , 0u64 , false ) ;
799
- ICmp ( cx, lib:: llvm:: IntEQ , rhs, zero)
800
- }
801
- _ => {
802
- cx. sess ( ) . bug ( format ! ( "fail-if-zero on unexpected type: {}" ,
803
- ty_to_str( cx. tcx( ) , rhs_t) ) . as_slice ( ) ) ;
804
- }
796
+ let ( is_zero, is_signed ) = match ty:: get ( rhs_t) . sty {
797
+ ty:: ty_int( t) => {
798
+ let zero = C_integral ( Type :: int_from_ty ( cx. ccx ( ) , t) , 0u64 , false ) ;
799
+ ( ICmp ( cx, lib:: llvm:: IntEQ , rhs, zero) , true )
800
+ }
801
+ ty:: ty_uint( t) => {
802
+ let zero = C_integral ( Type :: uint_from_ty ( cx. ccx ( ) , t) , 0u64 , false ) ;
803
+ ( ICmp ( cx, lib:: llvm:: IntEQ , rhs, zero) , false )
804
+ }
805
+ _ => {
806
+ cx. sess ( ) . bug ( format ! ( "fail-if-zero on unexpected type: {}" ,
807
+ ty_to_str( cx. tcx( ) , rhs_t) ) . as_slice ( ) ) ;
808
+ }
805
809
} ;
806
- with_cond ( cx, is_zero, |bcx| {
807
- controlflow:: trans_fail ( bcx, span, InternedString :: new ( text) )
808
- } )
810
+ let bcx = with_cond ( cx, is_zero, |bcx| {
811
+ controlflow:: trans_fail ( bcx, span, InternedString :: new ( zero_text) )
812
+ } ) ;
813
+
814
+ // To quote LLVM's documentation for the sdiv instruction:
815
+ //
816
+ // Division by zero leads to undefined behavior. Overflow also leads
817
+ // to undefined behavior; this is a rare case, but can occur, for
818
+ // example, by doing a 32-bit division of -2147483648 by -1.
819
+ //
820
+ // In order to avoid undefined behavior, we perform runtime checks for
821
+ // signed division/remainder which would trigger overflow. For unsigned
822
+ // integers, no action beyond checking for zero need be taken.
823
+ if is_signed {
824
+ let ( llty, min) = match ty:: get ( rhs_t) . sty {
825
+ ty:: ty_int( t) => {
826
+ let llty = Type :: int_from_ty ( cx. ccx ( ) , t) ;
827
+ let min = match t {
828
+ ast:: TyI if llty == Type :: i32 ( cx. ccx ( ) ) => i32:: MIN as u64 ,
829
+ ast:: TyI => i64:: MIN as u64 ,
830
+ ast:: TyI8 => i8:: MIN as u64 ,
831
+ ast:: TyI16 => i16:: MIN as u64 ,
832
+ ast:: TyI32 => i32:: MIN as u64 ,
833
+ ast:: TyI64 => i64:: MIN as u64 ,
834
+ } ;
835
+ ( llty, min)
836
+ }
837
+ _ => unreachable ! ( ) ,
838
+ } ;
839
+ let minus_one = ICmp ( bcx, lib:: llvm:: IntEQ , rhs,
840
+ C_integral ( llty, -1 , false ) ) ;
841
+ with_cond ( bcx, minus_one, |bcx| {
842
+ let is_min = ICmp ( bcx, lib:: llvm:: IntEQ , lhs,
843
+ C_integral ( llty, min, true ) ) ;
844
+ with_cond ( bcx, is_min, |bcx| {
845
+ controlflow:: trans_fail ( bcx, span,
846
+ InternedString :: new ( overflow_text) )
847
+ } )
848
+ } )
849
+ } else {
850
+ bcx
851
+ }
809
852
}
810
853
811
854
pub fn trans_external_path ( ccx : & CrateContext , did : ast:: DefId , t : ty:: t ) -> ValueRef {
0 commit comments