Skip to content

Commit d95f341

Browse files
committed
Add integer to f16 conversions
These are not present in LLVM's `compiler-rt` but LLVM does emit them in some cases [1]. [1]: rust-lang/rust#132614 (comment)
1 parent aa9def7 commit d95f341

File tree

5 files changed

+159
-27
lines changed

5 files changed

+159
-27
lines changed

builtins-test/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ no-asm = ["compiler_builtins/no-asm"]
3535
mem = ["compiler_builtins/mem"]
3636
mangled-names = ["compiler_builtins/mangled-names"]
3737
# Skip tests that rely on f128 symbols being available on the system
38-
no-sys-f128 = ["no-sys-f128-int-convert", "no-sys-f16-f128-convert"]
38+
no-sys-f128 = ["no-sys-f16-int-convert", "no-sys-f128-int-convert", "no-sys-f16-f128-convert"]
3939
# Some platforms have some f128 functions but everything except integer conversions
4040
no-sys-f128-int-convert = []
41+
no-sys-f16-int-convert = []
4142
no-sys-f16-f128-convert = []
4243
no-sys-f16-f64-convert = []
4344
# Skip tests that rely on f16 symbols being available on the system

builtins-test/build.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum Feature {
1111
NoSysF128IntConvert,
1212
NoSysF16,
1313
NoSysF16F64Convert,
14+
NoSysF16IntConvert,
1415
NoSysF16F128Convert,
1516
}
1617

@@ -19,7 +20,13 @@ impl Feature {
1920
match self {
2021
Self::NoSysF128 => [Self::NoSysF128IntConvert, Self::NoSysF16F128Convert].as_slice(),
2122
Self::NoSysF128IntConvert => [].as_slice(),
22-
Self::NoSysF16 => [Self::NoSysF16F64Convert, Self::NoSysF16F128Convert].as_slice(),
23+
Self::NoSysF16 => [
24+
Self::NoSysF16IntConvert,
25+
Self::NoSysF16F64Convert,
26+
Self::NoSysF16F128Convert,
27+
]
28+
.as_slice(),
29+
Self::NoSysF16IntConvert => [].as_slice(),
2330
Self::NoSysF16F64Convert => [].as_slice(),
2431
Self::NoSysF16F128Convert => [].as_slice(),
2532
}
@@ -101,6 +108,10 @@ fn main() {
101108
"no-sys-f128-int-convert",
102109
"using apfloat fallback for f128 <-> int conversions",
103110
),
111+
Feature::NoSysF16IntConvert => (
112+
"no-sys-f16-int-convert",
113+
"using apfloat fallback for f16 <-> int conversions",
114+
),
104115
Feature::NoSysF16F64Convert => (
105116
"no-sys-f16-f64-convert",
106117
"using apfloat fallback for f16 <-> f64 conversions",

builtins-test/tests/conv.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ mod i_to_f {
3434
FloatTy::from_u128(x.try_into().unwrap()).value
3535
};
3636

37-
<$f_ty>::from_bits(apf.to_bits())
37+
<$f_ty>::from_bits(apf.to_bits().try_into().unwrap())
3838
},
3939
x
4040
);
@@ -96,6 +96,16 @@ mod i_to_f {
9696
};
9797
}
9898

99+
#[cfg(f16_enabled)]
100+
i_to_f! { f16, Half, not(feature = "no-sys-f16-int-convert"),
101+
u32, __floatunsihf;
102+
i32, __floatsihf;
103+
u64, __floatundihf;
104+
i64, __floatdihf;
105+
u128, __floatuntihf;
106+
i128, __floattihf;
107+
}
108+
99109
i_to_f! { f32, Single, all(),
100110
u32, __floatunsisf;
101111
i32, __floatsisf;

compiler-builtins/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,17 @@ of being added to Rust.
159159
- [x] fixunstfdi.c
160160
- [x] fixunstfsi.c
161161
- [x] fixunstfti.c
162+
- [x] floatdihf.c (not yet in `compiler-rt` but emitted by LLVM)
162163
- [x] floatditf.c
164+
- [x] floatsihf.c (not yet in `compiler-rt` but emitted by LLVM)
163165
- [x] floatsitf.c
166+
- [x] floattihf.c (not yet in `compiler-rt` but emitted by LLVM)
164167
- [x] floattitf.c
168+
- [x] floatundihf.c (not yet in `compiler-rt` but emitted by LLVM)
165169
- [x] floatunditf.c
170+
- [x] floatunsihf.c (not yet in `compiler-rt` but emitted by LLVM)
166171
- [x] floatunsitf.c
172+
- [x] floatuntihf.c (not yet in `compiler-rt` but emitted by LLVM)
167173
- [x] floatuntitf.c
168174
- [x] multf3.c
169175
- [x] powitf2.c

compiler-builtins/src/float/conv.rs

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ mod int_to_float {
7878
F::from_bits(conv(i.unsigned_abs()) | sign_bit)
7979
}
8080

81+
#[cfg(f16_enabled)]
82+
pub fn u32_to_f16_bits(i: u32) -> u16 {
83+
let n = i.leading_zeros();
84+
let i_m = i.wrapping_shl(n);
85+
// Mantissa with implicit bit set
86+
let m_base: u16 = (i_m >> shift_f_lt_i::<u32, f16>()) as u16;
87+
// The entire lower half of `i` will be truncated (masked portion), plus the
88+
// next `EXP_BITS` bits.
89+
let adj = (i_m >> f16::EXP_BITS | i_m & 0xFF) as u16;
90+
let m = m_adj::<f16>(m_base, adj);
91+
let e = if i == 0 { 0 } else { exp::<u32, f16>(n) - 1 };
92+
// Any int can have an exponent out of range for `f16`, unlike other float types.
93+
// Clamp this.
94+
if e >= f16::EXP_SAT as u16 - 1 {
95+
f16::INFINITY.to_bits()
96+
} else {
97+
repr::<f16>(e, m)
98+
}
99+
}
100+
81101
pub fn u32_to_f32_bits(i: u32) -> u32 {
82102
if i == 0 {
83103
return 0;
@@ -121,6 +141,33 @@ mod int_to_float {
121141
(h as u128) << 64
122142
}
123143

144+
#[cfg(f16_enabled)]
145+
pub fn u64_to_f16_bits(i: u64) -> u16 {
146+
let n = i.leading_zeros();
147+
let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero
148+
let m_base: u16 = (i_m >> shift_f_lt_i::<u64, f16>()) as u16;
149+
150+
// Within the upper `F::BITS`, everything except for the signifcand
151+
// gets truncated
152+
let d1: u16 = (i_m >> (u64::BITS - f16::BITS - f16::SIG_BITS - 1)).cast();
153+
154+
// The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just
155+
// check if it is nonzero.
156+
let d2: u16 = (i_m << f16::BITS >> f16::BITS != 0).into();
157+
let adj = d1 | d2;
158+
159+
// Mantissa with implicit bit set
160+
let m = m_adj::<f16>(m_base, adj);
161+
let e = if i == 0 { 0 } else { exp::<u64, f16>(n) - 1 };
162+
163+
// Clamp to infinity if the exponent is out of range
164+
if e >= f16::EXP_SAT as u16 - 1 {
165+
f16::INFINITY.to_bits()
166+
} else {
167+
repr::<f16>(e, m)
168+
}
169+
}
170+
124171
pub fn u64_to_f32_bits(i: u64) -> u32 {
125172
let n = i.leading_zeros();
126173
let i_m = i.wrapping_shl(n);
@@ -159,6 +206,33 @@ mod int_to_float {
159206
repr::<f128>(e, m)
160207
}
161208

209+
#[cfg(f16_enabled)]
210+
pub fn u128_to_f16_bits(i: u128) -> u16 {
211+
let n = i.leading_zeros();
212+
let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero
213+
let m_base: u16 = (i_m >> shift_f_lt_i::<u128, f16>()) as u16;
214+
215+
// Within the upper `F::BITS`, everything except for the signifcand
216+
// gets truncated
217+
let d1: u16 = (i_m >> (u128::BITS - f16::BITS - f16::SIG_BITS - 1)).cast();
218+
219+
// The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just
220+
// check if it is nonzero.
221+
let d2: u16 = (i_m << f16::BITS >> f16::BITS != 0).into();
222+
let adj = d1 | d2;
223+
224+
// Mantissa with implicit bit set
225+
let m = m_adj::<f16>(m_base, adj);
226+
let e = if i == 0 { 0 } else { exp::<u128, f16>(n) - 1 };
227+
228+
// Clamp to infinity if the exponent is out of range
229+
if e >= f16::EXP_SAT as u16 - 1 {
230+
f16::INFINITY.to_bits()
231+
} else {
232+
repr::<f16>(e, m)
233+
}
234+
}
235+
162236
pub fn u128_to_f32_bits(i: u128) -> u32 {
163237
let n = i.leading_zeros();
164238
let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero
@@ -209,6 +283,11 @@ mod int_to_float {
209283

210284
// Conversions from unsigned integers to floats.
211285
intrinsics! {
286+
#[cfg(f16_enabled)]
287+
pub extern "C" fn __floatunsihf(i: u32) -> f16 {
288+
f16::from_bits(int_to_float::u32_to_f16_bits(i))
289+
}
290+
212291
#[arm_aeabi_alias = __aeabi_ui2f]
213292
pub extern "C" fn __floatunsisf(i: u32) -> f32 {
214293
f32::from_bits(int_to_float::u32_to_f32_bits(i))
@@ -219,6 +298,17 @@ intrinsics! {
219298
f64::from_bits(int_to_float::u32_to_f64_bits(i))
220299
}
221300

301+
#[ppc_alias = __floatunsikf]
302+
#[cfg(f128_enabled)]
303+
pub extern "C" fn __floatunsitf(i: u32) -> f128 {
304+
f128::from_bits(int_to_float::u32_to_f128_bits(i))
305+
}
306+
307+
#[cfg(f16_enabled)]
308+
pub extern "C" fn __floatundihf(i: u64) -> f16 {
309+
f16::from_bits(int_to_float::u64_to_f16_bits(i))
310+
}
311+
222312
#[arm_aeabi_alias = __aeabi_ul2f]
223313
pub extern "C" fn __floatundisf(i: u64) -> f32 {
224314
f32::from_bits(int_to_float::u64_to_f32_bits(i))
@@ -229,6 +319,17 @@ intrinsics! {
229319
f64::from_bits(int_to_float::u64_to_f64_bits(i))
230320
}
231321

322+
#[ppc_alias = __floatundikf]
323+
#[cfg(f128_enabled)]
324+
pub extern "C" fn __floatunditf(i: u64) -> f128 {
325+
f128::from_bits(int_to_float::u64_to_f128_bits(i))
326+
}
327+
328+
#[cfg(f16_enabled)]
329+
pub extern "C" fn __floatuntihf(i: u128) -> f16 {
330+
f16::from_bits(int_to_float::u128_to_f16_bits(i))
331+
}
332+
232333
#[cfg_attr(target_os = "uefi", unadjusted_on_win64)]
233334
pub extern "C" fn __floatuntisf(i: u128) -> f32 {
234335
f32::from_bits(int_to_float::u128_to_f32_bits(i))
@@ -239,18 +340,6 @@ intrinsics! {
239340
f64::from_bits(int_to_float::u128_to_f64_bits(i))
240341
}
241342

242-
#[ppc_alias = __floatunsikf]
243-
#[cfg(f128_enabled)]
244-
pub extern "C" fn __floatunsitf(i: u32) -> f128 {
245-
f128::from_bits(int_to_float::u32_to_f128_bits(i))
246-
}
247-
248-
#[ppc_alias = __floatundikf]
249-
#[cfg(f128_enabled)]
250-
pub extern "C" fn __floatunditf(i: u64) -> f128 {
251-
f128::from_bits(int_to_float::u64_to_f128_bits(i))
252-
}
253-
254343
#[ppc_alias = __floatuntikf]
255344
#[cfg(f128_enabled)]
256345
pub extern "C" fn __floatuntitf(i: u128) -> f128 {
@@ -260,6 +349,11 @@ intrinsics! {
260349

261350
// Conversions from signed integers to floats.
262351
intrinsics! {
352+
#[cfg(f16_enabled)]
353+
pub extern "C" fn __floatsihf(i: i32) -> f16 {
354+
int_to_float::signed(i, int_to_float::u32_to_f16_bits)
355+
}
356+
263357
#[arm_aeabi_alias = __aeabi_i2f]
264358
pub extern "C" fn __floatsisf(i: i32) -> f32 {
265359
int_to_float::signed(i, int_to_float::u32_to_f32_bits)
@@ -270,6 +364,17 @@ intrinsics! {
270364
int_to_float::signed(i, int_to_float::u32_to_f64_bits)
271365
}
272366

367+
#[ppc_alias = __floatsikf]
368+
#[cfg(f128_enabled)]
369+
pub extern "C" fn __floatsitf(i: i32) -> f128 {
370+
int_to_float::signed(i, int_to_float::u32_to_f128_bits)
371+
}
372+
373+
#[cfg(f16_enabled)]
374+
pub extern "C" fn __floatdihf(i: i64) -> f16 {
375+
int_to_float::signed(i, int_to_float::u64_to_f16_bits)
376+
}
377+
273378
#[arm_aeabi_alias = __aeabi_l2f]
274379
pub extern "C" fn __floatdisf(i: i64) -> f32 {
275380
int_to_float::signed(i, int_to_float::u64_to_f32_bits)
@@ -280,6 +385,17 @@ intrinsics! {
280385
int_to_float::signed(i, int_to_float::u64_to_f64_bits)
281386
}
282387

388+
#[ppc_alias = __floatdikf]
389+
#[cfg(f128_enabled)]
390+
pub extern "C" fn __floatditf(i: i64) -> f128 {
391+
int_to_float::signed(i, int_to_float::u64_to_f128_bits)
392+
}
393+
394+
#[cfg(f16_enabled)]
395+
pub extern "C" fn __floattihf(i: i128) -> f16 {
396+
int_to_float::signed(i, int_to_float::u128_to_f16_bits)
397+
}
398+
283399
#[cfg_attr(target_os = "uefi", unadjusted_on_win64)]
284400
pub extern "C" fn __floattisf(i: i128) -> f32 {
285401
int_to_float::signed(i, int_to_float::u128_to_f32_bits)
@@ -290,18 +406,6 @@ intrinsics! {
290406
int_to_float::signed(i, int_to_float::u128_to_f64_bits)
291407
}
292408

293-
#[ppc_alias = __floatsikf]
294-
#[cfg(f128_enabled)]
295-
pub extern "C" fn __floatsitf(i: i32) -> f128 {
296-
int_to_float::signed(i, int_to_float::u32_to_f128_bits)
297-
}
298-
299-
#[ppc_alias = __floatdikf]
300-
#[cfg(f128_enabled)]
301-
pub extern "C" fn __floatditf(i: i64) -> f128 {
302-
int_to_float::signed(i, int_to_float::u64_to_f128_bits)
303-
}
304-
305409
#[ppc_alias = __floattikf]
306410
#[cfg(f128_enabled)]
307411
pub extern "C" fn __floattitf(i: i128) -> f128 {

0 commit comments

Comments
 (0)