Implement fptoint_sat for f16 and f128

This commit is contained in:
Antoni Boucher 2026-02-13 08:12:59 -05:00
parent c2b8abd810
commit 458adbd66a
2 changed files with 23 additions and 14 deletions

View file

@ -1873,32 +1873,33 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
// we're rounding towards zero, we just get float_ty::MAX (which is always an integer). // we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
// This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
let int_max = |signed: bool, int_width: u64| -> u128 { fn int_max(signed: bool, int_width: u64) -> u128 {
let shift_amount = 128 - int_width; let shift_amount = 128 - int_width;
if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
}; }
let int_min = |signed: bool, int_width: u64| -> i128 { fn int_min(signed: bool, int_width: u64) -> i128 {
if signed { i128::MIN >> (128 - int_width) } else { 0 } if signed { i128::MIN >> (128 - int_width) } else { 0 }
}; }
let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { // TODO: rewrite using a generic function with <F: Float>.
let compute_clamp_bounds_half = |signed: bool, int_width: u64| -> (u128, u128) {
let rounded_min = let rounded_min =
ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); ieee::Half::from_i128_r(int_min(signed, int_width), Round::TowardZero);
assert_eq!(rounded_min.status, Status::OK); //assert_eq!(rounded_min.status, Status::OK);
let rounded_max = let rounded_max =
ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); ieee::Half::from_u128_r(int_max(signed, int_width), Round::TowardZero);
assert!(rounded_max.value.is_finite()); assert!(rounded_max.value.is_finite());
(rounded_min.value.to_bits(), rounded_max.value.to_bits()) (rounded_min.value.to_bits(), rounded_max.value.to_bits())
}; };
let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { fn compute_clamp_bounds<F: Float>(signed: bool, int_width: u64) -> (u128, u128) {
let rounded_min = let rounded_min =
ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); F::from_i128_r(int_min(signed, int_width), Round::TowardZero);
assert_eq!(rounded_min.status, Status::OK); assert_eq!(rounded_min.status, Status::OK);
let rounded_max = let rounded_max =
ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); F::from_u128_r(int_max(signed, int_width), Round::TowardZero);
assert!(rounded_max.value.is_finite()); assert!(rounded_max.value.is_finite());
(rounded_min.value.to_bits(), rounded_max.value.to_bits()) (rounded_min.value.to_bits(), rounded_max.value.to_bits())
}; }
// To implement saturation, we perform the following steps: // To implement saturation, we perform the following steps:
// //
// 1. Cast val to an integer with fpto[su]i. This may result in undef. // 1. Cast val to an integer with fpto[su]i. This may result in undef.
@ -1928,15 +1929,19 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let float_bits_to_llval = |bx: &mut Self, bits| { let float_bits_to_llval = |bx: &mut Self, bits| {
let bits_llval = match float_width { let bits_llval = match float_width {
16 => bx.cx().const_u16(bits as u16),
32 => bx.cx().const_u32(bits as u32), 32 => bx.cx().const_u32(bits as u32),
64 => bx.cx().const_u64(bits as u64), 64 => bx.cx().const_u64(bits as u64),
128 => bx.cx().const_u128(bits),
n => bug!("unsupported float width {}", n), n => bug!("unsupported float width {}", n),
}; };
bx.bitcast(bits_llval, float_ty) bx.bitcast(bits_llval, float_ty)
}; };
let (f_min, f_max) = match float_width { let (f_min, f_max) = match float_width {
32 => compute_clamp_bounds_single(signed, int_width), 16 => compute_clamp_bounds_half(signed, int_width),
64 => compute_clamp_bounds_double(signed, int_width), 32 => compute_clamp_bounds::<ieee::Single>(signed, int_width),
64 => compute_clamp_bounds::<ieee::Double>(signed, int_width),
128 => compute_clamp_bounds::<ieee::Quad>(signed, int_width),
n => bug!("unsupported float width {}", n), n => bug!("unsupported float width {}", n),
}; };
let f_min = float_bits_to_llval(self, f_min); let f_min = float_bits_to_llval(self, f_min);

View file

@ -20,6 +20,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
bytes_in_context(self, bytes) bytes_in_context(self, bytes)
} }
pub fn const_u16(&self, i: u16) -> RValue<'gcc> {
self.const_uint(self.type_u16(), i as u64)
}
fn global_string(&self, string: &str) -> LValue<'gcc> { fn global_string(&self, string: &str) -> LValue<'gcc> {
// TODO(antoyo): handle non-null-terminated strings. // TODO(antoyo): handle non-null-terminated strings.
let string = self.context.new_string_literal(string); let string = self.context.new_string_literal(string);