From 421fa6c2d7f2aefa7b9685560be46f68b3155216 Mon Sep 17 00:00:00 2001 From: abdul124 Date: Mon, 8 Jul 2024 15:07:48 +0800 Subject: [PATCH] core: reduce code duplication in codegen/extern_fns --- nac3core/src/codegen/extern_fns.rs | 665 +++++------------------------ 1 file changed, 103 insertions(+), 562 deletions(-) diff --git a/nac3core/src/codegen/extern_fns.rs b/nac3core/src/codegen/extern_fns.rs index c22d69d9..8a17f76f 100644 --- a/nac3core/src/codegen/extern_fns.rs +++ b/nac3core/src/codegen/extern_fns.rs @@ -4,514 +4,119 @@ use itertools::Either; use crate::codegen::CodeGenContext; -/// Invokes the [`tan`](https://en.cppreference.com/w/c/numeric/math/tan) function. -pub fn call_tan<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "tan"; +/// Macro to conveniently generate extern function call +/// Generates a public function with `FloatValue` return type +/// +/// Arguments: +/// * `$fn_name:ident`: The identifier of the rust function to be generated +/// * `$extern_fn:literal`: Name of underlying extern function +/// * `$(,$args:ident)*`: Operands of the function +/// The data type of these operands must be `FloatValue` +/// * `$(,$attributes:literal)*)`: Attributes linked with the extern function +/// +macro_rules! helper_generate_extern_fn_call { + ($fn_name:ident, $extern_fn:literal $(,$args:ident)* $(,$attributes:literal)*) => { + #[doc = concat!("Invokes the [`", stringify!($extern_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($llvm_name), ") function." )] + pub fn $fn_name<'ctx>( + ctx: &CodeGenContext<'ctx, '_> + $(,$args: FloatValue<'ctx>)*, + name: Option<&str>, + ) -> FloatValue<'ctx> { + const FN_NAME: &str = $extern_fn; - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); + let llvm_f64 = ctx.ctx.f64_type(); + $(debug_assert_eq!($args.get_type(), llvm_f64);)* - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); + let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { + let fn_type = llvm_f64.fn_type(&[$($args.get_type().into()),*], false); + let func = ctx.module.add_function(FN_NAME, fn_type, None); + for attr in [$($attributes),*] { + func.add_attribute( + AttributeLoc::Function, + ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), + ); + } + func + }); + + ctx.builder + .build_call(extern_fn, &[$($args.into()),*], name.unwrap_or_default()) + .map(CallSiteValue::try_as_basic_value) + .map(|v| v.map_left(BasicValueEnum::into_float_value)) + .map(Either::unwrap_left) + .unwrap() } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() + }; } -/// Invokes the [`asin`](https://en.cppreference.com/w/c/numeric/math/asin) function. -pub fn call_asin<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "asin"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() +/// Macro to conveniently generate extern function call with [`helper_generate_extern_fn_call`]. +/// Handles unary extern functions only (Use `generate_extern_binary_fn_call` for binary functions) +/// Both function return type and function parameter type are `FloatValue` +/// +/// Arguments: +/// * `$fn_name:ident`: The identifier of the rust function to be generated +/// * `$extern_fn:literal`: Name of underlying extern function +/// * `$(,$attributes:literal)*)`: Attributes linked with the extern function +/// The default attributes are "mustprogress", "nofree", "nounwind", "willreturn", and "writeonly" +/// These will be used unless other attributes are specified +/// +macro_rules! generate_extern_unary_fn_call { + ($fn_name:ident, $extern_fn:literal) => { + helper_generate_extern_fn_call!($fn_name, $extern_fn, arg, "mustprogress", "nofree", "nounwind", "willreturn", "writeonly"); + }; + ($fn_name:ident, $extern_fn:literal $(,$attributes:literal)*) => { + helper_generate_extern_fn_call!($fn_name, $extern_fn, arg $(,$attributes)*); + }; } -/// Invokes the [`acos`](https://en.cppreference.com/w/c/numeric/math/acos) function. -pub fn call_acos<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "acos"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() +/// Macro to conveniently generate extern function call with [`helper_generate_extern_fn_call`]. +/// Handles binary extern functions only (Use `generate_extern_unary_fn_call` for unary functions) +/// Both function return type and function parameter type are `FloatValue` +/// +/// Arguments: +/// * `$fn_name:ident`: The identifier of the rust function to be generated +/// * `$extern_fn:literal`: Name of underlying extern function +/// * `$(,$attributes:literal)*)`: Attributes linked with the extern function +/// The default attributes are "mustprogress", "nofree", "nounwind", "willreturn", and "writeonly" +/// These will be used unless other attributes are specified +/// +macro_rules! generate_extern_binary_fn_call { + ($fn_name:ident, $extern_fn:literal) => { + helper_generate_extern_fn_call!($fn_name, $extern_fn, arg1, arg2, "mustprogress", "nofree", "nounwind", "willreturn", "writeonly"); + }; + ($fn_name:ident, $extern_fn:literal $(,$attributes:literal)*) => { + helper_generate_extern_fn_call!($fn_name, $extern_fn, arg1, arg2 $(,$attributes)*); + }; } -/// Invokes the [`atan`](https://en.cppreference.com/w/c/numeric/math/atan) function. -pub fn call_atan<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "atan"; +generate_extern_unary_fn_call!(call_tan, "tan"); +generate_extern_unary_fn_call!(call_asin, "asin"); +generate_extern_unary_fn_call!(call_acos, "acos"); +generate_extern_unary_fn_call!(call_atan, "atan"); +generate_extern_unary_fn_call!(call_sinh, "sinh"); +generate_extern_unary_fn_call!(call_cosh, "cosh"); +generate_extern_unary_fn_call!(call_tanh, "tanh"); +generate_extern_unary_fn_call!(call_asinh, "asinh"); +generate_extern_unary_fn_call!(call_acosh, "acosh"); +generate_extern_unary_fn_call!(call_atanh, "atanh"); +generate_extern_unary_fn_call!(call_expm1, "expm1"); +generate_extern_unary_fn_call!( + call_cbrt, + "cbrt", + "mustprogress", + "nofree", + "nosync", + "nounwind", + "readonly", + "willreturn" +); +generate_extern_unary_fn_call!(call_erf, "erf", "nounwind"); +generate_extern_unary_fn_call!(call_erfc, "erfc", "nounwind"); +generate_extern_unary_fn_call!(call_j1, "j1", "nounwind"); - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`sinh`](https://en.cppreference.com/w/c/numeric/math/sinh) function. -pub fn call_sinh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "sinh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`cosh`](https://en.cppreference.com/w/c/numeric/math/cosh) function. -pub fn call_cosh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "cosh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`tanh`](https://en.cppreference.com/w/c/numeric/math/tanh) function. -pub fn call_tanh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "tanh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`asinh`](https://en.cppreference.com/w/c/numeric/math/asinh) function. -pub fn call_asinh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "asinh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`acosh`](https://en.cppreference.com/w/c/numeric/math/acosh) function. -pub fn call_acosh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "acosh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`atanh`](https://en.cppreference.com/w/c/numeric/math/atanh) function. -pub fn call_atanh<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "atanh"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`expm1`](https://en.cppreference.com/w/c/numeric/math/expm1) function. -pub fn call_expm1<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "expm1"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`cbrt`](https://en.cppreference.com/w/c/numeric/math/cbrt) function. -pub fn call_cbrt<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "cbrt"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nosync", "nounwind", "readonly", "willreturn"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`erf`](https://en.cppreference.com/w/c/numeric/math/erf) function. -pub fn call_erf<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "erf"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0), - ); - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`erfc`](https://en.cppreference.com/w/c/numeric/math/erfc) function. -pub fn call_erfc<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "erfc"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0), - ); - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`j1`](https://www.gnu.org/software/libc/manual/html_node/Special-Functions.html#index-j1) -/// function. -pub fn call_j1<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - arg: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "j1"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(arg.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0), - ); - - func - }); - - ctx.builder - .build_call(extern_fn, &[arg.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`atan2`](https://en.cppreference.com/w/c/numeric/math/atan2) function. -pub fn call_atan2<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - y: FloatValue<'ctx>, - x: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "atan2"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(y.get_type(), llvm_f64); - debug_assert_eq!(x.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), - ); - } - - func - }); - - ctx.builder - .build_call(extern_fn, &[y.into(), x.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} +generate_extern_binary_fn_call!(call_atan2, "atan2"); +generate_extern_binary_fn_call!(call_hypot, "hypot", "nounwind"); +generate_extern_binary_fn_call!(call_nextafter, "nextafter", "nounwind"); /// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function. pub fn call_ldexp<'ctx>( @@ -547,67 +152,3 @@ pub fn call_ldexp<'ctx>( .map(Either::unwrap_left) .unwrap() } - -/// Invokes the [`hypot`](https://en.cppreference.com/w/c/numeric/math/hypot) function. -pub fn call_hypot<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - x: FloatValue<'ctx>, - y: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "hypot"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(x.get_type(), llvm_f64); - debug_assert_eq!(y.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0), - ); - - func - }); - - ctx.builder - .build_call(extern_fn, &[x.into(), y.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -} - -/// Invokes the [`nextafter`](https://en.cppreference.com/w/c/numeric/math/nextafter) function. -pub fn call_nextafter<'ctx>( - ctx: &CodeGenContext<'ctx, '_>, - from: FloatValue<'ctx>, - to: FloatValue<'ctx>, - name: Option<&str>, -) -> FloatValue<'ctx> { - const FN_NAME: &str = "nextafter"; - - let llvm_f64 = ctx.ctx.f64_type(); - debug_assert_eq!(from.get_type(), llvm_f64); - debug_assert_eq!(to.get_type(), llvm_f64); - - let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { - let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_f64.into()], false); - let func = ctx.module.add_function(FN_NAME, fn_type, None); - func.add_attribute( - AttributeLoc::Function, - ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0), - ); - - func - }); - - ctx.builder - .build_call(extern_fn, &[from.into(), to.into()], name.unwrap_or_default()) - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_float_value)) - .map(Either::unwrap_left) - .unwrap() -}