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

RFC: specialized calling convention for pure functions that return a constant #16837

Merged
merged 1 commit into from
Jun 30, 2016

Conversation

JeffBezanson
Copy link
Sponsor Member

This allows deleting their code and avoiding native code gen entirely. This happens a fair amount due to functions like promote_type, zero, trait functions, etc. It's about 8% of all inferred functions in the system image. However since such functions tend to be very small, the gains are not as much as that number might imply.

@andyferris
Copy link
Member

Hi Jeff,

Just a general question as to the design of constant propagation and where this is going - are you planning on allowing constant propagation and inlining during inference, here, or just afterwards? I see the post-inference optimizations are very useful, but sometimes these calculations affect type.

For instance, a simple example that I would love to see be inferrable:

addval{M,N}(::Type{Val{M}}, ::Type{Val{N}}) = Val{M+N}

If this constant is inlined post-inference, then can its type parameter be used for later calculations? e.g.

function foo()
    a = Val{2}
    b = Val{3}
    c = Val{4}
    d = addval(a,b)
    return addval(d,c)
end

(Where I really want to end up with is:

@inline Base.getindex{Idx}(table::Table{Idx}, field::Symbol) = Table.data[find_index(Idx, field)]

where Idx is a tuple of symbols and table.data is a tuple of data and field is written by the user as a literal, so they can extract column :a with table[:a]. I can't get any combination of @pure, @inline and @generated to do the compile-time processing that I want (the user needs to type field as Val{:a} instead of :a). The problem with this last one is that table is not constant, and I can't get getindex() to inline during inference to make a call to find_index(Idx, field), which is a pure function of constants... )

@JeffBezanson
Copy link
Sponsor Member Author

This change is actually orthogonal to inference and constant propagation. It just says that if we can infer the result of a pure function to be a constant, then we don't need to generate native code for it or keep its IR around.

Separately, we do hope to improve constant propagation over time. I think Val{M+N} will definitely be inferred sooner or later. Unfortunately the case of table[:a] is especially difficult, since it is extremely expensive to analyze functions for all argument values, and it's hard to know when it will be profitable to do so. We might in fact need a new feature for specialization and/or inference on values.

@andyferris
Copy link
Member

OK, thanks, I get it now - this is an internal optimization for things like pure functions that only depend on the types of their inputs.

Separately, we do hope to improve constant propagation over time. I think Val{M+N} will definitely be inferred sooner or later.

Cool, I'm really looking forward to this. :)

Unfortunately the case of table[:a] is especially difficult, since it is extremely expensive to analyze functions for all argument values, and it's hard to know when it will be profitable to do so. We might in fact need a new feature for specialization and/or inference on values.

Yes, I realize it is difficult (expensive, rather) to analyze based on value. I was more saying that if my @inline in the above explicitly copy-pasted the IR from the function to the destination during the inference phase, then inference would run over find_index(Idx, field) and realize it is a pure function of constants before it converged. As I understand it (please correct me if I'm wrong), most inlining happens post-inference but pre-codegen, currently.

@@ -1916,11 +1924,36 @@ function finish(me::InferenceState)
end
widen_all_consts!(me.linfo)

if (isa(me.bestguess,Const) && me.bestguess.val !== nothing) ||
(isType(me.bestguess) && !has_typevars(me.bestguess.parameters[1],true))
if !ispure && length(me.linfo.code) < 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is 10 just an arbitrarily chosen cutoff for this?

return true
end
if e.head === :static_parameter
if head === :static_parameter || head === :meta || head === :line || head === :gotoifnot ||
head === :inbounds || head === :boundscheck
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:gotoifnot isn't effect-free

@JeffBezanson
Copy link
Sponsor Member Author

Fixed.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jun 14, 2016

LabelNode too?

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jun 30, 2016

lgtm. just needs a rebase

this allows deleting their code and avoiding native code gen entirely
@JeffBezanson JeffBezanson reopened this Jun 30, 2016
@JeffBezanson JeffBezanson merged commit a66ace6 into master Jun 30, 2016
@tkelman tkelman deleted the jb/const_call branch July 1, 2016 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants