Open willtebbutt opened 1 month ago
invoke
, and hit a method where you might want to apply an rrule!!
, then it will be missed. This should not cause correctness issues, but may cause some calls to invoke
not to work. On the basis that invoke
usage is fairly uncommon, I'm not too worried about this.Completely resolving this problem looks likely to be a bit more work, and to be a bit of a pain. This is because the compiler "lowers" invoke
calls to :invoke
expressions prior to hitting inlining. For example,
Expr(:call, invoke, f, TT, args...)
gets converted to
Expr(:invoke, a_method_instance, f, args...)
This is a problem because we lose access to TT
, which is needed to assess whether or not somthing is a primitive.
In order to resolve this, I believe it will be necessary to customise the compilation pipeline earlier on. This is moderately straightforward, but I'm inclined to leave it until we find it to be a problem in practice, given that there are other more pressing things to deal with.
Just out of curiosity, would invokelatest
create extra trouble beyond invoke
?
Almost certainly -- I've not looked in to exactly what happens yet, but I suspect our best bet will be to provide some kind of informative error if someone attempts to AD through invokelatest
.
Didn't mean to close this.
invoke
is an interesting built-in function. I came across a use of it when debugging a PR linked to #197 .invoke
recapRecall that invoke behaves as follows:
invoke
lets us call whichever method of a function applies to the types provided in the second argument.There is, in principle, no particular performance penalty associated to this, it just changes which
Method
offoo
is specialised toFloat64
. In this case, aMethodInstance
is produced for theMethod
returned bywhich is specialised for
Float64
argument types.The IR generated for a call to invoke varies depending on a few different factors. I believe that all possible cases are considered below:
1. Statically-Resolvable Types + Inlinable
If an
invoke
call appears inside another function the compiler is at liberty to entirely inline it away. For example, the optimised IR associated tobar
is2. Statically-Resolvable Types + Not Inlinable
In instances where the compiler is able to resolve the
MethodInstance
statically, but not inline the call away, it will produce an:invoke
Expr
(not to be confused with the functioninvoke
). For exampleInterestingly, one cannot tell from looking at the displayed IR that the call to
invoke
was there -- i.e. the print-out of the IR is the same as that which would have been generated if the definition ofbaz
wereThe difference, however, is most certainly there:
By looking at the
def
field of theMethodInstance
associated to the:invoke
Expr
, we see that it has specialised thefoo(x::Any)
method offoo
.3. Dynamically-Resolved Types
If the types are not known to the invoke call statically, then a
:call
toinvoke
(the built-in) appears in the IR. For example:In this case it is plain to see what is going on from the
Main.invoke
:call
expression near the end of the IR.Differentiating
invoke
invoke
complicates the AD story only slightly. We address each of the above cases in turn:1. Statically-Resolvable Types + Inlinable
In this case, we need to avoid inlining if there is a method of
rrule!!
which is applicable to the set of types provided toinvoke
. If this is not the case, then we can safetly inline.2. Statically-Resolvable Types + Not Inlinable
Derive a rule as usual, but the code which we look up must be the code based on the types provided, rather than the values of the arguments. If there's an applicable method of
rrule!!
, use that, otherwise derive the rule.3. Dynamically-Resolved Types
Basically the same as 2.
Implementation Strategy
I think the way to do this is to implement a method of
is_primitive
which callsis_primitive
on the function and the types provided to invoke. Something like(this code won't run as written, but it should illustrate what I mean).
We then need to modify the implementation of
build_rrule
to keep track of both the types which identify the method that we specialise, and the types of the arguments that we pass to the specialised method. At present, they are always the same thing.Summary
Supporting
invoke
is a bit of work, but should ultimately be fairly straightforward if the above is all correct.