]> git.lizzy.rs Git - rust.git/blobdiff - src/cast.rs
Implement more simd_reduce_* intrinsics
[rust.git] / src / cast.rs
index 39aa2c96d4d36003a75b1c90065afc0b70b09a2b..57204de1135be435c524b2fbbb3ce69d608da6ff 100644 (file)
@@ -1,7 +1,9 @@
+//! Various number casting functions
+
 use crate::prelude::*;
 
-pub fn clif_intcast<'a, 'tcx: 'a>(
-    fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
+pub(crate) fn clif_intcast(
+    fx: &mut FunctionCx<'_, '_, impl Module>,
     val: Value,
     to: Type,
     signed: bool,
@@ -13,15 +15,19 @@ pub fn clif_intcast<'a, 'tcx: 'a>(
 
         // extend
         (_, types::I128) => {
-            let wider = if from == types::I64 {
+            let lo = if from == types::I64 {
                 val
             } else if signed {
                 fx.bcx.ins().sextend(types::I64, val)
             } else {
                 fx.bcx.ins().uextend(types::I64, val)
             };
-            let zero = fx.bcx.ins().iconst(types::I64, 0);
-            fx.bcx.ins().iconcat(wider, zero)
+            let hi = if signed {
+                fx.bcx.ins().sshr_imm(lo, 63)
+            } else {
+                fx.bcx.ins().iconst(types::I64, 0)
+            };
+            fx.bcx.ins().iconcat(lo, hi)
         }
         (_, _) if to.wider_or_equal(from) => {
             if signed {
@@ -40,14 +46,12 @@ pub fn clif_intcast<'a, 'tcx: 'a>(
                 fx.bcx.ins().ireduce(to, lsb)
             }
         }
-        (_, _) => {
-            fx.bcx.ins().ireduce(to, val)
-        }
+        (_, _) => fx.bcx.ins().ireduce(to, val),
     }
 }
 
-pub fn clif_int_or_float_cast(
-    fx: &mut FunctionCx<'_, '_, impl Backend>,
+pub(crate) fn clif_int_or_float_cast(
+    fx: &mut FunctionCx<'_, '_, impl Module>,
     from: Value,
     from_signed: bool,
     to_ty: Type,
@@ -61,9 +65,49 @@ pub fn clif_int_or_float_cast(
             fx,
             from,
             to_ty,
-            from_signed, // FIXME is this correct?
+            // This is correct as either from_signed == to_signed (=> this is trivially correct)
+            // Or from_clif_ty == to_clif_ty, which means this is a no-op.
+            from_signed,
         )
     } else if from_ty.is_int() && to_ty.is_float() {
+        if from_ty == types::I128 {
+            // _______ss__f_
+            // __float  tisf: i128 -> f32
+            // __float  tidf: i128 -> f64
+            // __floatuntisf: u128 -> f32
+            // __floatuntidf: u128 -> f64
+
+            let name = format!(
+                "__float{sign}ti{flt}f",
+                sign = if from_signed { "" } else { "un" },
+                flt = match to_ty {
+                    types::F32 => "s",
+                    types::F64 => "d",
+                    _ => unreachable!("{:?}", to_ty),
+                },
+            );
+
+            let from_rust_ty = if from_signed {
+                fx.tcx.types.i128
+            } else {
+                fx.tcx.types.u128
+            };
+
+            let to_rust_ty = match to_ty {
+                types::F32 => fx.tcx.types.f32,
+                types::F64 => fx.tcx.types.f64,
+                _ => unreachable!(),
+            };
+
+            return fx
+                .easy_call(
+                    &name,
+                    &[CValue::by_val(from, fx.layout_of(from_rust_ty))],
+                    to_rust_ty,
+                )
+                .load_scalar(fx);
+        }
+
         // int-like -> float
         if from_signed {
             fx.bcx.ins().fcvt_from_sint(to_ty, from)
@@ -71,56 +115,82 @@ pub fn clif_int_or_float_cast(
             fx.bcx.ins().fcvt_from_uint(to_ty, from)
         }
     } else if from_ty.is_float() && to_ty.is_int() {
+        if to_ty == types::I128 {
+            // _____sssf___
+            // __fix   sfti: f32 -> i128
+            // __fix   dfti: f64 -> i128
+            // __fixunssfti: f32 -> u128
+            // __fixunsdfti: f64 -> u128
+
+            let name = format!(
+                "__fix{sign}{flt}fti",
+                sign = if to_signed { "" } else { "uns" },
+                flt = match from_ty {
+                    types::F32 => "s",
+                    types::F64 => "d",
+                    _ => unreachable!("{:?}", to_ty),
+                },
+            );
+
+            let from_rust_ty = match from_ty {
+                types::F32 => fx.tcx.types.f32,
+                types::F64 => fx.tcx.types.f64,
+                _ => unreachable!(),
+            };
+
+            let to_rust_ty = if to_signed {
+                fx.tcx.types.i128
+            } else {
+                fx.tcx.types.u128
+            };
+
+            return fx
+                .easy_call(
+                    &name,
+                    &[CValue::by_val(from, fx.layout_of(from_rust_ty))],
+                    to_rust_ty,
+                )
+                .load_scalar(fx);
+        }
+
         // float -> int-like
         if to_ty == types::I8 || to_ty == types::I16 {
-            // FIXME implement fcbt_to_*int_sat.i8/i16
+            // FIXME implement fcvt_to_*int_sat.i8/i16
             let val = if to_signed {
                 fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
             } else {
                 fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
             };
-            let (min, max) = type_min_max_value(to_ty, to_signed);
+            let (min, max) = match (to_ty, to_signed) {
+                (types::I8, false) => (0, i64::from(u8::MAX)),
+                (types::I16, false) => (0, i64::from(u16::MAX)),
+                (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
+                (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
+                _ => unreachable!(),
+            };
             let min_val = fx.bcx.ins().iconst(types::I32, min);
             let max_val = fx.bcx.ins().iconst(types::I32, max);
 
             let val = if to_signed {
-                let has_underflow = fx.bcx.ins().icmp_imm(
-                    IntCC::SignedLessThan,
-                    val,
-                    min,
-                );
-                let has_overflow = fx.bcx.ins().icmp_imm(
-                    IntCC::SignedGreaterThan,
-                    val,
-                    max,
-                );
+                let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min);
+                let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max);
                 let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
                 fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
             } else {
-                let has_overflow = fx.bcx.ins().icmp_imm(
-                    IntCC::UnsignedGreaterThan,
-                    val,
-                    max,
-                );
+                let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max);
                 fx.bcx.ins().select(has_overflow, max_val, val)
             };
             fx.bcx.ins().ireduce(to_ty, val)
+        } else if to_signed {
+            fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
         } else {
-            if to_signed {
-                fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
-            } else {
-                fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
-            }
+            fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
         }
     } else if from_ty.is_float() && to_ty.is_float() {
         // float -> float
         match (from_ty, to_ty) {
-            (types::F32, types::F64) => {
-                fx.bcx.ins().fpromote(types::F64, from)
-            }
-            (types::F64, types::F32) => {
-                fx.bcx.ins().fdemote(types::F32, from)
-            }
+            (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
+            (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
             _ => from,
         }
     } else {