@@ -805,13 +805,42 @@ impl<'ll, 'tcx, 'a> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
805
805
}
806
806
807
807
fn fptoui_sat ( & mut self , val : & ' ll Value , dest_ty : & ' ll Type ) -> & ' ll Value {
808
- // NVVM does not have support for saturated conversion. Setting rustc flag
809
- // `-Z saturating_float_casts=false` falls back to non-saturated, UB-prone
810
- // conversion, and should prevent this codegen. Otherwise, fall back to UB
811
- // prone conversion.
812
- self . cx ( ) . sess ( ) . dcx ( )
813
- . warn ( "Saturated float to int conversion is not supported on NVVM. Defaulting to UB prone conversion." ) ;
814
- self . fptoui ( val, dest_ty)
808
+ // NVVM does not support saturating casts, however, they are relatively simple to implement.
809
+ // (at least for unsigned ints). So, we emulate them here.
810
+
811
+ // In order to clamp the value, we need to know it's type.
812
+ let val_ty = self . val_ty ( val) ;
813
+ // Find the min / max intrinsics
814
+ let ( min, max) = match self . cx ( ) . float_width ( val_ty) {
815
+ 64 => ( "__nv_fmin" , "__nv_fmax" ) ,
816
+ 32 => ( "__nv_fminf" , "__nv_fmaxf" ) ,
817
+ _ => {
818
+ self . cx ( ) . sess ( ) . dcx ( )
819
+ . warn ( "Saturated float to int conversion is not supported in NVVM for type {val_ty:?}. Defaulting to UB prone conversion." ) ;
820
+ return self . fptoui ( val, dest_ty) ;
821
+ }
822
+ } ;
823
+ let ( max_ty, max) = self . cx ( ) . get_intrinsic ( max) ;
824
+ let ( min_ty, min) = self . cx ( ) . get_intrinsic ( min) ;
825
+ // Find the zero value, and the max value of a given int.
826
+ let zero = self . const_real ( val_ty, 0.0 ) ;
827
+ let max_value = match self . int_width ( dest_ty) {
828
+ 8 => u8:: MAX as f64 ,
829
+ 16 => u16:: MAX as f64 ,
830
+ 32 => u32:: MAX as f64 ,
831
+ 64 => u64:: MAX as f64 ,
832
+ 128 => u128:: MAX as f64 ,
833
+ _ => todo ! ( "Unsupported int type {dest_ty:?}" ) ,
834
+ } ;
835
+ let max_value = self . const_real ( val_ty, max_value) ;
836
+ // Compute max(val, 0). This will clamp negative values to zero **AND**
837
+ // replace NaNs with 0s(just like how Rust is specified to behave)
838
+ let res = self . call ( max_ty, None , None , max, & [ val, zero] , None , None ) ;
839
+ // Clamp all values higher than max to max
840
+ let res = self . call ( min_ty, None , None , min, & [ res, max_value] , None , None ) ;
841
+ // Now, we know that `res` is non-nan, and in range (min, max). So, it is well-defined
842
+ // for all inputs :D!
843
+ self . fptoui ( res, dest_ty)
815
844
}
816
845
817
846
fn fptosi_sat ( & mut self , val : & ' ll Value , dest_ty : & ' ll Type ) -> & ' ll Value {
0 commit comments