diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index c01937bf6c..efa240be6e 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -2676,7 +2676,10 @@ impl CTypeKind { pub fn is_floating_type(&self) -> bool { use CTypeKind::*; - matches!(self, Float | Double | LongDouble | Half | BFloat16) + matches!( + self, + Float | Double | LongDouble | Float128 | Half | BFloat16 + ) } pub fn as_underlying_decl(&self) -> Option { @@ -2731,6 +2734,7 @@ impl CTypeKind { (ULongLong, ty) | (ty, ULongLong) if int(ty) => ULongLong, (LongDouble, ty) | (ty, LongDouble) if float(ty) || int(ty) => LongDouble, + (Float128, ty) | (ty, Float128) if float(ty) || int(ty) => Float128, (Int128, ty) | (ty, Int128) if int(ty) => Int128, (UInt128, ty) | (ty, UInt128) if int(ty) => UInt128, diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index fec0c861e4..02abf17888 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -75,15 +75,29 @@ impl<'c> Translation<'c> { "__builtin_huge_valf" => Ok(WithStmts::new_val( mk().abs_path_expr(vec!["core", "f32", "INFINITY"]), )), - "__builtin_huge_val" | "__builtin_huge_vall" => Ok(WithStmts::new_val( + "__builtin_huge_val" => Ok(WithStmts::new_val( mk().abs_path_expr(vec!["core", "f64", "INFINITY"]), )), + "__builtin_huge_vall" => { + self.use_crate(ExternCrate::F128); + + Ok(WithStmts::new_val( + mk().abs_path_expr(vec!["f128", "f128", "INFINITY"]), + )) + } "__builtin_inff" => Ok(WithStmts::new_val( mk().abs_path_expr(vec!["core", "f32", "INFINITY"]), )), - "__builtin_inf" | "__builtin_infl" => Ok(WithStmts::new_val( + "__builtin_inf" => Ok(WithStmts::new_val( mk().abs_path_expr(vec!["core", "f64", "INFINITY"]), )), + "__builtin_infl" => { + self.use_crate(ExternCrate::F128); + + Ok(WithStmts::new_val( + mk().abs_path_expr(vec!["f128", "f128", "INFINITY"]), + )) + } "__builtin_nanf" => Ok(WithStmts::new_val( mk().abs_path_expr(vec!["core", "f32", "NAN"]), )), @@ -98,15 +112,9 @@ impl<'c> Translation<'c> { )) } "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { - // Long doubles require the Float trait from num_traits to call this method - if builtin_name == "__builtin_signbitl" { - self.with_cur_file_item_store(|item_store| { - item_store.add_use(true, vec!["num_traits".into()], "Float"); - }); - } + self.import_num_traits(args[0])?; let val = self.convert_expr(ctx.used(), args[0], None)?; - Ok(val.map(|v| { let val = mk().method_call_expr(v, "is_sign_negative", vec![]); @@ -149,10 +157,14 @@ impl<'c> Translation<'c> { Ok(val.map(|x| mk().method_call_expr(x, "swap_bytes", vec![]))) } "__builtin_fabs" | "__builtin_fabsf" | "__builtin_fabsl" => { + self.import_num_traits(args[0])?; + let val = self.convert_expr(ctx.used(), args[0], None)?; Ok(val.map(|x| mk().method_call_expr(x, "abs", vec![]))) } "__builtin_isfinite" | "__builtin_isnan" => { + self.import_num_traits(args[0])?; + let val = self.convert_expr(ctx.used(), args[0], None)?; let seg = match builtin_name { @@ -166,6 +178,8 @@ impl<'c> Translation<'c> { })) } "__builtin_isinf_sign" => { + self.import_num_traits(args[0])?; + // isinf_sign(x) -> fabs(x) == infinity ? (signbit(x) ? -1 : 1) : 0 let val = self.convert_expr(ctx.used(), args[0], None)?; Ok(val.map(|x| { @@ -723,6 +737,26 @@ impl<'c> Translation<'c> { } } + fn import_num_traits(&self, arg_id: CExprId) -> TranslationResult<()> { + let arg_type_id = self.ast_context[arg_id] + .kind + .get_qual_type() + .ok_or_else(|| format_err!("bad arg type"))?; + let arg_type_kind = &self.ast_context.resolve_type(arg_type_id.ctype).kind; + + match arg_type_kind { + CTypeKind::LongDouble | CTypeKind::Float128 => { + self.use_crate(ExternCrate::NumTraits); + self.with_cur_file_item_store(|item_store| { + item_store.add_use(true, vec!["num_traits".into()], "Float"); + }); + } + _ => {} + } + + Ok(()) + } + // This translation logic handles converting code that uses // https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html fn convert_overflow_arith( diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 256b225de6..16b2daefba 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -84,7 +84,7 @@ impl<'c> Translation<'c> { c_str.to_owned() }; let val = match self.ast_context.resolve_type(ty.ctype).kind { - CTypeKind::LongDouble => { + CTypeKind::LongDouble | CTypeKind::Float128 => { if ctx.is_const { return Err(format_translation_err!( None, diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 13190694a3..7e969806fa 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1720,7 +1720,7 @@ impl<'c> Translation<'c> { // The f128 crate doesn't currently provide a way to const initialize // values, except for common mathematical constants - if let CTypeKind::LongDouble = self.ast_context[qtype.ctype].kind { + if let CTypeKind::LongDouble | CTypeKind::Float128 = self.ast_context[qtype.ctype].kind { return true; } @@ -3067,7 +3067,7 @@ impl<'c> Translation<'c> { // so type annotation is need for 0-init ints and floats at the moment, but // they could be simplified in favor of type suffixes Bool | Char | SChar | Short | Int | Long | LongLong | UChar | UShort | UInt | ULong - | ULongLong | LongDouble | Int128 | UInt128 => initializer.is_none(), + | ULongLong | LongDouble | Float128 | Int128 | UInt128 => initializer.is_none(), Float | Double => initializer.is_none(), Struct(_) | Union(_) | Enum(_) => false, Function(..) => unreachable!("Can't have a function directly as a type"), @@ -4479,20 +4479,30 @@ impl<'c> Translation<'c> { | CastKind::BooleanToSignedIntegral => { let target_ty = self.convert_type(target_cty.ctype)?; - if let CTypeKind::LongDouble = target_ty_kind { - if ctx.is_const { - return Err(format_translation_err!( - None, - "f128 cannot be used in constants because \ - `f128::f128::new` is not `const`", - )); - } + if let CTypeKind::LongDouble | CTypeKind::Float128 = target_ty_kind { + if let CTypeKind::LongDouble | CTypeKind::Float128 = + self.ast_context[source_cty.ctype].kind + { + // These are both converted to `f128`, so a cast between the two should + // just be a no-op. + Ok(val) + } else { + if ctx.is_const { + return Err(format_translation_err!( + None, + "f128 cannot be used in constants because \ + `f128::f128::new` is not `const`", + )); + } - self.use_crate(ExternCrate::F128); + self.use_crate(ExternCrate::F128); - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); - Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) - } else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind { + let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); + Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) + } + } else if let CTypeKind::LongDouble | CTypeKind::Float128 = + self.ast_context[source_cty.ctype].kind + { self.f128_cast_to(val, target_ty_kind) } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { // Casts targeting `enum` types... @@ -4631,7 +4641,7 @@ impl<'c> Translation<'c> { )) } else if resolved_ty.is_floating_type() { match self.ast_context[ty_id].kind { - CTypeKind::LongDouble => { + CTypeKind::LongDouble | CTypeKind::Float128 => { self.use_crate(ExternCrate::F128); Ok(WithStmts::new_val( mk().abs_path_expr(vec!["f128", "f128", "ZERO"]), @@ -4874,7 +4884,10 @@ impl<'c> Translation<'c> { }; // The backup is to just compare against zero - let zero = if ty.is_floating_type() { + let zero = if let CTypeKind::LongDouble | CTypeKind::Float128 = ty { + self.use_crate(ExternCrate::F128); + mk().abs_path_expr(vec!["f128", "f128", "ZERO"]) + } else if ty.is_floating_type() { mk().lit_expr(mk().float_unsuffixed_lit("0.")) } else { mk().lit_expr(mk().int_unsuffixed_lit(0)) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 17b4892340..d264925111 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -224,16 +224,17 @@ impl<'c> Translation<'c> { let lhs_type = self.convert_type(compute_lhs_type_id.ctype)?; // We can't simply as-cast into a non primitive like f128 - let lhs = if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble { - self.use_crate(ExternCrate::F128); + let lhs = + if let CTypeKind::LongDouble | CTypeKind::Float128 = compute_lhs_resolved_ty.kind { + self.use_crate(ExternCrate::F128); - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "from"]); - let args = vec![read]; + let fn_path = mk().abs_path_expr(vec!["f128", "f128", "from"]); + let args = vec![read]; - mk().call_expr(fn_path, args) - } else { - mk().cast_expr(read, lhs_type) - }; + mk().call_expr(fn_path, args) + } else { + mk().cast_expr(read, lhs_type) + }; let ty = self.convert_type(compute_res_type_id.ctype)?; let mut val = self.convert_binary_operator( ctx, @@ -253,7 +254,8 @@ impl<'c> Translation<'c> { val.result_map(|val| { self.convert_cast_to_enum(ctx, lhs_type_id.ctype, enum_id, None, val) })? - } else if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble { + } else if let CTypeKind::LongDouble | CTypeKind::Float128 = compute_lhs_resolved_ty.kind + { // We can't as-cast from a non primitive like f128 back to the result_type self.f128_cast_to(val, resolve_lhs_kind)? } else { @@ -739,7 +741,7 @@ impl<'c> Translation<'c> { // TODO: If rust gets f16 support: // CTypeKind::Half | CTypeKind::Float | CTypeKind::Double => mk().lit_expr(mk().float_unsuffixed_lit("1.")), - CTypeKind::LongDouble => { + CTypeKind::LongDouble | CTypeKind::Float128 => { self.use_crate(ExternCrate::F128); let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); @@ -802,7 +804,7 @@ impl<'c> Translation<'c> { CTypeKind::Float | CTypeKind::Double => { mk().lit_expr(mk().float_unsuffixed_lit("1.")) } - CTypeKind::LongDouble => { + CTypeKind::LongDouble | CTypeKind::Float128 => { self.use_crate(ExternCrate::F128); let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); diff --git a/c2rust-transpile/tests/snapshots/f128.c b/c2rust-transpile/tests/snapshots/f128.c new file mode 100644 index 0000000000..d5ece43479 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/f128.c @@ -0,0 +1,37 @@ +#include +#include + +void long_double_test(void) { + long double one = 1.0l; + long double zero; + long double add = one + zero; + long double huge = __builtin_huge_vall(); + + int i = 1; + float f = 1.0f; + long double cast_from_int = i; + long double cast_from_float = f; + + bool is_inf = isinf(huge); + if (one) { + int dummy; + } +} + +void float128_test(void) { + __float128 one = 1.0q; + __float128 zero; + __float128 add = one + zero; + __float128 huge = __builtin_huge_vall(); + + int i = 1; + float f = 1.0f; + __float128 cast_from_int = i; + __float128 cast_from_float = f; + long double ld_from_float128 = one; + + bool is_inf = isinf(huge); + if (one) { + int dummy; + } +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2021.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2021.snap new file mode 100644 index 0000000000..214de7a5b3 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2021.snap @@ -0,0 +1,57 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/f128.2021.rs +--- +#![allow( + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![deny(unsafe_op_in_unsafe_fn)] +use ::num_traits::Float; +#[no_mangle] +pub unsafe extern "C" fn long_double_test() { + unsafe { + let mut one: ::f128::f128 = ::f128::f128::new(1.0); + let mut zero: ::f128::f128 = ::f128::f128::ZERO; + let mut add: ::f128::f128 = one + zero; + let mut huge: ::f128::f128 = ::f128::f128::INFINITY; + let mut i: ::core::ffi::c_int = 1 as ::core::ffi::c_int; + let mut f: ::core::ffi::c_float = 1.0f32; + let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); + let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut is_inf: bool = if huge.is_infinite() { + if huge.is_sign_positive() { 1 } else { -1 } + } else { + 0 + } != 0; + if one != ::f128::f128::ZERO { + let mut dummy: ::core::ffi::c_int = 0; + } + } +} +#[no_mangle] +pub unsafe extern "C" fn float128_test() { + unsafe { + let mut one: ::f128::f128 = ::f128::f128::new(1.0); + let mut zero: ::f128::f128 = ::f128::f128::ZERO; + let mut add: ::f128::f128 = one + zero; + let mut huge: ::f128::f128 = ::f128::f128::INFINITY; + let mut i: ::core::ffi::c_int = 1 as ::core::ffi::c_int; + let mut f: ::core::ffi::c_float = 1.0f32; + let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); + let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut ld_from_float128: ::f128::f128 = one; + let mut is_inf: bool = if huge.is_infinite() { + if huge.is_sign_positive() { 1 } else { -1 } + } else { + 0 + } != 0; + if one != ::f128::f128::ZERO { + let mut dummy: ::core::ffi::c_int = 0; + } + } +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2024.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2024.snap new file mode 100644 index 0000000000..13485d262b --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.2024.snap @@ -0,0 +1,57 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/f128.2024.rs +--- +#![allow( + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![deny(unsafe_op_in_unsafe_fn)] +use ::num_traits::Float; +#[unsafe(no_mangle)] +pub unsafe extern "C" fn long_double_test() { + unsafe { + let mut one: ::f128::f128 = ::f128::f128::new(1.0); + let mut zero: ::f128::f128 = ::f128::f128::ZERO; + let mut add: ::f128::f128 = one + zero; + let mut huge: ::f128::f128 = ::f128::f128::INFINITY; + let mut i: ::core::ffi::c_int = 1 as ::core::ffi::c_int; + let mut f: ::core::ffi::c_float = 1.0f32; + let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); + let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut is_inf: bool = if huge.is_infinite() { + if huge.is_sign_positive() { 1 } else { -1 } + } else { + 0 + } != 0; + if one != ::f128::f128::ZERO { + let mut dummy: ::core::ffi::c_int = 0; + } + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn float128_test() { + unsafe { + let mut one: ::f128::f128 = ::f128::f128::new(1.0); + let mut zero: ::f128::f128 = ::f128::f128::ZERO; + let mut add: ::f128::f128 = one + zero; + let mut huge: ::f128::f128 = ::f128::f128::INFINITY; + let mut i: ::core::ffi::c_int = 1 as ::core::ffi::c_int; + let mut f: ::core::ffi::c_float = 1.0f32; + let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); + let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut ld_from_float128: ::f128::f128 = one; + let mut is_inf: bool = if huge.is_infinite() { + if huge.is_sign_positive() { 1 } else { -1 } + } else { + 0 + } != 0; + if one != ::f128::f128::ZERO { + let mut dummy: ::core::ffi::c_int = 0; + } + } +}