Skip to content

Commit 9e83090

Browse files
committed
Add support for casting to and from f16/f128
1 parent c10a6e1 commit 9e83090

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

src/cast.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Various number casting functions
22
3+
use crate::codegen_f16_f128;
34
use crate::prelude::*;
45

56
pub(crate) fn clif_intcast(
@@ -36,6 +37,14 @@ pub(crate) fn clif_int_or_float_cast(
3637
) -> Value {
3738
let from_ty = fx.bcx.func.dfg.value_type(from);
3839

40+
// FIXME(bytecodealliance/wasmtime#8312): Remove in favour of native
41+
// Cranelift operations once Cranelift backends have lowerings for them.
42+
if matches!(from_ty, types::F16 | types::F128)
43+
|| matches!(to_ty, types::F16 | types::F128) && from_ty != to_ty
44+
{
45+
return codegen_f16_f128::codegen_cast(fx, from, from_signed, to_ty, to_signed);
46+
}
47+
3948
if from_ty.is_int() && to_ty.is_int() {
4049
// int-like -> int-like
4150
clif_intcast(
@@ -58,8 +67,10 @@ pub(crate) fn clif_int_or_float_cast(
5867
"__float{sign}ti{flt}f",
5968
sign = if from_signed { "" } else { "un" },
6069
flt = match to_ty {
70+
types::F16 => "h",
6171
types::F32 => "s",
6272
types::F64 => "d",
73+
types::F128 => "t",
6374
_ => unreachable!("{:?}", to_ty),
6475
},
6576
);
@@ -90,8 +101,10 @@ pub(crate) fn clif_int_or_float_cast(
90101
"__fix{sign}{flt}fti",
91102
sign = if to_signed { "" } else { "uns" },
92103
flt = match from_ty {
104+
types::F16 => "h",
93105
types::F32 => "s",
94106
types::F64 => "d",
107+
types::F128 => "t",
95108
_ => unreachable!("{:?}", to_ty),
96109
},
97110
);
@@ -145,8 +158,12 @@ pub(crate) fn clif_int_or_float_cast(
145158
} else if from_ty.is_float() && to_ty.is_float() {
146159
// float -> float
147160
match (from_ty, to_ty) {
148-
(types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
149-
(types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
161+
(types::F16, types::F32 | types::F64 | types::F128)
162+
| (types::F32, types::F64 | types::F128)
163+
| (types::F64, types::F128) => fx.bcx.ins().fpromote(to_ty, from),
164+
(types::F128, types::F64 | types::F32 | types::F16)
165+
| (types::F64, types::F32 | types::F16)
166+
| (types::F32, types::F16) => fx.bcx.ins().fdemote(to_ty, from),
150167
_ => from,
151168
}
152169
} else {

src/codegen_f16_f128.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value
1313
fx.lib_call("__extendhfsf2", vec![arg_ty], vec![AbiParam::new(types::F32)], &[value])[0]
1414
}
1515

16+
fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
17+
let ret = f16_to_f32(fx, value);
18+
fx.bcx.ins().fpromote(types::F64, ret)
19+
}
20+
1621
pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
1722
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
1823
types::I16
@@ -28,6 +33,21 @@ pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value
2833
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
2934
}
3035

36+
fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
37+
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
38+
types::I16
39+
} else {
40+
types::F16
41+
};
42+
let ret = fx.lib_call(
43+
"__truncdfhf2",
44+
vec![AbiParam::new(types::F64)],
45+
vec![AbiParam::new(ret_ty)],
46+
&[value],
47+
)[0];
48+
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
49+
}
50+
3151
pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value {
3252
let ty = fx.bcx.func.dfg.value_type(lhs);
3353
match ty {
@@ -97,6 +117,109 @@ pub(crate) fn neg_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
97117
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
98118
}
99119

120+
pub(crate) fn codegen_cast(
121+
fx: &mut FunctionCx<'_, '_, '_>,
122+
from: Value,
123+
from_signed: bool,
124+
to_ty: Type,
125+
to_signed: bool,
126+
) -> Value {
127+
let from_ty = fx.bcx.func.dfg.value_type(from);
128+
if from_ty.is_float() && to_ty.is_float() {
129+
let name = match (from_ty, to_ty) {
130+
(types::F16, types::F32) => return f16_to_f32(fx, from),
131+
(types::F16, types::F64) => return f16_to_f64(fx, from),
132+
(types::F16, types::F128) => "__extendhftf2",
133+
(types::F32, types::F128) => "__extendsftf2",
134+
(types::F64, types::F128) => "__extenddftf2",
135+
(types::F128, types::F64) => "__trunctfdf2",
136+
(types::F128, types::F32) => "__trunctfsf2",
137+
(types::F128, types::F16) => "__trunctfhf2",
138+
(types::F64, types::F16) => return f64_to_f16(fx, from),
139+
(types::F32, types::F16) => return f32_to_f16(fx, from),
140+
_ => unreachable!("{from_ty:?} -> {to_ty:?}"),
141+
};
142+
fx.lib_call(name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])[0]
143+
} else if from_ty.is_int() && to_ty == types::F16 {
144+
let res = clif_int_or_float_cast(fx, from, from_signed, types::F32, false);
145+
f32_to_f16(fx, res)
146+
} else if from_ty == types::F16 && to_ty.is_int() {
147+
let from = f16_to_f32(fx, from);
148+
clif_int_or_float_cast(fx, from, false, to_ty, to_signed)
149+
} else if from_ty.is_int() && to_ty == types::F128 {
150+
let (from, from_ty) = if from_ty.bits() < 32 {
151+
(clif_int_or_float_cast(fx, from, from_signed, types::I32, from_signed), types::I32)
152+
} else {
153+
(from, from_ty)
154+
};
155+
let name = format!(
156+
"__float{sign}{size}itf",
157+
sign = if from_signed { "" } else { "un" },
158+
size = match from_ty {
159+
types::I32 => 's',
160+
types::I64 => 'd',
161+
types::I128 => 't',
162+
_ => unreachable!("{from_ty:?}"),
163+
},
164+
);
165+
fx.lib_call(
166+
&name,
167+
vec![lib_call_arg_param(fx.tcx, from_ty, from_signed)],
168+
vec![AbiParam::new(to_ty)],
169+
&[from],
170+
)[0]
171+
} else if from_ty == types::F128 && to_ty.is_int() {
172+
let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty };
173+
let name = format!(
174+
"__fix{sign}tf{size}i",
175+
sign = if from_signed { "" } else { "un" },
176+
size = match ret_ty {
177+
types::I32 => 's',
178+
types::I64 => 'd',
179+
types::I128 => 't',
180+
_ => unreachable!("{from_ty:?}"),
181+
},
182+
);
183+
let ret =
184+
fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])
185+
[0];
186+
let val = if ret_ty == to_ty {
187+
ret
188+
} else {
189+
let (min, max) = match (to_ty, to_signed) {
190+
(types::I8, false) => (0, i64::from(u8::MAX)),
191+
(types::I16, false) => (0, i64::from(u16::MAX)),
192+
(types::I8, true) => (i64::from(i8::MIN as u32), i64::from(i8::MAX as u32)),
193+
(types::I16, true) => (i64::from(i16::MIN as u32), i64::from(i16::MAX as u32)),
194+
_ => unreachable!("{to_ty:?}"),
195+
};
196+
let min_val = fx.bcx.ins().iconst(types::I32, min);
197+
let max_val = fx.bcx.ins().iconst(types::I32, max);
198+
199+
let val = if to_signed {
200+
let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, ret, min);
201+
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, ret, max);
202+
let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, ret);
203+
fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
204+
} else {
205+
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, ret, max);
206+
fx.bcx.ins().select(has_overflow, max_val, ret)
207+
};
208+
fx.bcx.ins().ireduce(to_ty, val)
209+
};
210+
211+
if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts {
212+
return val;
213+
}
214+
215+
let is_not_nan = fcmp(fx, FloatCC::Equal, from, from);
216+
let zero = type_zero_value(&mut fx.bcx, to_ty);
217+
fx.bcx.ins().select(is_not_nan, val, zero)
218+
} else {
219+
unreachable!("{from_ty:?} -> {to_ty:?}");
220+
}
221+
}
222+
100223
pub(crate) fn fmin_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
101224
fx.lib_call(
102225
"fminimumf128",

0 commit comments

Comments
 (0)