diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 3ddfa8871f..1de002498b 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -494,6 +494,7 @@ RUN(NAME expr_19 LABELS cpython llvm c) RUN(NAME expr_20 LABELS cpython llvm c) RUN(NAME expr_21 LABELS cpython llvm c) RUN(NAME expr_22 LABELS cpython llvm c) +RUN(NAME expr_23 LABELS cpython llvm c) RUN(NAME expr_01u LABELS cpython llvm c NOFAST) RUN(NAME expr_02u LABELS cpython llvm c NOFAST) diff --git a/integration_tests/expr_23.py b/integration_tests/expr_23.py new file mode 100644 index 0000000000..5aa5244d8a --- /dev/null +++ b/integration_tests/expr_23.py @@ -0,0 +1,23 @@ +from lpython import f32, i32 + +def flip_sign_check(): + x: f32 + eps: f32 = f32(1e-5) + + number: i32 = 123 + x = f32(5.5) + + if (number%2 == 1): + x = -x + + assert abs(x - f32(-5.5)) < eps + + number = 124 + x = f32(5.5) + + if (number%2 == 1): + x = -x + + assert abs(x - f32(5.5)) < eps + +flip_sign_check() diff --git a/src/libasr/pass/flip_sign.cpp b/src/libasr/pass/flip_sign.cpp index 64d901e2c8..695aa97a14 100644 --- a/src/libasr/pass/flip_sign.cpp +++ b/src/libasr/pass/flip_sign.cpp @@ -99,10 +99,10 @@ class FlipSignVisitor : public PassUtils::SkipOptimizationFunctionVisitor &args) { + int a = ASR::down_cast(args[0])->m_n; + double b = ASR::down_cast(args[1])->m_r; + if (a % 2 == 1) b = -b; + return make_ConstantWithType(make_RealConstant_t, b, t1, loc); + } + + static inline ASR::asr_t* create_FlipSign(Allocator& al, const Location& loc, + Vec& args, + const std::function err) { + if (args.size() != 2) { + err("Intrinsic FlipSign function accepts exactly 2 arguments", loc); + } + ASR::ttype_t *type1 = ASRUtils::expr_type(args[0]); + ASR::ttype_t *type2 = ASRUtils::expr_type(args[1]); + if (!ASRUtils::is_integer(*type1) || !ASRUtils::is_real(*type2)) { + err("Argument of the FlipSign function must be int and real respectively", + args[0]->base.loc); + } + ASR::expr_t *m_value = nullptr; + if (all_args_evaluated(args)) { + Vec arg_values; arg_values.reserve(al, 2); + arg_values.push_back(al, expr_value(args[0])); + arg_values.push_back(al, expr_value(args[1])); + m_value = eval_FlipSign(al, loc, expr_type(args[1]), arg_values); + } + return ASR::make_IntrinsicScalarFunction_t(al, loc, + static_cast(IntrinsicScalarFunctions::FlipSign), + args.p, args.n, 0, ASRUtils::expr_type(args[1]), m_value); + } + + static inline ASR::expr_t* instantiate_FlipSign(Allocator &al, const Location &loc, + SymbolTable *scope, Vec& arg_types, ASR::ttype_t *return_type, + Vec& new_args, int64_t /*overload_id*/) { + declare_basic_variables("_lcompilers_optimization_flipsign_" + type_to_str_python(arg_types[1])); + fill_func_arg("signal", arg_types[0]); + fill_func_arg("variable", arg_types[1]); + auto result = declare(fn_name, return_type, ReturnVar); + /* + real(real32) function flipsigni32r32(signal, variable) + integer(int32), intent(in) :: signal + real(real32), intent(out) :: variable + integer(int32) :: q + q = signal/2 + flipsigni32r32 = variable + if (signal - 2*q == 1 ) flipsigni32r32 = -variable + end subroutine + */ + + ASR::expr_t *two = i(2, arg_types[0]); + ASR::expr_t *q = iDiv(args[0], two); + ASR::expr_t *cond = iSub(args[0], iMul(two, q)); + body.push_back(al, b.If(iEq(cond, i(1, arg_types[0])), { + b.Assignment(result, f32_neg(args[1], arg_types[1])) + }, { + b.Assignment(result, args[1]) + })); + + ASR::symbol_t *f_sym = make_Function_t(fn_name, fn_symtab, dep, args, + body, result, Source, Implementation, nullptr); + scope->add_symbol(fn_name, f_sym); + return b.Call(f_sym, new_args, return_type, nullptr); + } + +} // namespace FlipSign + #define create_exp_macro(X, stdeval) \ namespace X { \ static inline ASR::expr_t* eval_##X(Allocator &al, const Location &loc, \ @@ -2368,6 +2450,8 @@ namespace IntrinsicScalarFunctionRegistry { {nullptr, &UnaryIntrinsicFunction::verify_args}}, {static_cast(IntrinsicScalarFunctions::FMA), {&FMA::instantiate_FMA, &FMA::verify_args}}, + {static_cast(IntrinsicScalarFunctions::FlipSign), + {&FlipSign::instantiate_FlipSign, &FMA::verify_args}}, {static_cast(IntrinsicScalarFunctions::Abs), {&Abs::instantiate_Abs, &Abs::verify_args}}, {static_cast(IntrinsicScalarFunctions::Partition), @@ -2456,6 +2540,8 @@ namespace IntrinsicScalarFunctionRegistry { "exp2"}, {static_cast(IntrinsicScalarFunctions::FMA), "fma"}, + {static_cast(IntrinsicScalarFunctions::FlipSign), + "flipsign"}, {static_cast(IntrinsicScalarFunctions::Expm1), "expm1"}, {static_cast(IntrinsicScalarFunctions::ListIndex), diff --git a/src/libasr/pass/pass_utils.cpp b/src/libasr/pass/pass_utils.cpp index 3cd474ddba..96a8a608a8 100644 --- a/src/libasr/pass/pass_utils.cpp +++ b/src/libasr/pass/pass_utils.cpp @@ -588,13 +588,16 @@ namespace LCompilers { } - ASR::stmt_t* get_flipsign(ASR::expr_t* arg0, ASR::expr_t* arg1, - Allocator& al, ASR::TranslationUnit_t& unit, - LCompilers::PassOptions& pass_options, - SymbolTable*& current_scope, - const std::function err) { - ASR::symbol_t *v = import_generic_procedure("flipsign", "lfortran_intrinsic_optimization", - al, unit, pass_options, current_scope, arg0->base.loc); + ASR::expr_t* get_flipsign(ASR::expr_t* arg0, ASR::expr_t* arg1, + Allocator& al, ASR::TranslationUnit_t& unit, const Location& loc){ + ASRUtils::impl_function instantiate_function = + ASRUtils::IntrinsicScalarFunctionRegistry::get_instantiate_function( + static_cast(ASRUtils::IntrinsicScalarFunctions::FlipSign)); + Vec arg_types; + ASR::ttype_t* type = ASRUtils::expr_type(arg1); + arg_types.reserve(al, 2); + arg_types.push_back(al, ASRUtils::expr_type(arg0)); + arg_types.push_back(al, ASRUtils::expr_type(arg1)); Vec args; args.reserve(al, 2); ASR::call_arg_t arg0_, arg1_; @@ -602,10 +605,8 @@ namespace LCompilers { args.push_back(al, arg0_); arg1_.loc = arg1->base.loc, arg1_.m_value = arg1; args.push_back(al, arg1_); - return ASRUtils::STMT( - ASRUtils::symbol_resolve_external_generic_procedure_without_eval( - arg0->base.loc, v, args, current_scope, al, - err)); + return instantiate_function(al, loc, + unit.m_global_scope, arg_types, type, args, 0); } ASR::expr_t* to_int32(ASR::expr_t* x, ASR::ttype_t* int64type, Allocator& al) { diff --git a/src/libasr/pass/pass_utils.h b/src/libasr/pass/pass_utils.h index c8bf786b99..ed9161bc7a 100644 --- a/src/libasr/pass/pass_utils.h +++ b/src/libasr/pass/pass_utils.h @@ -73,12 +73,8 @@ namespace LCompilers { ASR::expr_t* get_bound(ASR::expr_t* arr_expr, int dim, std::string bound, Allocator& al); - - ASR::stmt_t* get_flipsign(ASR::expr_t* arg0, ASR::expr_t* arg1, - Allocator& al, ASR::TranslationUnit_t& unit, - LCompilers::PassOptions& pass_options, - SymbolTable*& current_scope, - const std::function err); + ASR::expr_t* get_flipsign(ASR::expr_t* arg0, ASR::expr_t* arg1, + Allocator& al, ASR::TranslationUnit_t& unit, const Location& loc); ASR::expr_t* to_int32(ASR::expr_t* x, ASR::ttype_t* int32type, Allocator& al);