2025-05-23 14:51:51 +00:00
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let (value, arg_ty) =
|
|
|
|
if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
|
|
|
(
|
|
|
|
fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value),
|
|
|
|
lib_call_arg_param(fx.tcx, types::I16, false),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(value, AbiParam::new(types::F16))
|
|
|
|
};
|
|
|
|
fx.lib_call("__extendhfsf2", vec![arg_ty], vec![AbiParam::new(types::F32)], &[value])[0]
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let ret = f16_to_f32(fx, value);
|
|
|
|
fx.bcx.ins().fpromote(types::F64, ret)
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
|
|
|
types::I16
|
|
|
|
} else {
|
|
|
|
types::F16
|
|
|
|
};
|
|
|
|
let ret = fx.lib_call(
|
|
|
|
"__truncsfhf2",
|
|
|
|
vec![AbiParam::new(types::F32)],
|
|
|
|
vec![AbiParam::new(ret_ty)],
|
|
|
|
&[value],
|
|
|
|
)[0];
|
|
|
|
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
|
|
|
types::I16
|
|
|
|
} else {
|
|
|
|
types::F16
|
|
|
|
};
|
|
|
|
let ret = fx.lib_call(
|
|
|
|
"__truncdfhf2",
|
|
|
|
vec![AbiParam::new(types::F64)],
|
|
|
|
vec![AbiParam::new(ret_ty)],
|
|
|
|
&[value],
|
|
|
|
)[0];
|
|
|
|
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value {
|
|
|
|
let ty = fx.bcx.func.dfg.value_type(lhs);
|
|
|
|
match ty {
|
|
|
|
types::F32 | types::F64 => fx.bcx.ins().fcmp(cc, lhs, rhs),
|
|
|
|
types::F16 => {
|
|
|
|
let lhs = f16_to_f32(fx, lhs);
|
|
|
|
let rhs = f16_to_f32(fx, rhs);
|
|
|
|
fx.bcx.ins().fcmp(cc, lhs, rhs)
|
|
|
|
}
|
|
|
|
types::F128 => {
|
|
|
|
let (name, int_cc) = match cc {
|
|
|
|
FloatCC::Equal => ("__eqtf2", IntCC::Equal),
|
|
|
|
FloatCC::NotEqual => ("__netf2", IntCC::NotEqual),
|
|
|
|
FloatCC::LessThan => ("__lttf2", IntCC::SignedLessThan),
|
|
|
|
FloatCC::LessThanOrEqual => ("__letf2", IntCC::SignedLessThanOrEqual),
|
|
|
|
FloatCC::GreaterThan => ("__gttf2", IntCC::SignedGreaterThan),
|
|
|
|
FloatCC::GreaterThanOrEqual => ("__getf2", IntCC::SignedGreaterThanOrEqual),
|
|
|
|
_ => unreachable!("not currently used in rustc_codegen_cranelift: {cc:?}"),
|
|
|
|
};
|
|
|
|
let res = fx.lib_call(
|
|
|
|
name,
|
|
|
|
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
|
|
|
// FIXME(rust-lang/compiler-builtins#919): This should be `I64` on non-AArch64
|
|
|
|
// architectures, but switching it before compiler-builtins is fixed causes test
|
|
|
|
// failures.
|
|
|
|
vec![AbiParam::new(types::I32)],
|
|
|
|
&[lhs, rhs],
|
|
|
|
)[0];
|
|
|
|
let zero = fx.bcx.ins().iconst(types::I32, 0);
|
|
|
|
let res = fx.bcx.ins().icmp(int_cc, res, zero);
|
|
|
|
res
|
|
|
|
}
|
|
|
|
_ => unreachable!("{ty:?}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn codegen_f128_binop(
|
|
|
|
fx: &mut FunctionCx<'_, '_, '_>,
|
|
|
|
bin_op: BinOp,
|
|
|
|
lhs: Value,
|
|
|
|
rhs: Value,
|
|
|
|
) -> Value {
|
|
|
|
let name = match bin_op {
|
|
|
|
BinOp::Add => "__addtf3",
|
|
|
|
BinOp::Sub => "__subtf3",
|
|
|
|
BinOp::Mul => "__multf3",
|
|
|
|
BinOp::Div => "__divtf3",
|
|
|
|
_ => unreachable!("handled in `codegen_float_binop`"),
|
|
|
|
};
|
|
|
|
fx.lib_call(
|
|
|
|
name,
|
|
|
|
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
|
|
|
vec![AbiParam::new(types::F128)],
|
|
|
|
&[lhs, rhs],
|
|
|
|
)[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn neg_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value);
|
|
|
|
let bits = fx.bcx.ins().bxor_imm(bits, 0x8000);
|
|
|
|
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn neg_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value);
|
|
|
|
let (low, high) = fx.bcx.ins().isplit(bits);
|
|
|
|
let high = fx.bcx.ins().bxor_imm(high, 0x8000_0000_0000_0000_u64 as i64);
|
|
|
|
let bits = fx.bcx.ins().iconcat(low, high);
|
|
|
|
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
|
|
|
|
}
|
2025-05-23 14:51:51 +00:00
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn abs_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value);
|
|
|
|
let bits = fx.bcx.ins().band_imm(bits, 0x7fff);
|
|
|
|
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn abs_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
|
|
|
let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value);
|
|
|
|
let (low, high) = fx.bcx.ins().isplit(bits);
|
|
|
|
let high = fx.bcx.ins().band_imm(high, 0x7fff_ffff_ffff_ffff_u64 as i64);
|
|
|
|
let bits = fx.bcx.ins().iconcat(low, high);
|
|
|
|
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn copysign_f16(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value {
|
|
|
|
let lhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), lhs);
|
|
|
|
let rhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), rhs);
|
|
|
|
let res = fx.bcx.ins().band_imm(lhs, 0x7fff);
|
|
|
|
let sign = fx.bcx.ins().band_imm(rhs, 0x8000);
|
|
|
|
let res = fx.bcx.ins().bor(res, sign);
|
|
|
|
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), res)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn copysign_f128(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value {
|
|
|
|
let lhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), lhs);
|
|
|
|
let rhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), rhs);
|
|
|
|
let (low, lhs_high) = fx.bcx.ins().isplit(lhs);
|
|
|
|
let (_, rhs_high) = fx.bcx.ins().isplit(rhs);
|
|
|
|
let high = fx.bcx.ins().band_imm(lhs_high, 0x7fff_ffff_ffff_ffff_u64 as i64);
|
|
|
|
let sign = fx.bcx.ins().band_imm(rhs_high, 0x8000_0000_0000_0000_u64 as i64);
|
|
|
|
let high = fx.bcx.ins().bor(high, sign);
|
|
|
|
let res = fx.bcx.ins().iconcat(low, high);
|
|
|
|
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), res)
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn codegen_cast(
|
|
|
|
fx: &mut FunctionCx<'_, '_, '_>,
|
|
|
|
from: Value,
|
|
|
|
from_signed: bool,
|
|
|
|
to_ty: Type,
|
|
|
|
to_signed: bool,
|
|
|
|
) -> Value {
|
|
|
|
let from_ty = fx.bcx.func.dfg.value_type(from);
|
|
|
|
if from_ty.is_float() && to_ty.is_float() {
|
|
|
|
let name = match (from_ty, to_ty) {
|
|
|
|
(types::F16, types::F32) => return f16_to_f32(fx, from),
|
|
|
|
(types::F16, types::F64) => return f16_to_f64(fx, from),
|
|
|
|
(types::F16, types::F128) => "__extendhftf2",
|
|
|
|
(types::F32, types::F128) => "__extendsftf2",
|
|
|
|
(types::F64, types::F128) => "__extenddftf2",
|
|
|
|
(types::F128, types::F64) => "__trunctfdf2",
|
|
|
|
(types::F128, types::F32) => "__trunctfsf2",
|
|
|
|
(types::F128, types::F16) => "__trunctfhf2",
|
|
|
|
(types::F64, types::F16) => return f64_to_f16(fx, from),
|
|
|
|
(types::F32, types::F16) => return f32_to_f16(fx, from),
|
|
|
|
_ => unreachable!("{from_ty:?} -> {to_ty:?}"),
|
|
|
|
};
|
|
|
|
fx.lib_call(name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])[0]
|
|
|
|
} else if from_ty.is_int() && to_ty == types::F16 {
|
|
|
|
let res = clif_int_or_float_cast(fx, from, from_signed, types::F32, false);
|
|
|
|
f32_to_f16(fx, res)
|
|
|
|
} else if from_ty == types::F16 && to_ty.is_int() {
|
|
|
|
let from = f16_to_f32(fx, from);
|
|
|
|
clif_int_or_float_cast(fx, from, false, to_ty, to_signed)
|
|
|
|
} else if from_ty.is_int() && to_ty == types::F128 {
|
|
|
|
let (from, from_ty) = if from_ty.bits() < 32 {
|
|
|
|
(clif_int_or_float_cast(fx, from, from_signed, types::I32, from_signed), types::I32)
|
|
|
|
} else {
|
|
|
|
(from, from_ty)
|
|
|
|
};
|
|
|
|
let name = format!(
|
|
|
|
"__float{sign}{size}itf",
|
|
|
|
sign = if from_signed { "" } else { "un" },
|
|
|
|
size = match from_ty {
|
|
|
|
types::I32 => 's',
|
|
|
|
types::I64 => 'd',
|
|
|
|
types::I128 => 't',
|
|
|
|
_ => unreachable!("{from_ty:?}"),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
fx.lib_call(
|
|
|
|
&name,
|
|
|
|
vec![lib_call_arg_param(fx.tcx, from_ty, from_signed)],
|
|
|
|
vec![AbiParam::new(to_ty)],
|
|
|
|
&[from],
|
|
|
|
)[0]
|
|
|
|
} else if from_ty == types::F128 && to_ty.is_int() {
|
|
|
|
let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty };
|
|
|
|
let name = format!(
|
|
|
|
"__fix{sign}tf{size}i",
|
|
|
|
sign = if from_signed { "" } else { "un" },
|
|
|
|
size = match ret_ty {
|
|
|
|
types::I32 => 's',
|
|
|
|
types::I64 => 'd',
|
|
|
|
types::I128 => 't',
|
|
|
|
_ => unreachable!("{from_ty:?}"),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let ret =
|
|
|
|
fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])
|
|
|
|
[0];
|
|
|
|
let val = if ret_ty == to_ty {
|
|
|
|
ret
|
|
|
|
} else {
|
|
|
|
let (min, max) = match (to_ty, to_signed) {
|
|
|
|
(types::I8, false) => (0, i64::from(u8::MAX)),
|
|
|
|
(types::I16, false) => (0, i64::from(u16::MAX)),
|
|
|
|
(types::I8, true) => (i64::from(i8::MIN as u32), i64::from(i8::MAX as u32)),
|
|
|
|
(types::I16, true) => (i64::from(i16::MIN as u32), i64::from(i16::MAX as u32)),
|
|
|
|
_ => unreachable!("{to_ty:?}"),
|
|
|
|
};
|
|
|
|
let min_val = fx.bcx.ins().iconst(types::I32, min);
|
|
|
|
let max_val = fx.bcx.ins().iconst(types::I32, max);
|
|
|
|
|
|
|
|
let val = if to_signed {
|
|
|
|
let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, ret, min);
|
|
|
|
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, ret, max);
|
|
|
|
let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, ret);
|
|
|
|
fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
|
|
|
|
} else {
|
|
|
|
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, ret, max);
|
|
|
|
fx.bcx.ins().select(has_overflow, max_val, ret)
|
|
|
|
};
|
|
|
|
fx.bcx.ins().ireduce(to_ty, val)
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
let is_not_nan = fcmp(fx, FloatCC::Equal, from, from);
|
|
|
|
let zero = type_zero_value(&mut fx.bcx, to_ty);
|
|
|
|
fx.bcx.ins().select(is_not_nan, val, zero)
|
|
|
|
} else {
|
|
|
|
unreachable!("{from_ty:?} -> {to_ty:?}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn fma_f16(fx: &mut FunctionCx<'_, '_, '_>, x: Value, y: Value, z: Value) -> Value {
|
|
|
|
let x = f16_to_f64(fx, x);
|
|
|
|
let y = f16_to_f64(fx, y);
|
|
|
|
let z = f16_to_f64(fx, z);
|
|
|
|
let res = fx.bcx.ins().fma(x, y, z);
|
|
|
|
f64_to_f16(fx, res)
|
|
|
|
}
|
|
|
|
|
2025-05-23 14:51:51 +00:00
|
|
|
pub(crate) fn fmin_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
|
|
|
|
fx.lib_call(
|
|
|
|
"fminimumf128",
|
|
|
|
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
|
|
|
vec![AbiParam::new(types::F128)],
|
|
|
|
&[a, b],
|
|
|
|
)[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn fmax_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
|
|
|
|
fx.lib_call(
|
|
|
|
"fmaximumf128",
|
|
|
|
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
|
|
|
vec![AbiParam::new(types::F128)],
|
|
|
|
&[a, b],
|
|
|
|
)[0]
|
|
|
|
}
|