diff --git a/nac3core/src/codegen/builtin_fns.rs b/nac3core/src/codegen/builtin_fns.rs new file mode 100644 index 00000000..49ecdda0 --- /dev/null +++ b/nac3core/src/codegen/builtin_fns.rs @@ -0,0 +1,1130 @@ +use inkwell::{FloatPredicate, IntPredicate}; +use inkwell::types::{BasicTypeEnum, IntType}; +use inkwell::values::{BasicValueEnum, FloatValue, IntValue}; +use itertools::Itertools; + +use crate::codegen::{CodeGenContext, CodeGenerator, extern_fns, irrt, llvm_intrinsics}; +use crate::typecheck::typedef::Type; + +/// Shorthand for [`unreachable!()`] when a type of argument is not supported. +/// +/// The generated message will contain the function name and the name of the unsupported type. +fn unsupported_type( + ctx: &CodeGenContext<'_, '_>, + fn_name: &str, + tys: &[Type], +) -> ! { + unreachable!( + "{fn_name}() not supported for '{}'", + tys.iter().map(|ty| format!("'{}'", ctx.unifier.stringify(*ty))).join(", "), + ) +} + +/// Invokes the `int32` builtin function. +pub fn call_int32<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> IntValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.bool)); + + ctx.builder + .build_int_z_extend(n.into_int_value(), llvm_i32, "zext") + .unwrap() + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 32 => { + debug_assert!([ + ctx.primitives.int32, + ctx.primitives.uint32, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + n.into_int_value() + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 64 => { + debug_assert!([ + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + ctx.builder + .build_int_truncate(n.into_int_value(), llvm_i32, "trunc") + .unwrap() + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let to_int64 = ctx.builder + .build_float_to_signed_int(n.into_float_value(), ctx.ctx.i64_type(), "") + .unwrap(); + ctx.builder + .build_int_truncate(to_int64, llvm_i32, "conv") + .unwrap() + } + + _ => unsupported_type(ctx, "int32", &[n_ty]) + } +} + +/// Invokes the `int64` builtin function. +pub fn call_int64<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> IntValue<'ctx> { + let llvm_i64 = ctx.ctx.i64_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8 | 32) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + if ctx.unifier.unioned(n_ty, ctx.primitives.int32) { + ctx.builder + .build_int_s_extend(n.into_int_value(), llvm_i64, "sext") + .unwrap() + } else { + ctx.builder + .build_int_z_extend(n.into_int_value(), llvm_i64, "zext") + .unwrap() + } + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 64 => { + debug_assert!([ + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + n.into_int_value() + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + ctx.builder + .build_float_to_signed_int(n.into_float_value(), ctx.ctx.i64_type(), "fptosi") + .unwrap() + } + + _ => unsupported_type(ctx, "int64", &[n_ty]) + } +} + +/// Invokes the `uint32` builtin function. +pub fn call_uint32<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> IntValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.bool)); + + ctx.builder + .build_int_z_extend(n.into_int_value(), llvm_i32, "zext") + .unwrap() + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 32 => { + debug_assert!([ + ctx.primitives.int32, + ctx.primitives.uint32, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + n.into_int_value() + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 64 => { + debug_assert!( + ctx.unifier.unioned(n_ty, ctx.primitives.int64) + || ctx.unifier.unioned(n_ty, ctx.primitives.uint64) + ); + + ctx.builder + .build_int_truncate(n.into_int_value(), llvm_i32, "trunc") + .unwrap() + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let val = n.into_float_value(); + let val_gez = ctx.builder + .build_float_compare(FloatPredicate::OGE, val, val.get_type().const_zero(), "") + .unwrap(); + + let to_int32 = ctx.builder + .build_float_to_signed_int(val, llvm_i32, "") + .unwrap(); + let to_uint64 = ctx.builder + .build_float_to_unsigned_int(val, ctx.ctx.i64_type(), "") + .unwrap(); + + ctx.builder + .build_select( + val_gez, + ctx.builder.build_int_truncate(to_uint64, llvm_i32, "").unwrap(), + to_int32, + "conv", + ) + .map(BasicValueEnum::into_int_value) + .unwrap() + } + + _ => unsupported_type(ctx, "uint32", &[n_ty]) + } +} + +/// Invokes the `uint64` builtin function. +pub fn call_uint64<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> IntValue<'ctx> { + let llvm_i64 = ctx.ctx.i64_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8 | 32) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + if ctx.unifier.unioned(n_ty, ctx.primitives.int32) { + ctx.builder + .build_int_s_extend(n.into_int_value(), llvm_i64, "sext") + .unwrap() + } else { + ctx.builder + .build_int_z_extend(n.into_int_value(), llvm_i64, "zext") + .unwrap() + } + } + + BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() == 64 => { + debug_assert!([ + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + n.into_int_value() + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let val = n.into_float_value(); + let val_gez = ctx.builder + .build_float_compare(FloatPredicate::OGE, val, val.get_type().const_zero(), "") + .unwrap(); + + let to_int64 = ctx.builder + .build_float_to_signed_int(val, llvm_i64, "") + .unwrap(); + let to_uint64 = ctx.builder + .build_float_to_unsigned_int(val, llvm_i64, "") + .unwrap(); + + ctx.builder + .build_select(val_gez, to_uint64, to_int64, "conv") + .map(BasicValueEnum::into_int_value) + .unwrap() + } + + _ => unsupported_type(ctx, "uint64", &[n_ty]) + } +} + +/// Invokes the `float` builtin function. +pub fn call_float<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> FloatValue<'ctx> { + let llvm_f64 = ctx.ctx.f64_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8 | 32 | 64) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + if [ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.int64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty)) { + ctx.builder + .build_signed_int_to_float(n.into_int_value(), llvm_f64, "sitofp") + .unwrap() + } else { + ctx.builder + .build_unsigned_int_to_float(n.into_int_value(), llvm_f64, "uitofp") + .unwrap() + } + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + n.into_float_value() + } + + _ => unsupported_type(ctx, "float", &[n_ty]) + } +} + +/// Invokes the `round` builtin function. +pub fn call_round<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, FloatValue<'ctx>), + llvm_ret_ty: IntType<'ctx>, +) -> IntValue<'ctx> { + const FN_NAME: &str = "round"; + + let (n_ty, n) = n; + + if !ctx.unifier.unioned(n_ty, ctx.primitives.float) { + unsupported_type(ctx, FN_NAME, &[n_ty]) + } + + let val = llvm_intrinsics::call_float_round(ctx, n, None); + ctx.builder + .build_float_to_signed_int(val, llvm_ret_ty, FN_NAME) + .unwrap() +} + +/// Invokes the `np_round` builtin function. +pub fn call_numpy_round<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (n_ty, n) = n; + + if !ctx.unifier.unioned(n_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_round", &[n_ty]) + } + + llvm_intrinsics::call_float_roundeven(ctx, n, None) +} + +/// Invokes the `bool` builtin function. +pub fn call_bool<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> IntValue<'ctx> { + const FN_NAME: &str = "bool"; + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(int_ty) if matches!(int_ty.get_bit_width(), 1 | 8) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.bool)); + + n.into_int_value() + } + + BasicTypeEnum::IntType(_) => { + debug_assert!([ + ctx.primitives.int32, + ctx.primitives.uint32, + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + let val = n.into_int_value(); + ctx.builder + .build_int_compare(IntPredicate::NE, val, val.get_type().const_zero(), FN_NAME) + .unwrap() + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let val = n.into_float_value(); + ctx.builder + .build_float_compare(FloatPredicate::UNE, val, val.get_type().const_zero(), FN_NAME) + .unwrap() + } + + _ => unsupported_type(ctx, FN_NAME, &[n_ty]) + } +} + +/// Invokes the `floor` builtin function. +pub fn call_floor<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, FloatValue<'ctx>), + llvm_ret_ty: BasicTypeEnum<'ctx>, +) -> BasicValueEnum<'ctx> { + const FN_NAME: &str = "floor"; + + let (n_ty, n) = n; + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let val = llvm_intrinsics::call_float_floor(ctx, n, None); + match llvm_ret_ty { + _ if llvm_ret_ty == val.get_type().into() => val.into(), + + BasicTypeEnum::IntType(_) => { + ctx.builder + .build_float_to_signed_int(val, llvm_ret_ty.into_int_type(), FN_NAME) + .map(Into::into) + .unwrap() + } + + _ => unsupported_type(ctx, FN_NAME, &[n_ty]) + } +} + +/// Invokes the `ceil` builtin function. +pub fn call_ceil<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, FloatValue<'ctx>), + llvm_ret_ty: BasicTypeEnum<'ctx>, +) -> BasicValueEnum<'ctx> { + const FN_NAME: &str = "ceil"; + + let (n_ty, n) = n; + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + let val = llvm_intrinsics::call_float_ceil(ctx, n, None); + match llvm_ret_ty { + _ if llvm_ret_ty == val.get_type().into() => val.into(), + + BasicTypeEnum::IntType(_) => { + ctx.builder + .build_float_to_signed_int(val, llvm_ret_ty.into_int_type(), FN_NAME) + .map(Into::into) + .unwrap() + } + + _ => unsupported_type(ctx, FN_NAME, &[n_ty]) + } +} + +/// Invokes the `min` builtin function. +pub fn call_min<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + m: (Type, BasicValueEnum<'ctx>), + n: (Type, BasicValueEnum<'ctx>), +) -> BasicValueEnum<'ctx> { + const FN_NAME: &str = "min"; + + let (m_ty, m) = m; + let (n_ty, n) = n; + + if !ctx.unifier.unioned(m_ty, n_ty) { + unsupported_type(ctx, FN_NAME, &[m_ty, n_ty]) + } + debug_assert_eq!(m.get_type(), n.get_type()); + + let common_ty = m_ty; + let llvm_common_ty = m.get_type(); + + match llvm_common_ty { + BasicTypeEnum::IntType(_) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(common_ty, *ty))); + + let (m, n) = (m.into_int_value(), n.into_int_value()); + + if [ + ctx.primitives.int32, + ctx.primitives.int64, + ].iter().any(|ty| ctx.unifier.unioned(common_ty, *ty)) { + llvm_intrinsics::call_int_smin(ctx, m, n, Some(FN_NAME)).into() + } else { + llvm_intrinsics::call_int_umin(ctx, m, n, Some(FN_NAME)).into() + } + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(common_ty, ctx.primitives.float)); + + let (m, n) = (m.into_float_value(), n.into_float_value()); + + llvm_intrinsics::call_float_minnum(ctx, m, n, Some(FN_NAME)).into() + } + + _ => unsupported_type(ctx, FN_NAME, &[m_ty, n_ty]) + } +} + +/// Invokes the `max` builtin function. +pub fn call_max<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + m: (Type, BasicValueEnum<'ctx>), + n: (Type, BasicValueEnum<'ctx>), +) -> BasicValueEnum<'ctx> { + const FN_NAME: &str = "max"; + + let (m_ty, m) = m; + let (n_ty, n) = n; + + if !ctx.unifier.unioned(m_ty, n_ty) { + unsupported_type(ctx, FN_NAME, &[m_ty, n_ty]) + } + debug_assert_eq!(m.get_type(), n.get_type()); + + let common_ty = m_ty; + let llvm_common_ty = m.get_type(); + + match llvm_common_ty { + BasicTypeEnum::IntType(_) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(common_ty, *ty))); + + let (m, n) = (m.into_int_value(), n.into_int_value()); + + if [ + ctx.primitives.int32, + ctx.primitives.int64, + ].iter().any(|ty| ctx.unifier.unioned(common_ty, *ty)) { + llvm_intrinsics::call_int_smax(ctx, m, n, Some(FN_NAME)).into() + } else { + llvm_intrinsics::call_int_umax(ctx, m, n, Some(FN_NAME)).into() + } + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(common_ty, ctx.primitives.float)); + + let (m, n) = (m.into_float_value(), n.into_float_value()); + + llvm_intrinsics::call_float_maxnum(ctx, m, n, Some(FN_NAME)).into() + } + + _ => unsupported_type(ctx, FN_NAME, &[m_ty, n_ty]) + } +} + +/// Invokes the `abs` builtin function. +pub fn call_abs<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + n: (Type, BasicValueEnum<'ctx>), +) -> BasicValueEnum<'ctx> { + const FN_NAME: &str = "abs"; + + let llvm_i1 = ctx.ctx.bool_type(); + + let (n_ty, n) = n; + + match n.get_type() { + BasicTypeEnum::IntType(_) => { + debug_assert!([ + ctx.primitives.bool, + ctx.primitives.int32, + ctx.primitives.uint32, + ctx.primitives.int64, + ctx.primitives.uint64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty))); + + if [ + ctx.primitives.int32, + ctx.primitives.int64, + ].iter().any(|ty| ctx.unifier.unioned(n_ty, *ty)) { + llvm_intrinsics::call_int_abs( + ctx, + n.into_int_value(), + llvm_i1.const_zero(), + Some(FN_NAME), + ).into() + } else { + n + } + } + + BasicTypeEnum::FloatType(_) => { + debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float)); + + llvm_intrinsics::call_float_fabs(ctx, n.into_float_value(), Some(FN_NAME)).into() + } + + _ => unsupported_type(ctx, FN_NAME, &[n_ty]) + } +} + +/// Invokes the `np_isnan` builtin function. +pub fn call_numpy_isnan<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> IntValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_isnan", &[x_ty]) + } + + irrt::call_isnan(generator, ctx, x) +} + +/// Invokes the `np_isinf` builtin function. +pub fn call_numpy_isinf<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> IntValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_isinf", &[x_ty]) + } + + irrt::call_isinf(generator, ctx, x) +} + +/// Invokes the `np_sin` builtin function. +pub fn call_numpy_sin<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_sin", &[x_ty]) + } + + llvm_intrinsics::call_float_sin(ctx, x, None) +} + +/// Invokes the `np_cos` builtin function. +pub fn call_numpy_cos<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_cos", &[x_ty]) + } + + llvm_intrinsics::call_float_cos(ctx, x, None) +} + +/// Invokes the `np_exp` builtin function. +pub fn call_numpy_exp<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_exp", &[x_ty]) + } + + llvm_intrinsics::call_float_exp(ctx, x, None) +} + +/// Invokes the `np_exp2` builtin function. +pub fn call_numpy_exp2<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_exp2", &[x_ty]) + } + + llvm_intrinsics::call_float_exp2(ctx, x, None) +} + +/// Invokes the `np_log` builtin function. +pub fn call_numpy_log<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_log", &[x_ty]) + } + + llvm_intrinsics::call_float_log(ctx, x, None) +} + +/// Invokes the `np_log10` builtin function. +pub fn call_numpy_log10<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_log10", &[x_ty]) + } + + llvm_intrinsics::call_float_log10(ctx, x, None) +} + +/// Invokes the `np_log2` builtin function. +pub fn call_numpy_log2<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_log2", &[x_ty]) + } + + llvm_intrinsics::call_float_log2(ctx, x, None) +} + +/// Invokes the `np_sqrt` builtin function. +pub fn call_numpy_fabs<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_fabs", &[x_ty]) + } + + llvm_intrinsics::call_float_fabs(ctx, x, None) +} + +/// Invokes the `np_sqrt` builtin function. +pub fn call_numpy_sqrt<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_sqrt", &[x_ty]) + } + + llvm_intrinsics::call_float_sqrt(ctx, x, None) +} + +/// Invokes the `np_rint` builtin function. +pub fn call_numpy_rint<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_rint", &[x_ty]) + } + + llvm_intrinsics::call_float_roundeven(ctx, x, None) +} + +/// Invokes the `np_tan` builtin function. +pub fn call_numpy_tan<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_tan", &[x_ty]) + } + + extern_fns::call_tan(ctx, x, None) +} + +/// Invokes the `np_arcsin` builtin function. +pub fn call_numpy_arcsin<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_arcsin", &[x_ty]) + } + + extern_fns::call_asin(ctx, x, None) +} + +/// Invokes the `np_arccos` builtin function. +pub fn call_numpy_arccos<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_arccos", &[x_ty]) + } + + extern_fns::call_acos(ctx, x, None) +} + +/// Invokes the `np_arctan` builtin function. +pub fn call_numpy_arctan<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_arctan", &[x_ty]) + } + + extern_fns::call_atan(ctx, x, None) +} + +/// Invokes the `np_sinh` builtin function. +pub fn call_numpy_sinh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_sinh", &[x_ty]) + } + + extern_fns::call_sinh(ctx, x, None) +} + +/// Invokes the `np_cosh` builtin function. +pub fn call_numpy_cosh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_cosh", &[x_ty]) + } + + extern_fns::call_cosh(ctx, x, None) +} + +/// Invokes the `np_tanh` builtin function. +pub fn call_numpy_tanh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_tanh", &[x_ty]) + } + + extern_fns::call_tanh(ctx, x, None) +} + +/// Invokes the `np_asinh` builtin function. +pub fn call_numpy_asinh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_asinh", &[x_ty]) + } + + extern_fns::call_asinh(ctx, x, None) +} + +/// Invokes the `np_acosh` builtin function. +pub fn call_numpy_acosh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_acosh", &[x_ty]) + } + + extern_fns::call_acosh(ctx, x, None) +} + +/// Invokes the `np_atanh` builtin function. +pub fn call_numpy_atanh<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_atanh", &[x_ty]) + } + + extern_fns::call_atanh(ctx, x, None) +} + +/// Invokes the `np_expm1` builtin function. +pub fn call_numpy_expm1<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_expm1", &[x_ty]) + } + + extern_fns::call_expm1(ctx, x, None) +} + +/// Invokes the `np_cbrt` builtin function. +pub fn call_numpy_cbrt<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "np_cbrt", &[x_ty]) + } + + extern_fns::call_cbrt(ctx, x, None) +} + +/// Invokes the `sp_spec_erf` builtin function. +pub fn call_scipy_special_erf<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + z: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (z_ty, z) = z; + + if !ctx.unifier.unioned(z_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_erf", &[z_ty]) + } + + extern_fns::call_erf(ctx, z, None) +} + +/// Invokes the `sp_spec_erfc` builtin function. +pub fn call_scipy_special_erfc<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_erfc", &[x_ty]) + } + + extern_fns::call_erfc(ctx, x, None) +} + +/// Invokes the `sp_spec_gamma` builtin function. +pub fn call_scipy_special_gamma<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + z: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (z_ty, z) = z; + + if !ctx.unifier.unioned(z_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_gamma", &[z_ty]) + } + + irrt::call_gamma(ctx, z) +} + +/// Invokes the `sp_spec_gammaln` builtin function. +pub fn call_scipy_special_gammaln<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_gammaln", &[x_ty]) + } + + irrt::call_gammaln(ctx, x) +} + +/// Invokes the `sp_spec_j0` builtin function. +pub fn call_scipy_special_j0<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_j0", &[x_ty]) + } + + irrt::call_j0(ctx, x) +} + +/// Invokes the `sp_spec_j1` builtin function. +pub fn call_scipy_special_j1<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let (x_ty, x) = x; + + if !ctx.unifier.unioned(x_ty, ctx.primitives.float) { + unsupported_type(ctx, "sp_spec_j1", &[x_ty]) + } + + extern_fns::call_j1(ctx, x, None) +} + +/// Invokes the `np_arctan2` builtin function. +pub fn call_numpy_arctan2<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_atan2", &[x1_ty, x2_ty]) + } + + extern_fns::call_atan2(ctx, x1, x2, None) +} + +/// Invokes the `np_copysign` builtin function. +pub fn call_numpy_copysign<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + debug_assert_eq!(x1.get_type(), x2.get_type()); + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_copysign", &[x1_ty, x2_ty]) + } + + llvm_intrinsics::call_float_copysign(ctx, x1, x2, None) +} + +/// Invokes the `np_fmax` builtin function. +pub fn call_numpy_fmax<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + debug_assert_eq!(x1.get_type(), x2.get_type()); + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_fmax", &[x1_ty, x2_ty]) + } + + llvm_intrinsics::call_float_maxnum(ctx, x1, x2, None) +} + +/// Invokes the `np_fmin` builtin function. +pub fn call_numpy_fmin<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + debug_assert_eq!(x1.get_type(), x2.get_type()); + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_fmin", &[x1_ty, x2_ty]) + } + + llvm_intrinsics::call_float_minnum(ctx, x1, x2, None) +} + +/// Invokes the `np_ldexp` builtin function. +pub fn call_numpy_ldexp<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, IntValue<'ctx>), +) -> FloatValue<'ctx> { + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + + if !ctx.unifier.unioned(x1_ty, ctx.primitives.float) { + unsupported_type(ctx, "fp_ldexp", &[x1_ty, x2_ty]) + } + if !ctx.unifier.unioned(x2_ty, ctx.primitives.int32) { + unsupported_type(ctx, "fp_ldexp", &[x1_ty, x2_ty]) + } + + extern_fns::call_ldexp(ctx, x1, x2, None) +} + +/// Invokes the `np_hypot` builtin function. +pub fn call_numpy_hypot<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_hypot", &[x1_ty, x2_ty]) + } + + extern_fns::call_hypot(ctx, x1, x2, None) +} + +/// Invokes the `np_nextafter` builtin function. +pub fn call_numpy_nextafter<'ctx>( + ctx: &mut CodeGenContext<'ctx, '_>, + x1: (Type, FloatValue<'ctx>), + x2: (Type, FloatValue<'ctx>), +) -> FloatValue<'ctx> { + let float_t = ctx.primitives.float; + + let (x1_ty, x1) = x1; + let (x2_ty, x2) = x2; + + if !ctx.unifier.unioned(x1_ty, float_t) || !ctx.unifier.unioned(x2_ty, float_t) { + unsupported_type(ctx, "np_nextafter", &[x1_ty, x2_ty]) + } + + extern_fns::call_nextafter(ctx, x1, x2, None) +} \ No newline at end of file diff --git a/nac3core/src/codegen/extern_fns.rs b/nac3core/src/codegen/extern_fns.rs new file mode 100644 index 00000000..e29b8a17 --- /dev/null +++ b/nac3core/src/codegen/extern_fns.rs @@ -0,0 +1,613 @@ +use inkwell::attributes::{Attribute, AttributeLoc}; +use inkwell::values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue}; +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"; + + 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 [`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() +} + +/// 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() +} + +/// 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"; + + 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() +} + +/// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function. +pub fn call_ldexp<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + arg: FloatValue<'ctx>, + exp: IntValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + const FN_NAME: &str = "ldexp"; + + let llvm_f64 = ctx.ctx.f64_type(); + let llvm_i32 = ctx.ctx.i32_type(); + debug_assert_eq!(arg.get_type(), llvm_f64); + debug_assert_eq!(exp.get_type(), llvm_i32); + + let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { + let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_i32.into()], false); + let func = ctx.module.add_function(FN_NAME, fn_type, None); + for attr in ["mustprogress", "nofree", "nounwind", "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(), exp.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 [`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() +} \ No newline at end of file diff --git a/nac3core/src/codegen/llvm_intrinsics.rs b/nac3core/src/codegen/llvm_intrinsics.rs index 3be6c7ef..78187ea5 100644 --- a/nac3core/src/codegen/llvm_intrinsics.rs +++ b/nac3core/src/codegen/llvm_intrinsics.rs @@ -311,6 +311,29 @@ pub fn call_memcpy_generic<'ctx>( call_memcpy(ctx, dest, src, len, is_volatile); } +/// Invokes the [`llvm.sqrt`](https://llvm.org/docs/LangRef.html#llvm-sqrt-intrinsic) intrinsic. +pub fn call_float_sqrt<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.sqrt.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.powi`](https://llvm.org/docs/LangRef.html#llvm-powi-intrinsic) intrinsic. pub fn call_float_powi<'ctx>( ctx: &CodeGenContext<'ctx, '_>, @@ -340,6 +363,52 @@ pub fn call_float_powi<'ctx>( .unwrap() } +/// Invokes the [`llvm.sin`](https://llvm.org/docs/LangRef.html#llvm-sin-intrinsic) intrinsic. +pub fn call_float_sin<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.sin.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.cos`](https://llvm.org/docs/LangRef.html#llvm-cos-intrinsic) intrinsic. +pub fn call_float_cos<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.cos.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.pow`](https://llvm.org/docs/LangRef.html#llvm-pow-intrinsic) intrinsic. pub fn call_float_pow<'ctx>( ctx: &CodeGenContext<'ctx, '_>, @@ -366,6 +435,122 @@ pub fn call_float_pow<'ctx>( .unwrap() } +/// Invokes the [`llvm.exp`](https://llvm.org/docs/LangRef.html#llvm-exp-intrinsic) intrinsic. +pub fn call_float_exp<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.exp.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.exp2`](https://llvm.org/docs/LangRef.html#llvm-exp2-intrinsic) intrinsic. +pub fn call_float_exp2<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.exp2.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.log`](https://llvm.org/docs/LangRef.html#llvm-log-intrinsic) intrinsic. +pub fn call_float_log<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.log.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.log10`](https://llvm.org/docs/LangRef.html#llvm-log10-intrinsic) intrinsic. +pub fn call_float_log10<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.log10.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.log2`](https://llvm.org/docs/LangRef.html#llvm-log2-intrinsic) intrinsic. +pub fn call_float_log2<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + val: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + let llvm_float_t = val.get_type(); + + let fn_name = format!("llvm.log2.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[val.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 [`llvm.fabs`](https://llvm.org/docs/LangRef.html#llvm-fabs-intrinsic) intrinsic. pub fn call_float_fabs<'ctx>( ctx: &CodeGenContext<'ctx, '_>, @@ -442,6 +627,32 @@ pub fn call_float_maxnum<'ctx>( .unwrap() } +/// Invokes the [`llvm.copysign`](https://llvm.org/docs/LangRef.html#llvm-copysign-intrinsic) intrinsic. +pub fn call_float_copysign<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + mag: FloatValue<'ctx>, + sgn: FloatValue<'ctx>, + name: Option<&str>, +) -> FloatValue<'ctx> { + debug_assert_eq!(mag.get_type(), sgn.get_type()); + + let llvm_float_t = mag.get_type(); + + let fn_name = format!("llvm.copysign.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t)); + let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| { + let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false); + + ctx.module.add_function(fn_name.as_str(), fn_type, None) + }); + + ctx.builder + .build_call(intrinsic_fn, &[mag.into(), sgn.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 [`llvm.floor`](https://llvm.org/docs/LangRef.html#llvm-floor-intrinsic) intrinsic. pub fn call_float_floor<'ctx>( ctx: &CodeGenContext<'ctx, '_>, diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index c07185eb..06b24bff 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -39,9 +39,11 @@ use std::sync::{ }; use std::thread; +pub mod builtin_fns; pub mod classes; pub mod concrete_type; pub mod expr; +pub mod extern_fns; mod generator; pub mod irrt; pub mod llvm_intrinsics; diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 752dc659..5e6d56c8 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -1,10 +1,10 @@ use super::*; use crate::{ codegen::{ + builtin_fns, classes::{ArrayLikeValue, NDArrayValue, RangeValue, TypedArrayLikeAccessor}, expr::destructure_range, irrt::*, - llvm_intrinsics, numpy::*, stmt::exn_constructor, }, @@ -19,7 +19,6 @@ use inkwell::{ attributes::{Attribute, AttributeLoc}, types::{BasicType, BasicMetadataTypeEnum}, values::{BasicValue, BasicMetadataValueEnum, CallSiteValue}, - FloatPredicate, IntPredicate }; use itertools::Either; @@ -579,63 +578,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let PrimitiveStore { - int32, - int64, - uint32, - uint64, - float, - bool: boolean, - .. - } = ctx.primitives; - let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - Ok(if ctx.unifier.unioned(arg_ty, boolean) { - Some( - ctx.builder - .build_int_z_extend( - arg.into_int_value(), - ctx.ctx.i32_type(), - "zext", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, int32) - || ctx.unifier.unioned(arg_ty, uint32) - { - Some(arg) - } else if ctx.unifier.unioned(arg_ty, int64) - || ctx.unifier.unioned(arg_ty, uint64) - { - Some( - ctx.builder - .build_int_truncate( - arg.into_int_value(), - ctx.ctx.i32_type(), - "trunc", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, float) { - let to_int64 = ctx - .builder - .build_float_to_signed_int( - arg.into_float_value(), - ctx.ctx.i64_type(), - "", - ) - .unwrap(); - let val = ctx.builder - .build_int_truncate(to_int64, ctx.ctx.i32_type(), "conv") - .unwrap(); - Some(val.into()) - } else { - unreachable!() - }) + Ok(Some(builtin_fns::call_int32(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -654,62 +600,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let PrimitiveStore { - int32, - int64, - uint32, - uint64, - float, - bool: boolean, - .. - } = ctx.primitives; - let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - Ok( - if ctx.unifier.unioned(arg_ty, boolean) - || ctx.unifier.unioned(arg_ty, uint32) - { - Some( - ctx.builder - .build_int_z_extend( - arg.into_int_value(), - ctx.ctx.i64_type(), - "zext", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, int32) { - Some( - ctx.builder - .build_int_s_extend( - arg.into_int_value(), - ctx.ctx.i64_type(), - "sext", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, int64) - || ctx.unifier.unioned(arg_ty, uint64) - { - Some(arg) - } else if ctx.unifier.unioned(arg_ty, float) { - let val = ctx - .builder - .build_float_to_signed_int( - arg.into_float_value(), - ctx.ctx.i64_type(), - "fptosi", - ) - .map(Into::into) - .unwrap(); - Some(val) - } else { - unreachable!() - }, - ) + + Ok(Some(builtin_fns::call_int64(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -728,77 +622,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let PrimitiveStore { - int32, - int64, - uint32, - uint64, - float, - bool: boolean, - .. - } = ctx.primitives; - let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - let res = if ctx.unifier.unioned(arg_ty, boolean) { - ctx.builder - .build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext") - .map(Into::into) - .unwrap() - } else if ctx.unifier.unioned(arg_ty, int32) - || ctx.unifier.unioned(arg_ty, uint32) - { - arg - } else if ctx.unifier.unioned(arg_ty, int64) - || ctx.unifier.unioned(arg_ty, uint64) - { - ctx.builder - .build_int_truncate(arg.into_int_value(), ctx.ctx.i32_type(), "trunc") - .map(Into::into) - .unwrap() - } else if ctx.unifier.unioned(arg_ty, float) { - let llvm_i32 = ctx.ctx.i32_type(); - let llvm_i64 = ctx.ctx.i64_type(); - let arg = arg.into_float_value(); - let arg_gez = ctx.builder - .build_float_compare( - FloatPredicate::OGE, - arg, - arg.get_type().const_zero(), - "", - ) - .unwrap(); - - let to_int32 = ctx.builder - .build_float_to_signed_int( - arg, - llvm_i32, - "", - ) - .unwrap(); - let to_uint64 = ctx.builder - .build_float_to_unsigned_int( - arg, - llvm_i64, - "", - ) - .unwrap(); - - let val = ctx.builder - .build_select( - arg_gez, - ctx.builder.build_int_truncate(to_uint64, llvm_i32, "").unwrap(), - to_int32, - "conv", - ) - .unwrap(); - - val - } else { - unreachable!() - }; - Ok(Some(res)) + Ok(Some(builtin_fns::call_uint32(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -817,63 +644,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let PrimitiveStore { - int32, - int64, - uint32, - uint64, - float, - bool: boolean, - .. - } = ctx.primitives; - let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - let res = if ctx.unifier.unioned(arg_ty, uint32) - || ctx.unifier.unioned(arg_ty, boolean) - { - ctx.builder - .build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext") - .map(Into::into) - .unwrap() - } else if ctx.unifier.unioned(arg_ty, int32) { - ctx.builder - .build_int_s_extend(arg.into_int_value(), ctx.ctx.i64_type(), "sext") - .map(Into::into) - .unwrap() - } else if ctx.unifier.unioned(arg_ty, int64) - || ctx.unifier.unioned(arg_ty, uint64) - { - arg - } else if ctx.unifier.unioned(arg_ty, float) { - let llvm_i64 = ctx.ctx.i64_type(); - let arg = arg.into_float_value(); - let arg_gez = ctx.builder - .build_float_compare( - FloatPredicate::OGE, - arg, - arg.get_type().const_zero(), - "", - ) - .unwrap(); - - let to_int64 = ctx.builder - .build_float_to_signed_int(arg, llvm_i64, "") - .unwrap(); - let to_uint64 = ctx.builder - .build_float_to_unsigned_int(arg, llvm_i64, "") - .unwrap(); - - let val = ctx.builder - .build_select(arg_gez, to_uint64, to_int64, "conv") - .unwrap(); - - val - } else { - unreachable!() - }; - Ok(Some(res)) + Ok(Some(builtin_fns::call_uint64(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -892,42 +666,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let PrimitiveStore { - int32, - int64, - uint32, - uint64, - float, - bool: boolean, - .. - } = ctx.primitives; - let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - Ok( - if [boolean, int32, int64].iter().any(|ty| ctx.unifier.unioned(arg_ty, *ty)) - { - let arg = arg.into_int_value(); - let val = ctx - .builder - .build_signed_int_to_float(arg, ctx.ctx.f64_type(), "sitofp") - .map(Into::into) - .unwrap(); - Some(val) - } else if [uint32, uint64].iter().any(|ty| ctx.unifier.unioned(arg_ty, *ty)) { - let arg = arg.into_int_value(); - let val = ctx - .builder - .build_unsigned_int_to_float(arg, ctx.ctx.f64_type(), "uitofp") - .map(Into::into) - .unwrap(); - Some(val) - } else if ctx.unifier.unioned(arg_ty, float) { - Some(arg) - } else { - unreachable!() - }, - ) + + Ok(Some(builtin_fns::call_float(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -1047,18 +789,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "round", int32, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i32 = ctx.ctx.i32_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_round(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i32, "round") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_round(ctx, (arg_ty, arg), llvm_i32).into())) }), ), create_fn_by_codegen( @@ -1067,18 +806,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "round64", int64, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i64 = ctx.ctx.i64_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_round(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i64, "round") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_round(ctx, (arg_ty, arg), llvm_i64).into())) }), ), create_fn_by_codegen( @@ -1087,14 +823,13 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "np_round", float, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_roundeven(ctx, arg, None); - - Ok(Some(val.into())) + Ok(Some(builtin_fns::call_numpy_round(ctx, (arg_ty, arg)).into())) }), ), Arc::new(RwLock::new(TopLevelDef::Function { @@ -1236,54 +971,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let int32 = ctx.primitives.int32; - let int64 = ctx.primitives.int64; - let float = ctx.primitives.float; - let boolean = ctx.primitives.bool; let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; - Ok(if ctx.unifier.unioned(arg_ty, boolean) { - Some(arg) - } else if ctx.unifier.unioned(arg_ty, int32) { - Some( - ctx.builder - .build_int_compare( - IntPredicate::NE, - ctx.ctx.i32_type().const_zero(), - arg.into_int_value(), - "bool", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, int64) { - Some( - ctx.builder - .build_int_compare( - IntPredicate::NE, - ctx.ctx.i64_type().const_zero(), - arg.into_int_value(), - "bool", - ) - .map(Into::into) - .unwrap(), - ) - } else if ctx.unifier.unioned(arg_ty, float) { - let val = ctx - .builder - .build_float_compare( - // UEQ as bool(nan) is True - FloatPredicate::UEQ, - arg.into_float_value(), - ctx.ctx.f64_type().const_zero(), - "bool", - ) - .map(Into::into) - .unwrap(); - Some(val) - } else { - unreachable!() - }) + + Ok(Some(builtin_fns::call_bool(ctx, (arg_ty, arg)).into())) }, )))), loc: None, @@ -1294,18 +985,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "floor", int32, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i32 = ctx.ctx.i32_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_floor(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i32, "floor") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_floor(ctx, (arg_ty, arg), llvm_i32.into()))) }), ), create_fn_by_codegen( @@ -1314,18 +1002,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "floor64", int64, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i64 = ctx.ctx.i64_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_floor(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i64, "floor") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_floor(ctx, (arg_ty, arg), llvm_i64.into()))) }), ), create_fn_by_codegen( @@ -1334,13 +1019,13 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "np_floor", float, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_floor(ctx, arg, None); - Ok(Some(val.into())) + Ok(Some(builtin_fns::call_floor(ctx, (arg_ty, arg), arg.get_type().into()))) }), ), create_fn_by_codegen( @@ -1349,18 +1034,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "ceil", int32, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i32 = ctx.ctx.i32_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_ceil(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i32, "ceil") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_ceil(ctx, (arg_ty, arg), llvm_i32.into()))) }), ), create_fn_by_codegen( @@ -1369,18 +1051,15 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "ceil64", int64, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { let llvm_i64 = ctx.ctx.i64_type(); + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_ceil(ctx, arg, None); - let val_toint = ctx.builder - .build_float_to_signed_int(val, llvm_i64, "ceil") - .unwrap(); - Ok(Some(val_toint.into())) + Ok(Some(builtin_fns::call_ceil(ctx, (arg_ty, arg), llvm_i64.into()))) }), ), create_fn_by_codegen( @@ -1389,13 +1068,13 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built "np_ceil", float, &[(float, "n")], - Box::new(|ctx, _, _, args, generator| { + Box::new(|ctx, _, fun, args, generator| { + let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone() .to_basic_value_enum(ctx, generator, ctx.primitives.float)? .into_float_value(); - let val = llvm_intrinsics::call_float_ceil(ctx, arg, None); - Ok(Some(val.into())) + Ok(Some(builtin_fns::call_ceil(ctx, (arg_ty, arg), arg.get_type().into()))) }), ), Arc::new(RwLock::new({ @@ -1528,45 +1207,12 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let boolean = ctx.primitives.bool; - let int32 = ctx.primitives.int32; - let int64 = ctx.primitives.int64; - let uint32 = ctx.primitives.uint32; - let uint64 = ctx.primitives.uint64; - let float = ctx.primitives.float; let m_ty = fun.0.args[0].ty; let n_ty = fun.0.args[1].ty; let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?; let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?; - let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b); - if !is_type(m_ty, n_ty) { - unreachable!() - } - let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) { - llvm_intrinsics::call_int_umin( - ctx, - m_val.into_int_value(), - n_val.into_int_value(), - Some("min"), - ).into() - } else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) { - llvm_intrinsics::call_int_smin( - ctx, - m_val.into_int_value(), - n_val.into_int_value(), - Some("min"), - ).into() - } else if is_type(m_ty, n_ty) && is_type(n_ty, float) { - llvm_intrinsics::call_float_minnum( - ctx, - m_val.into_float_value(), - n_val.into_float_value(), - Some("min"), - ).into() - } else { - unreachable!() - }; - Ok(val.into()) + + Ok(Some(builtin_fns::call_min(ctx, (m_ty, m_val), (n_ty, n_val)))) }, )))), loc: None, @@ -1588,45 +1234,12 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let boolean = ctx.primitives.bool; - let int32 = ctx.primitives.int32; - let int64 = ctx.primitives.int64; - let uint32 = ctx.primitives.uint32; - let uint64 = ctx.primitives.uint64; - let float = ctx.primitives.float; let m_ty = fun.0.args[0].ty; let n_ty = fun.0.args[1].ty; let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?; let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?; - let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b); - if !is_type(m_ty, n_ty) { - unreachable!() - } - let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) { - llvm_intrinsics::call_int_umax( - ctx, - m_val.into_int_value(), - n_val.into_int_value(), - Some("max"), - ).into() - } else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) { - llvm_intrinsics::call_int_smax( - ctx, - m_val.into_int_value(), - n_val.into_int_value(), - Some("max"), - ).into() - } else if is_type(m_ty, n_ty) && is_type(n_ty, float) { - llvm_intrinsics::call_float_maxnum( - ctx, - m_val.into_float_value(), - n_val.into_float_value(), - Some("max"), - ).into() - } else { - unreachable!() - }; - Ok(val.into()) + + Ok(Some(builtin_fns::call_max(ctx, (m_ty, m_val), (n_ty, n_val)))) }, )))), loc: None, @@ -1645,35 +1258,10 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, _, fun, args, generator| { - let boolean = ctx.primitives.bool; - let int32 = ctx.primitives.int32; - let int64 = ctx.primitives.int64; - let uint32 = ctx.primitives.uint32; - let uint64 = ctx.primitives.uint64; - let float = ctx.primitives.float; - let llvm_i1 = ctx.ctx.bool_type(); let n_ty = fun.0.args[0].ty; let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator, n_ty)?; - let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b); - let val: BasicValueEnum = if [boolean, uint32, uint64].iter().any(|t| is_type(n_ty, *t)) { - n_val - } else if [int32, int64].iter().any(|t| is_type(n_ty, *t)) { - llvm_intrinsics::call_int_abs( - ctx, - n_val.into_int_value(), - llvm_i1.const_zero(), - Some("abs"), - ).into() - } else if is_type(n_ty, float) { - llvm_intrinsics::call_float_fabs( - ctx, - n_val.into_float_value(), - Some("abs"), - ).into() - } else { - unreachable!() - }; - Ok(val.into()) + + Ok(Some(builtin_fns::call_abs(ctx, (n_ty, n_val)))) }, )))), loc: None, @@ -1685,17 +1273,12 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built boolean, &[(float, "x")], Box::new(|ctx, _, fun, args, generator| { - let float = ctx.primitives.float; - let x_ty = fun.0.args[0].ty; let x_val = args[0].1.clone() - .to_basic_value_enum(ctx, generator, x_ty)?; + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); - assert!(ctx.unifier.unioned(x_ty, float)); - - let val = call_isnan(generator, ctx, x_val.into_float_value()); - - Ok(Some(val.into())) + Ok(Some(builtin_fns::call_numpy_isnan(generator, ctx, (x_ty, x_val)).into())) }), ), create_fn_by_codegen( @@ -1705,224 +1288,373 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built boolean, &[(float, "x")], Box::new(|ctx, _, fun, args, generator| { - let float = ctx.primitives.float; - let x_ty = fun.0.args[0].ty; let x_val = args[0].1.clone() - .to_basic_value_enum(ctx, generator, x_ty)?; + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); - assert!(ctx.unifier.unioned(x_ty, float)); - - let val = call_isinf(generator, ctx, x_val.into_float_value()); - - Ok(Some(val.into())) + Ok(Some(builtin_fns::call_numpy_isinf(generator, ctx, (x_ty, x_val)).into())) }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_sin", float, &[(float, "x")], - "llvm.sin.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_sin(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_cos", float, &[(float, "x")], - "llvm.cos.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_cos(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_exp", float, &[(float, "x")], - "llvm.exp.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_exp(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_exp2", float, &[(float, "x")], - "llvm.exp2.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_exp2(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_log", float, &[(float, "x")], - "llvm.log.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_log(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_log10", float, &[(float, "x")], - "llvm.log10.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_log10(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_log2", float, &[(float, "x")], - "llvm.log2.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_log2(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_fabs", float, &[(float, "x")], - "llvm.fabs.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_fabs(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_sqrt", float, &[(float, "x")], - "llvm.sqrt.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_sqrt(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_rint", float, &[(float, "x")], - "llvm.roundeven.f64", + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_rint(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_tan", float, &[(float, "x")], - "tan", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_tan(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arcsin", float, &[(float, "x")], - "asin", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_arcsin(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arccos", float, &[(float, "x")], - "acos", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_arccos(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arctan", float, &[(float, "x")], - "atan", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_arctan(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_sinh", float, &[(float, "x")], - "sinh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_sinh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_cosh", float, &[(float, "x")], - "cosh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_cosh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_tanh", float, &[(float, "x")], - "tanh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_tanh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arcsinh", float, &[(float, "x")], - "asinh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_asinh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arccosh", float, &[(float, "x")], - "acosh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_acosh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arctanh", float, &[(float, "x")], - "atanh", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_atanh(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_expm1", float, &[(float, "x")], - "expm1", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_expm1(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_cbrt", float, &[(float, "x")], - "cbrt", - &["readnone", "willreturn"], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_cbrt(ctx, (x_ty, x_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "sp_spec_erf", float, &[(float, "z")], - "erf", - &[], + Box::new(|ctx, _, fun, args, generator| { + let z_ty = fun.0.args[0].ty; + let z_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, z_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_scipy_special_erf(ctx, (z_ty, z_val)).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "sp_spec_erfc", float, &[(float, "x")], - "erfc", - &[], + Box::new(|ctx, _, fun, args, generator| { + let z_ty = fun.0.args[0].ty; + let z_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, z_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_scipy_special_erfc(ctx, (z_ty, z_val)).into())) + }), ), create_fn_by_codegen( unifier, @@ -1931,17 +1663,14 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built float, &[(float, "z")], Box::new(|ctx, _, fun, args, generator| { - let float = ctx.primitives.float; - let z_ty = fun.0.args[0].ty; let z_val = args[0].1.clone() - .to_basic_value_enum(ctx, generator, z_ty)?; + .to_basic_value_enum(ctx, generator, z_ty)? + .into_float_value(); - assert!(ctx.unifier.unioned(z_ty, float)); - - Ok(Some(call_gamma(ctx, z_val.into_float_value()).into())) - } - )), + Ok(Some(builtin_fns::call_scipy_special_gamma(ctx, (z_ty, z_val)).into())) + }), + ), create_fn_by_codegen( unifier, &var_map, @@ -1949,15 +1678,12 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built float, &[(float, "x")], Box::new(|ctx, _, fun, args, generator| { - let float = ctx.primitives.float; + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); - let z_ty = fun.0.args[0].ty; - let z_val = args[0].1.clone() - .to_basic_value_enum(ctx, generator, z_ty)?; - - assert!(ctx.unifier.unioned(z_ty, float)); - - Ok(Some(call_gammaln(ctx, z_val.into_float_value()).into())) + Ok(Some(builtin_fns::call_scipy_special_gammaln(ctx, (x_ty, x_val)).into())) }), ), create_fn_by_codegen( @@ -1967,86 +1693,190 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built float, &[(float, "x")], Box::new(|ctx, _, fun, args, generator| { - let float = ctx.primitives.float; - let z_ty = fun.0.args[0].ty; let z_val = args[0].1.clone() - .to_basic_value_enum(ctx, generator, z_ty)?; + .to_basic_value_enum(ctx, generator, z_ty)? + .into_float_value(); - assert!(ctx.unifier.unioned(z_ty, float)); - - Ok(Some(call_j0(ctx, z_val.into_float_value()).into())) + Ok(Some(builtin_fns::call_scipy_special_j0(ctx, (z_ty, z_val)).into())) }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "sp_spec_j1", float, &[(float, "x")], - "j1", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x_ty = fun.0.args[0].ty; + let x_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_scipy_special_j1(ctx, (x_ty, x_val)).into())) + }), ), // Not mapped: jv/yv, libm only supports integer orders. - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_arctan2", float, &[(float, "x1"), (float, "x2")], - "atan2", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_arctan2( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_copysign", float, &[(float, "x1"), (float, "x2")], - "llvm.copysign.f64", + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_copysign( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_fmax", float, &[(float, "x1"), (float, "x2")], - "llvm.maxnum.f64", + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_fmax( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_intrinsic( + create_fn_by_codegen( unifier, &var_map, "np_fmin", float, &[(float, "x1"), (float, "x2")], - "llvm.minnum.f64", + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_fmin( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_ldexp", float, &[(float, "x1"), (int32, "x2")], - "ldexp", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_int_value(); + + Ok(Some(builtin_fns::call_numpy_ldexp( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_hypot", float, &[(float, "x1"), (float, "x2")], - "hypot", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_hypot( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), - create_fn_by_extern( + create_fn_by_codegen( unifier, &var_map, "np_nextafter", float, &[(float, "x1"), (float, "x2")], - "nextafter", - &[], + Box::new(|ctx, _, fun, args, generator| { + let x1_ty = fun.0.args[0].ty; + let x1_val = args[0].1.clone() + .to_basic_value_enum(ctx, generator, x1_ty)? + .into_float_value(); + let x2_ty = fun.0.args[1].ty; + let x2_val = args[1].1.clone() + .to_basic_value_enum(ctx, generator, x2_ty)? + .into_float_value(); + + Ok(Some(builtin_fns::call_numpy_nextafter( + ctx, + (x1_ty, x1_val), + (x2_ty, x2_val), + ).into())) + }), ), Arc::new(RwLock::new(TopLevelDef::Function { name: "Some".into(),