changed a year ago
Published Linked with GitHub

Thoughts on generalized method specialization

Motivation

There are a few work streams that are looking to do more things with method
specialization. Of the top of my head, these are:

  • #52233, wanting better participation
    of external AbstractInterpreters in precompile caching

  • #50641, wanting the ability to specialize
    methods on speculative assumptions that are checked at a higher level

  • The ability to codegen the result of const-proped IR that is not inlined (for external
    absints, or potentially for use by base with appropriate heuristics).

  • The ability to specialize (or not) a method on whether or not the return value is used.

  • The ability to specialize a method on the used-ness of its arguments or not.

  • Various fancier specializations in external packages

Separating the specialization axes

As I've thought about this more, I think a useful distinction to draw is whether the specialization
is of the code itself (likely for performance) or is a specialization of other code (e.g. the caller) based
on the properties of the method. In particular, the former is independent of any inference results about
the method, world ages, etc and may always be generated for a particular method. Said, another way,
it is always possible to say:

​"Give me a specialization of this method for arguments f(::Int)"

but not

​"Give me the specialization of this method that always returns Int and is nothrow".

For convenience, i'll call these two modes extrinsic and intrinsic specializations, but the terms are
certainly up for debate.

As applied to our current codebase, this distinction is reasonably manifest in the separation between
MethodInstance and CodeInstance, where the former represents the extrinsic specialization axis
(specTypes) and the latter represents the intrinsic specialization axis (inferred return type, effects,
escape information etc).

Another useful way to think about this is that the former is not subject to world age invalidation, while
the latter is.

As applied to the other possible specialization types listed in the first section, I think this would imply:

​- External codegen, return-usedness, more-precise-than-type specializations are all extrinsic
​- Argument used-ness and some of the external specialization requests are intrinsic

Unfortunately, effect preconditions (#50641) don't fit particularly neatly in this framework. On the surface,
they are extrinsic predicates (may I assume that this function throws or not?), but they are based on intrinsic
predicate assumptions (under precisely what conditions may I make this assumption). I think this means that
for now they need to be treated as intrinsic, but it's possible we may need further separation.

Implications

If we take this framework seriously, I think there's a few implications:

  1. Expr(:invoke) should quite possibly take CodeInstances not MethodInstances. In particular, an Expr(:invoke)
    implies that we have made use of intrinsic specialization information in the caller. Right now, with only the
    type-based, specialization axis, it's reasonably easy to tell what is being invoked, but if we add additional specialization,
    this might become harder to track.

  2. #52233 should possibly be partioning MethodInstances rather than code instances. I'm not really advocating for this to be
    done before merging that PR, so nothing here affects that PR directly, but I think this would be an implementation of the
    TODO noted in that PR (partioning the CodeInstance cache by owner).

  3. CodeInstance should probably gain a specsig signature field that is independent of the MethodInstance specTypes to enable
    intrinsic specializations that change the signature (e.g. argument usedness).

  4. The compilation state cache should possibly be split out of CodeInstance (or possibly the specialization part of CodeInstance should be split out into a separate MethodSpecialization object) as there may be a need for world-age partioned codegen along additional axes (because callees change in ways that to not affect the specialization).

  5. The additional intrinsic specialization requests (#50641 and external absint) would become extra fields in the CodeInstance that
    are ignored by other cache lookups (as already implemented in #50641).

Select a repo