diff --git a/cranelift/filetests/filetests/runtests/conversion.clif b/cranelift/filetests/filetests/runtests/conversion.clif new file mode 100644 index 000000000000..011abcfc743c --- /dev/null +++ b/cranelift/filetests/filetests/runtests/conversion.clif @@ -0,0 +1,55 @@ +test interpret +test run +target aarch64 +target s390x +target x86_64 + +function %fcvt_to_sint(f32) -> i32 { +block0(v0: f32): + v1 = fcvt_to_sint.i32 v0 + return v1 +} +; run: %fcvt_to_sint(0x0.0) == 0 +; run: %fcvt_to_sint(0x1.0) == 1 +; run: %fcvt_to_sint(0x1.d6f346p26) == 123456792 +; run: %fcvt_to_sint(0x8.1) == 8 + +function %fcvt_to_uint(f32) -> i32 { +block0(v0:f32): + v1 = fcvt_to_uint.i32 v0 + return v1 +} +; run: %fcvt_to_uint(0x0.0) == 0 +; run: %fcvt_to_uint(0x1.0) == 1 +; run: %fcvt_to_uint(0x4.2) == 4 +; run: %fcvt_to_uint(0x4.6) == 4 +; run: %fcvt_to_uint(0x1.d6f346p26) == 123456792 +; run: %fcvt_to_uint(0xB2D05E00.0) == 3000000000 + +function %fcvt_to_sint_sat(f32) -> i32 { +block0(v0: f32): + v1 = fcvt_to_sint_sat.i32 v0 + return v1 +} +; run: %fcvt_to_sint_sat(0x0.0) == 0 +; run: %fcvt_to_sint_sat(0x1.0) == 1 +; run: %fcvt_to_sint_sat(0x1.d6f346p26) == 123456792 +; run: %fcvt_to_sint_sat(0x8.1) == 8 +; run: %fcvt_to_sint_sat(-0x1.0) == -1 +; run: %fcvt_to_sint_sat(0x1.fffffep127) == 2147483647 +; run: %fcvt_to_sint_sat(-0x1.fffffep127) == -2147483648 + +function %fcvt_to_uint_sat(f32) -> i32 { +block0(v0:f32): + v1 = fcvt_to_uint_sat.i32 v0 + return v1 +} +; run: %fcvt_to_uint_sat(0x0.0) == 0 +; run: %fcvt_to_uint_sat(0x1.0) == 1 +; run: %fcvt_to_uint_sat(0x4.2) == 4 +; run: %fcvt_to_uint_sat(0x4.6) == 4 +; run: %fcvt_to_uint_sat(0x1.d6f346p26) == 123456792 +; run: %fcvt_to_uint_sat(0xB2D05E00.0) == 3000000000 +; run: %fcvt_to_uint_sat(-0x1.0) == 0 +; run: %fcvt_to_uint_sat(0x1.fffffep127) == 4294967295 +; run: %fcvt_to_uint_sat(-0x1.fffffep127) == 0 diff --git a/cranelift/filetests/filetests/runtests/conversions.clif b/cranelift/filetests/filetests/runtests/conversions-load-store.clif similarity index 100% rename from cranelift/filetests/filetests/runtests/conversions.clif rename to cranelift/filetests/filetests/runtests/conversions-load-store.clif diff --git a/cranelift/filetests/filetests/runtests/i128-conversion.clif b/cranelift/filetests/filetests/runtests/i128-conversion.clif new file mode 100644 index 000000000000..16ba8c7520b8 --- /dev/null +++ b/cranelift/filetests/filetests/runtests/i128-conversion.clif @@ -0,0 +1,52 @@ +test interpret +; `fcvt_to_{u,s}int.i128` not currently supported by any backend. + +function %fcvt_to_uint_i128(f32) -> i128 { +block0(v0: f32): + v1 = fcvt_to_uint.i128 v0 + return v1 +} +; run: %fcvt_to_uint_i128(0x0.0) == 0 +; run: %fcvt_to_uint_i128(0x1.0) == 1 +; run: %fcvt_to_uint_i128(0x1.0p31) == 2147483648 +; run: %fcvt_to_uint_i128(0x1.fffffp31) == 4294965248 +; run: %fcvt_to_uint_i128(0x1.0p63) == 9223372036854775808 +; run: %fcvt_to_uint_i128(0x1.fffffep127) == 170141183460469231731687303715884105727 + +function %fcvt_to_sint_i128(f32) -> i128 { +block0(v0: f32): + v1 = fcvt_to_sint.i128 v0 + return v1 +} +; run: %fcvt_to_sint_i128(0x0.0) == 0 +; run: %fcvt_to_sint_i128(0x1.0) == 1 +; run: %fcvt_to_sint_i128(0x1.0p31) == 2147483648 +; run: %fcvt_to_sint_i128(0x1.fffffp31) == 4294965248 +; run: %fcvt_to_sint_i128(-0x1.fffffp31) == -4294965248 +; run: %fcvt_to_sint_i128(0x1.0p63) == 9223372036854775808 +; run: %fcvt_to_sint_i128(-0x1.0p63) == -9223372036854775808 +; run: %fcvt_to_sint_i128(0x1.fffffep127) == 170141183460469231731687303715884105727 + +function %fcvt_to_uint_sat_i128(f32) -> i128 { +block0(v0: f32): + v1 = fcvt_to_uint_sat.i128 v0 + return v1 +} +; run: %fcvt_to_uint_sat_i128(0x0.0) == 0 +; run: %fcvt_to_uint_sat_i128(0x1.0) == 1 +; run: %fcvt_to_uint_sat_i128(0x1.0p31) == 2147483648 +; run: %fcvt_to_uint_sat_i128(0x1.fffffp31) == 4294965248 +; run: %fcvt_to_uint_sat_i128(-0x1.fffffp31) == 0 +; run: %fcvt_to_uint_sat_i128(0x1.fffffep127) == 170141183460469231731687303715884105727 + +function %fcvt_to_sint_sat_i128(f32) -> i128 { +block0(v0: f32): + v1 = fcvt_to_sint_sat.i128 v0 + return v1 +} +; run: %fcvt_to_sint_sat_i128(0x0.0) == 0 +; run: %fcvt_to_sint_sat_i128(0x1.0) == 1 +; run: %fcvt_to_sint_sat_i128(0x1.0p31) == 2147483648 +; run: %fcvt_to_sint_sat_i128(0x1.fffffp31) == 4294965248 +; run: %fcvt_to_sint_sat_i128(-0x1.fffffp31) == -4294965248 +; run: %fcvt_to_sint_sat_i128(0x1.fffffep127) == 170141183460469231731687303715884105727 diff --git a/cranelift/filetests/filetests/runtests/simd-conversion.clif b/cranelift/filetests/filetests/runtests/simd-conversion.clif index 62cff744818e..6866b679f2f7 100644 --- a/cranelift/filetests/filetests/runtests/simd-conversion.clif +++ b/cranelift/filetests/filetests/runtests/simd-conversion.clif @@ -1,3 +1,4 @@ +test interpret test run target aarch64 target s390x @@ -47,3 +48,29 @@ block0(v0: i32x4): } ; run: %fcvt_low_from_sint([0 1 -1 65535]) == [0x0.0 0x1.0] ; run: %fcvt_low_from_sint([-1 123456789 0 1]) == [-0x1.0 0x1.d6f3454p26] + +function %fvdemote(f64x2) -> f32x4 { +block0(v0: f64x2): + v1 = fvdemote v0 + return v1 +} + +; run: %fvdemote([0x0.0 0x0.0]) == [0x0.0 0x0.0 0x0.0 0x0.0] +; run: %fvdemote([0x0.1 0x0.2]) == [0x0.1 0x0.2 0x0.0 0x0.0] +; run: %fvdemote([0x2.1 0x1.2]) == [0x2.1 0x1.2 0x0.0 0x0.0] +; run: %fvdemote([0x2.1 0x1.2]) == [0x2.1 0x1.2 0x0.0 0x0.0] +; run: %fvdemote([0x2.1 0x1.2]) == [0x2.1 0x1.2 0x0.0 0x0.0] + + +function %fvpromote_low(f32x4) -> f64x2 { +block0(v0: f32x4): + v1 = fvpromote_low v0 + return v1 +} + +; run: %fvpromote_low([0x0.0 0x0.0 0x0.0 0x0.0]) == [0x0.0 0x0.0] +; run: %fvpromote_low([0x0.1 0x0.2 0x0.0 0x0.0]) == [0x0.1 0x0.2] +; run: %fvpromote_low([0x2.1 0x1.2 0x0.0 0x0.0]) == [0x2.1 0x1.2] +; run: %fvpromote_low([0x0.0 0x0.0 0x2.1 0x1.2]) == [0x0.0 0x0.0] +; run: %fvpromote_low([0x0.0 0x0.0 0x2.1 0x1.2]) == [0x0.0 0x0.0] + diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index 280c2c91f78d..9b92328dde62 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -1119,15 +1119,137 @@ where }; assign(vectorizelanes(&new_vec, new_type)?) } - Opcode::FcvtToUint => unimplemented!("FcvtToUint"), - Opcode::FcvtToUintSat => unimplemented!("FcvtToUintSat"), - Opcode::FcvtToSint => unimplemented!("FcvtToSint"), - Opcode::FcvtToSintSat => unimplemented!("FcvtToSintSat"), - Opcode::FcvtFromUint => unimplemented!("FcvtFromUint"), - Opcode::FcvtFromSint => unimplemented!("FcvtFromSint"), - Opcode::FcvtLowFromSint => unimplemented!("FcvtLowFromSint"), - Opcode::FvpromoteLow => unimplemented!("FvpromoteLow"), - Opcode::Fvdemote => unimplemented!("Fvdemote"), + Opcode::FcvtToUint | Opcode::FcvtToSint => { + // NaN check + if arg(0)?.is_nan()? { + return Ok(ControlFlow::Trap(CraneliftTrap::User( + TrapCode::BadConversionToInteger, + ))); + } + let x = arg(0)?.into_float()? as i128; + let is_signed = inst.opcode() == Opcode::FcvtToSint; + let (min, max) = ctrl_ty.bounds(is_signed); + let overflow = if is_signed { + x < (min as i128) || x > (max as i128) + } else { + x < 0 || (x as u128) > (max as u128) + }; + // bounds check + if overflow { + return Ok(ControlFlow::Trap(CraneliftTrap::User( + TrapCode::IntegerOverflow, + ))); + } + // perform the conversion. + assign(Value::int(x, ctrl_ty)?) + } + Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => { + let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); + let cvt = |x: V| -> ValueResult { + // NaN check + if x.is_nan()? { + V::int(0, ctrl_ty.lane_type()) + } else { + let is_signed = inst.opcode() == Opcode::FcvtToSintSat; + let (min, max) = ctrl_ty.bounds(is_signed); + let x = x.into_float()? as i128; + let x = if is_signed { + let x = i128::max(x, min as i128); + let x = i128::min(x, max as i128); + x + } else { + let x = if x < 0 { 0 } else { x }; + let x = u128::min(x as u128, max as u128); + x as i128 + }; + V::int(x, ctrl_ty.lane_type()) + } + }; + + let x = extractlanes(&arg(0)?, in_ty)?; + + assign(vectorizelanes( + &x.into_iter() + .map(cvt) + .collect::>>()?, + ctrl_ty, + )?) + } + Opcode::FcvtFromUint | Opcode::FcvtFromSint => { + let x = extractlanes( + &arg(0)?, + inst_context.type_of(inst_context.args()[0]).unwrap(), + )?; + let bits = |x: V| -> ValueResult { + let x = if inst.opcode() == Opcode::FcvtFromUint { + x.convert(ValueConversionKind::ToUnsigned)? + } else { + x + }; + Ok(match ctrl_ty.lane_type() { + types::F32 => (x.into_int()? as f32).to_bits() as u64, + types::F64 => (x.into_int()? as f64).to_bits(), + _ => unimplemented!("unexpected conversion to {:?}", ctrl_ty.lane_type()), + }) + }; + assign(vectorizelanes( + &x.into_iter() + .map(|x| V::float(bits(x)?, ctrl_ty.lane_type())) + .collect::>>()?, + ctrl_ty, + )?) + } + Opcode::FcvtLowFromSint => { + let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); + let x = extractlanes(&arg(0)?, in_ty)?; + + assign(vectorizelanes( + &(x[..(ctrl_ty.lane_count() as usize)] + .into_iter() + .map(|x| { + V::float( + match ctrl_ty.lane_type() { + types::F32 => (x.to_owned().into_int()? as f32).to_bits() as u64, + types::F64 => (x.to_owned().into_int()? as f64).to_bits(), + _ => unimplemented!("unexpected promotion to {:?}", ctrl_ty), + }, + ctrl_ty.lane_type(), + ) + }) + .collect::>>()?), + ctrl_ty, + )?) + } + Opcode::FvpromoteLow => { + let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); + assert_eq!(in_ty, types::F32X4); + let out_ty = types::F64X2; + let x = extractlanes(&arg(0)?, in_ty)?; + assign(vectorizelanes( + &x[..(out_ty.lane_count() as usize)] + .into_iter() + .map(|x| { + V::convert(x.to_owned(), ValueConversionKind::Exact(out_ty.lane_type())) + }) + .collect::>>()?, + out_ty, + )?) + } + Opcode::Fvdemote => { + let in_ty = inst_context.type_of(inst_context.args()[0]).unwrap(); + assert_eq!(in_ty, types::F64X2); + let out_ty = types::F32X4; + let x = extractlanes(&arg(0)?, in_ty)?; + let x = &mut x + .into_iter() + .map(|x| V::convert(x, ValueConversionKind::RoundNearestEven(out_ty.lane_type()))) + .collect::>>()?; + // zero the high bits. + for _ in 0..(out_ty.lane_count() as usize - x.len()) { + x.push(V::float(0, out_ty.lane_type())?); + } + assign(vectorizelanes(x, out_ty)?) + } Opcode::Isplit => assign_multiple(&[ Value::convert(arg(0)?, ValueConversionKind::Truncate(types::I64))?, Value::convert(arg(0)?, ValueConversionKind::ExtractUpper(types::I64))?, diff --git a/cranelift/interpreter/src/value.rs b/cranelift/interpreter/src/value.rs index 204195944ca3..fefd308107e5 100644 --- a/cranelift/interpreter/src/value.rs +++ b/cranelift/interpreter/src/value.rs @@ -246,7 +246,11 @@ impl Value for DataValue { } fn into_float(self) -> ValueResult { - unimplemented!() + match self { + DataValue::F32(n) => Ok(n.as_f32() as f64), + DataValue::F64(n) => Ok(n.as_f64()), + _ => Err(ValueError::InvalidType(ValueTypeClass::Float, self.ty())), + } } fn is_float(&self) -> bool { @@ -307,8 +311,11 @@ impl Value for DataValue { (val, ty) if val.ty().is_int() && ty.is_int() => { DataValue::from_integer(val.into_int()?, ty)? } + (DataValue::I32(n), types::F32) => DataValue::F32(f32::from_bits(n as u32).into()), + (DataValue::I64(n), types::F64) => DataValue::F64(f64::from_bits(n as u64).into()), (DataValue::F32(n), types::I32) => DataValue::I32(n.bits() as i32), (DataValue::F64(n), types::I64) => DataValue::I64(n.bits() as i64), + (DataValue::F32(n), types::F64) => DataValue::F64((n.as_f32() as f64).into()), (DataValue::B(b), t) if t.is_bool() => DataValue::B(b), (DataValue::B(b), t) if t.is_int() => { // Bools are represented in memory as all 1's @@ -412,9 +419,17 @@ impl Value for DataValue { DataValue::U128(n) => DataValue::I128(n as i128), _ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind), }, - ValueConversionKind::RoundNearestEven(ty) => match (self.ty(), ty) { - (types::F64, types::F32) => unimplemented!(), - _ => unimplemented!("conversion: {} -> {:?}", self.ty(), kind), + ValueConversionKind::RoundNearestEven(ty) => match (self, ty) { + (DataValue::F64(n), types::F32) => { + let mut x = n.as_f64() as f32; + // Rust rounds away from zero, so if we've rounded up we + // should replace this with a proper rounding tied to even. + if (x as f64) != n.as_f64() { + x = n.round_ties_even().as_f64() as f32; + } + DataValue::F32(x.into()) + } + (s, _) => unimplemented!("conversion: {} -> {:?}", s.ty(), kind), }, ValueConversionKind::ToBoolean => match self.ty() { ty if ty.is_bool() => DataValue::B(self.into_bool()?),