Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl GccType for Reg {
match self.kind {
RegKind::Integer => cx.type_ix(self.size.bits()),
RegKind::Float => match self.size.bits() {
16 => cx.f16_abi_type,
32 => cx.type_f32(),
64 => cx.type_f64(),
_ => bug!("unsupported float: {:?}", self),
Expand Down
159 changes: 150 additions & 9 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,55 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
);
result.to_rvalue()
}

fn f16_to_float_ext(&self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
if !self.cx.is_f16_abi_storage_type(value.get_type()) {
return None;
}

self.cx.f16_to_float_libcall(value, dest_ty, self.location)
}

fn float_to_f16_trunc(&self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
if !self.cx.is_f16_abi_storage_type(dest_ty) {
return None;
}

self.cx.float_to_f16_libcall(value, dest_ty, self.location)
}

fn int_to_f16_trunc(
&self,
value: RValue<'gcc>,
dest_ty: Type<'gcc>,
signed: bool,
) -> Option<RValue<'gcc>> {
if !self.cx.is_f16_abi_storage_type(dest_ty) {
return None;
}

let value = if signed {
self.gcc_int_to_float_cast(value, self.cx.type_f32())
} else {
self.gcc_uint_to_float_cast(value, self.cx.type_f32())
};
self.float_to_f16_trunc(value, dest_ty)
}

fn f16_arithmetic_binary_op(
&mut self,
a: RValue<'gcc>,
b: RValue<'gcc>,
op: impl FnOnce(&mut Self, RValue<'gcc>, RValue<'gcc>) -> RValue<'gcc>,
) -> Option<RValue<'gcc>> {
let dest_ty = a.get_type();
let a = self.f16_to_float_ext(a, self.cx.type_f32())?;
let b = self
.f16_to_float_ext(b, self.cx.type_f32())
.expect("f16 binary operands should have the same type");
let result = op(self, a, b);
Some(self.float_to_f16_trunc(result, dest_ty).expect("f32 should truncate to f16"))
}
}

impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> {
Expand Down Expand Up @@ -675,6 +724,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
if let Some(value) =
self.f16_arithmetic_binary_op(a, b, |this, a, b| this.assign_to_var(a + b))
{
return value;
}
self.assign_to_var(a + b)
}

Expand All @@ -684,6 +738,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
if let Some(value) =
self.f16_arithmetic_binary_op(a, b, |this, a, b| this.assign_to_var(a - b))
{
return value;
}
self.assign_to_var(a - b)
}

Expand All @@ -692,6 +751,17 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.f16_arithmetic_binary_op(a, b, |this, a, b| {
this.assign_to_var(this.cx.context.new_binary_op(
this.location,
BinaryOp::Mult,
a.get_type(),
a,
b,
))
}) {
return value;
}
self.assign_to_var(self.cx.context.new_binary_op(
self.location,
BinaryOp::Mult,
Expand Down Expand Up @@ -728,6 +798,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
if let Some(value) =
self.f16_arithmetic_binary_op(a, b, |this, a, b| this.assign_to_var(a / b))
{
return value;
}
self.assign_to_var(a / b)
}

Expand All @@ -740,6 +815,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.f16_arithmetic_binary_op(a, b, |this, a, b| {
let fmodf = this.context.get_builtin_function("fmodf");
this.context.new_call(this.location, fmodf, &[a, b])
}) {
return value;
}
// FIXME(antoyo): add check in libgccjit since using the binary operator % causes the following error:
// during RTL pass: expand
// libgccjit.so: error: in expmed_mode_index, at expmed.h:240
Expand Down Expand Up @@ -775,14 +856,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {

#[cfg(feature = "master")]
match self.cx.type_kind(a_type) {
TypeKind::Half => {
let fmodf = self.context.get_builtin_function("fmodf");
let f32_type = self.type_f32();
let a = self.context.new_cast(self.location, a, f32_type);
let b = self.context.new_cast(self.location, b, f32_type);
let result = self.context.new_call(self.location, fmodf, &[a, b]);
return self.context.new_cast(self.location, result, a_type);
}
TypeKind::Float => {
let fmodf = self.context.get_builtin_function("fmodf");
return self.context.new_call(self.location, fmodf, &[a, b]);
Expand Down Expand Up @@ -862,6 +935,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
let dest_ty = a.get_type();
if self.cx.is_f16_abi_storage_type(dest_ty) {
return self.cx.f16_neg(a, dest_ty);
}
set_rvalue_location(
self,
self.cx.context.new_unary_op(self.location, UnaryOp::Minus, a.get_type(), a),
Expand All @@ -874,24 +951,44 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {

fn fadd_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, lhs, rhs| this.assign_to_var(lhs + rhs))
{
return value;
}
let result = set_rvalue_location(self, lhs + rhs);
self.assign_to_var(result)
}

fn fsub_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, lhs, rhs| this.assign_to_var(lhs - rhs))
{
return value;
}
let result = set_rvalue_location(self, lhs - rhs);
self.assign_to_var(result)
}

fn fmul_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, lhs, rhs| this.assign_to_var(lhs * rhs))
{
return value;
}
let result = set_rvalue_location(self, lhs * rhs);
self.assign_to_var(result)
}

fn fdiv_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, lhs, rhs| this.assign_to_var(lhs / rhs))
{
return value;
}
let result = set_rvalue_location(self, lhs / rhs);
self.assign_to_var(result)
}
Expand All @@ -905,21 +1002,41 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {

fn fadd_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, a, b| this.assign_to_var(a + b))
{
return value;
}
self.assign_to_var(lhs + rhs)
}

fn fsub_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, a, b| this.assign_to_var(a - b))
{
return value;
}
self.assign_to_var(lhs - rhs)
}

fn fmul_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, a, b| this.assign_to_var(a * b))
{
return value;
}
self.assign_to_var(lhs * rhs)
}

fn fdiv_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
if let Some(value) =
self.f16_arithmetic_binary_op(lhs, rhs, |this, a, b| this.assign_to_var(a / b))
{
return value;
}
self.assign_to_var(lhs / rhs)
}

Expand Down Expand Up @@ -1262,27 +1379,41 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}

fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
let value = self.f16_to_float_ext(value, self.cx.type_f32()).unwrap_or(value);
set_rvalue_location(self, self.gcc_float_to_uint_cast(value, dest_ty))
}

fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
let value = self.f16_to_float_ext(value, self.cx.type_f32()).unwrap_or(value);
set_rvalue_location(self, self.gcc_float_to_int_cast(value, dest_ty))
}

fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.int_to_f16_trunc(value, dest_ty, false) {
return set_rvalue_location(self, value);
}
set_rvalue_location(self, self.gcc_uint_to_float_cast(value, dest_ty))
}

fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.int_to_f16_trunc(value, dest_ty, true) {
return set_rvalue_location(self, value);
}
set_rvalue_location(self, self.gcc_int_to_float_cast(value, dest_ty))
}

fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.float_to_f16_trunc(value, dest_ty) {
return set_rvalue_location(self, value);
}
// FIXME(antoyo): make sure it truncates.
set_rvalue_location(self, self.context.new_cast(self.location, value, dest_ty))
}

fn fpext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
if let Some(value) = self.f16_to_float_ext(value, dest_ty) {
return set_rvalue_location(self, value);
}
set_rvalue_location(self, self.context.new_cast(self.location, value, dest_ty))
}

Expand Down Expand Up @@ -1348,6 +1479,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// LLVM has a concept of "unordered compares", where eg ULT returns true if either the two
// arguments are unordered (i.e. either is NaN), or the lhs is less than the rhs. GCC does
// not natively have this concept, so in some cases we must manually handle NaNs

let lhs = self.f16_to_float_ext(lhs, self.cx.type_f32()).unwrap_or(lhs);
let rhs = self.f16_to_float_ext(rhs, self.cx.type_f32()).unwrap_or(rhs);

let must_handle_nan = match op {
RealPredicate::RealPredicateFalse => unreachable!(),
RealPredicate::RealOEQ => false,
Expand Down Expand Up @@ -1828,6 +1963,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
if scalar.is_bool() {
return self.unchecked_utrunc(val, self.cx().type_i1());
}
if let abi::Primitive::Float(abi::Float::F16) = scalar.primitive() {
return self.cx.bitcast_if_needed(val, self.cx.f16_abi_type);
}
val
}

Expand All @@ -1844,9 +1982,12 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
fn fptoint_sat(
&mut self,
signed: bool,
val: RValue<'gcc>,
mut val: RValue<'gcc>,
dest_ty: Type<'gcc>,
) -> RValue<'gcc> {
if let Some(extended) = self.f16_to_float_ext(val, self.cx.type_f32()) {
val = extended;
}
let src_ty = self.cx.val_ty(val);
let (float_ty, int_ty) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
Expand Down
Loading
Loading