Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimizer: support callsite annotations of @inline and @noinline #40754

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improved @noinline/@inline documentation and tests
  • Loading branch information
dghosef committed Jun 19, 2021
commit 48463248842fa73aea2f89a515cb53859ad5b05f
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ New language features
as `.&&` and `.||`. ([#39594])
* `⫪` (U+2AEA, `\Top`, `\downvDash`) and `⫫` (U+2AEB, `\Bot`, `\upvDash`, `\indep`)
may now be used as binary operators with comparison precedence. ([#39403])
* `@noinline` can now be used at function callsites and `@inline` and `@noinline` can be used in `do` blocks. ([#40754]).
* `@noinline` can now be used at function callsites. ([#40754])
* `@inline` and `@noinline` annotations may now be used in function bodies. ([#40754])

Language changes
----------------
Expand Down
14 changes: 4 additions & 10 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,8 @@ Give a hint to the compiler that this function is worth inlining.

Small functions typically do not need the `@inline` annotation,
as the compiler does it automatically. By using `@inline` on bigger functions,
an extra nudge can be given to the compiler to inline it. `@inline` can
only be used at function definitions.

If `@inline` is used at the beginning of a `do` block of a function
call, the `do` block will be marked for inlining.
an extra nudge can be given to the compiler to inline it.
`@inline` can be applied either in a function body or immediately before its definition.

This is shown in the following example:

Expand Down Expand Up @@ -228,11 +225,8 @@ By using `@noinline` on small functions, auto-inlining can be
prevented. `@noinline` can be used at function definitions,
function calls, and in `do` blocks.

If `@noinline` is used at the beginning of a `do` block of a function
call, the `do` block won't be inlined but the function itself still
could be. Similarily, if `@noinline` is specified immediately before a
function call with a `do` block, that function won't be inlined
but the `do` block body could possibly be.
`@noinline` can be applied either in a function body or
immediately before its definition.

This is shown in the following examples:

Expand Down
46 changes: 19 additions & 27 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -324,55 +324,47 @@ end
@inline f18773(x) = x
g18773(x) = @noinline f18773(x)
let ci = code_typed(g18773, Tuple{Int})[1].first
@test length(ci.code) == 2 &&
isexpr(ci.code[1], :invoke) &&
ci.code[1].args[1].def.name == :f18773
@test any(ci.code) do x
isexpr(x, :invoke) && x.args[1].def.name === :f18773
end
end
# Test that `@noinline` works across entire expression
h18773(x) = @noinline f18773(x) + f18773(x)
let ci = code_typed(h18773, Tuple{Int})[1].first
@test length(ci.code) == 4 &&
isexpr(ci.code[1], :invoke) &&
ci.code[1].args[1].def.name == :f18773 &&
isexpr(ci.code[2], :invoke) &&
ci.code[2].args[1].def.name == :f18773
@test count(ci.code) do x
isexpr(x, :invoke) && x.args[1].def.name === :f18773
end == 2
end

# Test inlining/noinlining of code within `do` blocks
@inline simple_caller(a) = a()
function do_inline(x)
simple_caller() do
@inline
# Tests `@inline`'s ability to override the lack of inline
# that these `println` statements would have caused
println(stdout, "Hello")
println(stdout, "World")
println(stdout, "Hello")
println(stdout, "World")
println(stdout, "Hello")
println(stdout, "World")
println(stdout, "Hello")
println(stdout, "World")
println(stdout, "Hello")
println(stdout, "World")
println(stdout, "Hello")
println(stdout, "World")
x
# this call won't be resolved and thus will prevent inlining to happen if we don't
# annotate `@inline` at the top of this anonymous function body
return unresolved_call(x)
end
end
let ci = code_typed(do_inline, Tuple{Int})[1].first
# A long body indicates inlining occurred
@test length(ci.code) == 25
# what we test here is that both `simple_caller` and the anonymous function that the
# `do` block creates are inlined away, and as a result there is only the unresolved call
@test all(ci.code) do x
!(isexpr(x, :invoke) && x.args[1].def.name === :simple_caller) &&
!(isexpr(x, :invoke) && startswith(string(x.args[1].def.name), '#'))
end
end
function do_noinline(x)
simple_caller() do
@noinline
x
end
end

let ci = code_typed(do_noinline, Tuple{Int})[1].first
@test length(ci.code) == 3 &&
isexpr(ci.code[2], :invoke)
@test any(ci.code) do x
isexpr(x, :invoke) && startswith(string(x.args[1].def.name), '#')
end
end

# Test that we can inline small constants even if they are not isbits
Expand Down