Interpreter: Implement floating point conversions (#4884)

* Interpreter: Implement floating point conversions

Implemented the following opcodes for the interpreter:
- `FcvtToUint`
- `FcvtToSint`
- `FcvtToUintSat`
- `FcvtToSintSat`
- `FcvtFromUint`
- `FcvtFromSint`
- `FcvtLowFromSint`
- `FvpromoteLow`
- `Fvdemote`

Copyright (c) 2022 Arm Limited

* Fix `I128` bounds checks for `FcvtTo{U,S}int{_,Sat}`

Copyright (c) 2022 Arm Limited

* Fix broken test

Copyright (c) 2022 Arm Limited
This commit is contained in:
Damian Heaton
2022-09-20 19:10:20 +01:00
committed by GitHub
parent 63c9e5d46d
commit cae7c196bb
6 changed files with 284 additions and 13 deletions

View File

@@ -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<V> {
// 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::<ValueResult<SimdVec<V>>>()?,
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<u64> {
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::<ValueResult<SimdVec<V>>>()?,
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::<ValueResult<SimdVec<V>>>()?),
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::<ValueResult<SimdVec<V>>>()?,
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::<ValueResult<SimdVec<V>>>()?;
// 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))?,