diff --git a/base/essentials.jl b/base/essentials.jl index 34f993d22642f..d9298b897befd 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -113,6 +113,11 @@ macro _propagate_inbounds_meta() return Expr(:meta, :inline, :propagate_inbounds) end +# Alternative to `@nonans`. +macro _nonans_meta() + return Expr(:meta, :nonans) +end + """ convert(T, x) diff --git a/base/exports.jl b/base/exports.jl index 23b4141a06c5e..b7da6ef13552e 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -971,6 +971,7 @@ export @nospecialize, @specialize, @polly, + @nonans, @assert, @__dot__, diff --git a/base/expr.jl b/base/expr.jl index c6a02e4571692..302354aabeb99 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -256,6 +256,23 @@ macro polly(ex) esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex) end +""" + @nonans + +Allows the compiler to assume no NaNs on floating-point operations. + +```julia +@nonans function mathfunc(x) + #= + Function Definition + =# +end +``` +""" +macro nonans(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :nonans) : ex) +end + ## some macro utilities ## function pushmeta!(ex::Expr, sym::Symbol, args::Any...) diff --git a/src/ast.c b/src/ast.c index a2a4857a11466..23cd95b324796 100644 --- a/src/ast.c +++ b/src/ast.c @@ -62,6 +62,7 @@ jl_sym_t *throw_undef_if_not_sym; jl_sym_t *getfield_undefref_sym; jl_sym_t *gc_preserve_begin_sym; jl_sym_t *gc_preserve_end_sym; jl_sym_t *escape_sym; jl_sym_t *aliasscope_sym; jl_sym_t *popaliasscope_sym; +jl_sym_t *nonans_sym; static uint8_t flisp_system_image[] = { #include @@ -367,6 +368,7 @@ void jl_init_frontend(void) do_sym = jl_symbol("do"); aliasscope_sym = jl_symbol("aliasscope"); popaliasscope_sym = jl_symbol("popaliasscope"); + nonans_sym = jl_symbol("nonans"); } JL_DLLEXPORT void jl_lisp_prompt(void) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8f976241573cd..473a287940be6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5618,6 +5618,10 @@ static std::unique_ptr emit_function( f->addFnAttr(Attribute::NoInline); } + if (jl_has_meta(stmts, nonans_sym)) { + f->addFnAttr("no-nans-fp-math", "true"); + } + if (returninfo.cc == jl_returninfo_t::Union) { f->addAttribute(1, Attribute::getWithDereferenceableBytes(jl_LLVMContext, returninfo.union_bytes)); f->addAttribute(1, Attribute::getWithAlignment(jl_LLVMContext, returninfo.union_align)); diff --git a/src/julia_internal.h b/src/julia_internal.h index 7c690254316d7..5ac930a364bc8 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1053,6 +1053,7 @@ extern jl_sym_t *throw_undef_if_not_sym; extern jl_sym_t *getfield_undefref_sym; extern jl_sym_t *gc_preserve_begin_sym; extern jl_sym_t *gc_preserve_end_sym; extern jl_sym_t *failed_sym; extern jl_sym_t *done_sym; extern jl_sym_t *runnable_sym; extern jl_sym_t *escape_sym; +extern jl_sym_t *nonans_sym; struct _jl_sysimg_fptrs_t; diff --git a/test/llvmpasses/nonans.jl b/test/llvmpasses/nonans.jl new file mode 100644 index 0000000000000..f4bf281d191c8 --- /dev/null +++ b/test/llvmpasses/nonans.jl @@ -0,0 +1,21 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# RUN: julia --startup-file=no %s %t && llvm-link -S %t/* -o %t/module.ll +# RUN: cat %t/module.ll | FileCheck %s + +## Notes: +# This script uses the `emit` function (defined llvmpasses.jl) to emit either +# optimized or unoptimized LLVM IR. Each function is emitted individually and +# `llvm-link` is used to create a single module that can be passed to opt. +# The order in which files are emitted and linked is important since `lit` will +# process the test cases in order. + +include(joinpath("..", "testhelpers", "llvmpasses.jl")) + +# CHECK-LABEL: @julia_minimum_nonans +@nonans function minimum_nonans(itr) + return reduce(itr) do a, b ifelse(a < b, a, b) end +end + +# CHECK: attributes #{{[0-9]+}} = {{{[a-z "=]*}}"no-nans-fp-math"="true"{{[a-z "=]*}}} +emit(minimum_nonans, Vector{Float64})