Skip to content

Commit 38d48db

Browse files
committed
Add f16/f128 comparison support
1 parent 27a9590 commit 38d48db

File tree

4 files changed

+162
-5
lines changed

4 files changed

+162
-5
lines changed

src/codegen_f16_f128.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,42 @@ pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value
2828
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
2929
}
3030

31+
pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value {
32+
let ty = fx.bcx.func.dfg.value_type(lhs);
33+
match ty {
34+
types::F32 | types::F64 => fx.bcx.ins().fcmp(cc, lhs, rhs),
35+
types::F16 => {
36+
let lhs = f16_to_f32(fx, lhs);
37+
let rhs = f16_to_f32(fx, rhs);
38+
fx.bcx.ins().fcmp(cc, lhs, rhs)
39+
}
40+
types::F128 => {
41+
let (name, int_cc) = match cc {
42+
FloatCC::Equal => ("__eqtf2", IntCC::Equal),
43+
FloatCC::NotEqual => ("__netf2", IntCC::NotEqual),
44+
FloatCC::LessThan => ("__lttf2", IntCC::SignedLessThan),
45+
FloatCC::LessThanOrEqual => ("__letf2", IntCC::SignedLessThanOrEqual),
46+
FloatCC::GreaterThan => ("__gttf2", IntCC::SignedGreaterThan),
47+
FloatCC::GreaterThanOrEqual => ("__getf2", IntCC::SignedGreaterThanOrEqual),
48+
_ => unreachable!("not currently used in rustc_codegen_cranelift: {cc:?}"),
49+
};
50+
let res = fx.lib_call(
51+
name,
52+
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
53+
// FIXME(rust-lang/compiler-builtins#919): This should be `I64` on non-AArch64
54+
// architectures, but switching it before compiler-builtins is fixed causes test
55+
// failures.
56+
vec![AbiParam::new(types::I32)],
57+
&[lhs, rhs],
58+
)[0];
59+
let zero = fx.bcx.ins().iconst(types::I32, 0);
60+
let res = fx.bcx.ins().icmp(int_cc, res, zero);
61+
res
62+
}
63+
_ => unreachable!("{ty:?}"),
64+
}
65+
}
66+
3167
pub(crate) fn codegen_f128_binop(
3268
fx: &mut FunctionCx<'_, '_, '_>,
3369
bin_op: BinOp,
@@ -62,3 +98,21 @@ pub(crate) fn neg_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
6298
let bits = fx.bcx.ins().iconcat(low, high);
6399
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
64100
}
101+
102+
pub(crate) fn fmin_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
103+
fx.lib_call(
104+
"fminimumf128",
105+
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
106+
vec![AbiParam::new(types::F128)],
107+
&[a, b],
108+
)[0]
109+
}
110+
111+
pub(crate) fn fmax_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
112+
fx.lib_call(
113+
"fmaximumf128",
114+
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
115+
vec![AbiParam::new(types::F128)],
116+
&[a, b],
117+
)[0]
118+
}

src/compiler_builtins.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ builtin_functions! {
6767
fn fmodf(a: f32, b: f32) -> f32;
6868
fn fmod(a: f64, b: f64) -> f64;
6969
fn fmodf128(a: f128, b: f128) -> f128;
70+
// float comparison
71+
fn __eqtf2(a: f128, b: f128) -> i32;
72+
fn __netf2(a: f128, b: f128) -> i32;
73+
fn __lttf2(a: f128, b: f128) -> i32;
74+
fn __letf2(a: f128, b: f128) -> i32;
75+
fn __gttf2(a: f128, b: f128) -> i32;
76+
fn __getf2(a: f128, b: f128) -> i32;
77+
fn fminimumf128(a: f128, b: f128) -> f128;
78+
fn fmaximumf128(a: f128, b: f128) -> f128;
7079
// Cranelift float libcalls
7180
fn fmaf(a: f32, b: f32, c: f32) -> f32;
7281
fn fma(a: f64, b: f64, c: f64) -> f64;

src/intrinsics/mod.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use rustc_span::{Symbol, sym};
2727

2828
pub(crate) use self::llvm::codegen_llvm_intrinsic_call;
2929
use crate::cast::clif_intcast;
30+
use crate::codegen_f16_f128;
3031
use crate::prelude::*;
3132

3233
fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! {
@@ -1118,6 +1119,20 @@ fn codegen_regular_intrinsic_call<'tcx>(
11181119
ret.write_cvalue(fx, old);
11191120
}
11201121

1122+
sym::minimumf16 => {
1123+
intrinsic_args!(fx, args => (a, b); intrinsic);
1124+
let a = a.load_scalar(fx);
1125+
let b = b.load_scalar(fx);
1126+
1127+
// FIXME(bytecodealliance/wasmtime#8312): Use `fmin` directly once
1128+
// Cranelift backend lowerings are implemented.
1129+
let a = codegen_f16_f128::f16_to_f32(fx, a);
1130+
let b = codegen_f16_f128::f16_to_f32(fx, b);
1131+
let val = fx.bcx.ins().fmin(a, b);
1132+
let val = codegen_f16_f128::f32_to_f16(fx, val);
1133+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
1134+
ret.write_cvalue(fx, val);
1135+
}
11211136
sym::minimumf32 => {
11221137
intrinsic_args!(fx, args => (a, b); intrinsic);
11231138
let a = a.load_scalar(fx);
@@ -1136,6 +1151,31 @@ fn codegen_regular_intrinsic_call<'tcx>(
11361151
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
11371152
ret.write_cvalue(fx, val);
11381153
}
1154+
sym::minimumf128 => {
1155+
intrinsic_args!(fx, args => (a, b); intrinsic);
1156+
let a = a.load_scalar(fx);
1157+
let b = b.load_scalar(fx);
1158+
1159+
// FIXME(bytecodealliance/wasmtime#8312): Use `fmin` once Cranelift
1160+
// backend lowerings are implemented.
1161+
let val = codegen_f16_f128::fmin_f128(fx, a, b);
1162+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128));
1163+
ret.write_cvalue(fx, val);
1164+
}
1165+
sym::maximumf16 => {
1166+
intrinsic_args!(fx, args => (a, b); intrinsic);
1167+
let a = a.load_scalar(fx);
1168+
let b = b.load_scalar(fx);
1169+
1170+
// FIXME(bytecodealliance/wasmtime#8312): Use `fmax` directly once
1171+
// Cranelift backend lowerings are implemented.
1172+
let a = codegen_f16_f128::f16_to_f32(fx, a);
1173+
let b = codegen_f16_f128::f16_to_f32(fx, b);
1174+
let val = fx.bcx.ins().fmax(a, b);
1175+
let val = codegen_f16_f128::f32_to_f16(fx, val);
1176+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
1177+
ret.write_cvalue(fx, val);
1178+
}
11391179
sym::maximumf32 => {
11401180
intrinsic_args!(fx, args => (a, b); intrinsic);
11411181
let a = a.load_scalar(fx);
@@ -1154,7 +1194,27 @@ fn codegen_regular_intrinsic_call<'tcx>(
11541194
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
11551195
ret.write_cvalue(fx, val);
11561196
}
1197+
sym::maximumf128 => {
1198+
intrinsic_args!(fx, args => (a, b); intrinsic);
1199+
let a = a.load_scalar(fx);
1200+
let b = b.load_scalar(fx);
1201+
1202+
// FIXME(bytecodealliance/wasmtime#8312): Use `fmax` once Cranelift
1203+
// backend lowerings are implemented.
1204+
let val = codegen_f16_f128::fmax_f128(fx, a, b);
1205+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128));
1206+
ret.write_cvalue(fx, val);
1207+
}
1208+
1209+
sym::minnumf16 => {
1210+
intrinsic_args!(fx, args => (a, b); intrinsic);
1211+
let a = a.load_scalar(fx);
1212+
let b = b.load_scalar(fx);
11571213

1214+
let val = crate::num::codegen_float_min(fx, a, b);
1215+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
1216+
ret.write_cvalue(fx, val);
1217+
}
11581218
sym::minnumf32 => {
11591219
intrinsic_args!(fx, args => (a, b); intrinsic);
11601220
let a = a.load_scalar(fx);
@@ -1173,6 +1233,24 @@ fn codegen_regular_intrinsic_call<'tcx>(
11731233
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
11741234
ret.write_cvalue(fx, val);
11751235
}
1236+
sym::minnumf128 => {
1237+
intrinsic_args!(fx, args => (a, b); intrinsic);
1238+
let a = a.load_scalar(fx);
1239+
let b = b.load_scalar(fx);
1240+
1241+
let val = crate::num::codegen_float_min(fx, a, b);
1242+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128));
1243+
ret.write_cvalue(fx, val);
1244+
}
1245+
sym::maxnumf16 => {
1246+
intrinsic_args!(fx, args => (a, b); intrinsic);
1247+
let a = a.load_scalar(fx);
1248+
let b = b.load_scalar(fx);
1249+
1250+
let val = crate::num::codegen_float_max(fx, a, b);
1251+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
1252+
ret.write_cvalue(fx, val);
1253+
}
11761254
sym::maxnumf32 => {
11771255
intrinsic_args!(fx, args => (a, b); intrinsic);
11781256
let a = a.load_scalar(fx);
@@ -1191,6 +1269,15 @@ fn codegen_regular_intrinsic_call<'tcx>(
11911269
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
11921270
ret.write_cvalue(fx, val);
11931271
}
1272+
sym::maxnumf128 => {
1273+
intrinsic_args!(fx, args => (a, b); intrinsic);
1274+
let a = a.load_scalar(fx);
1275+
let b = b.load_scalar(fx);
1276+
1277+
let val = crate::num::codegen_float_max(fx, a, b);
1278+
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128));
1279+
ret.write_cvalue(fx, val);
1280+
}
11941281

11951282
sym::catch_unwind => {
11961283
intrinsic_args!(fx, args => (f, data, catch_fn); intrinsic);

src/num.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,10 @@ pub(crate) fn codegen_float_binop<'tcx>(
416416
BinOp::Gt => FloatCC::GreaterThan,
417417
_ => unreachable!(),
418418
};
419-
let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
419+
// FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift
420+
// `fcmp` once `f16`/`f128` backend lowerings have been added to
421+
// Cranelift.
422+
let val = codegen_f16_f128::fcmp(fx, fltcc, lhs, rhs);
420423
return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
421424
}
422425
_ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
@@ -500,15 +503,19 @@ fn codegen_ptr_binop<'tcx>(
500503
// and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
501504
// a float against itself. Only in case of NaN is it not equal to itself.
502505
pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
503-
let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
504-
let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
506+
// FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift `fcmp` once
507+
// `f16`/`f128` backend lowerings have been added to Cranelift.
508+
let a_is_nan = codegen_f16_f128::fcmp(fx, FloatCC::NotEqual, a, a);
509+
let a_ge_b = codegen_f16_f128::fcmp(fx, FloatCC::GreaterThanOrEqual, a, b);
505510
let temp = fx.bcx.ins().select(a_ge_b, b, a);
506511
fx.bcx.ins().select(a_is_nan, b, temp)
507512
}
508513

509514
pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
510-
let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
511-
let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
515+
// FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift `fcmp` once
516+
// `f16`/`f128` backend lowerings have been added to Cranelift.
517+
let a_is_nan = codegen_f16_f128::fcmp(fx, FloatCC::NotEqual, a, a);
518+
let a_le_b = codegen_f16_f128::fcmp(fx, FloatCC::LessThanOrEqual, a, b);
512519
let temp = fx.bcx.ins().select(a_le_b, b, a);
513520
fx.bcx.ins().select(a_is_nan, b, temp)
514521
}

0 commit comments

Comments
 (0)