|  | 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] | 
|  | } | 
|  |  | 
|  | fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { | 
|  | let ret = f16_to_f32(fx, value); | 
|  | fx.bcx.ins().fpromote(types::F64, ret) | 
|  | } | 
|  |  | 
|  | 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 } | 
|  | } | 
|  |  | 
|  | 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 } | 
|  | } | 
|  |  | 
|  | 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:?}"), | 
|  | } | 
|  | } | 
|  |  | 
|  | 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) | 
|  | } | 
|  |  | 
|  | 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) | 
|  | } | 
|  |  | 
|  | 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:?}"); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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) | 
|  | } | 
|  |  | 
|  | 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] | 
|  | } |