When devirtualizing witness method and class method calls, we
transform apply instructions operating on the result of a SIL
witness_method or class_method instruction to direct calls of
a function_ref.
The generic signature of the dynamic call site might not match
the generic signature of the static thunk, so the substitution
list from the dynamic apply instruction cannot be used directly;
instead, we must transform it to a substitution list suitable
for the static thunk.
- With witness methods, the method is called using the protocol
requirement's signature, <Self : P, ...>, however the
witness thunk has a generic signature derived from the
concrete witness.
For example, the requirement might have a signature
<Self : P, T>, where the concrete witness thunk might
have a signature <X, Y>, where the concrete conforming type
is G<X, Y>.
At the call site, we substitute Self := G<X', Y'>; however
to be able to call the witness thunk directly, we need to
form substitutions X := X' and Y := Y'.
- A similar situation occurs with class methods when the
dynamically-dispatched call is performed against a derived
class, but devirtualization actually finds the method on a
base class of the derived class.
The base class may have a different number of generic
parameters than the derived class, either because the
derived class makes some generic parameters of the base
class concrete, or if the derived class introduces new
generic parameters of its own.
In both cases, we need to consider the generic signature of the
dynamic call site (the protocol requirement or the derived
class method) as well as the generic signature of the static
thunk, and carefully remap the substitutions from one form
into another.
Previously the optimizer would implicitly rely on substitutions
being in AllArchetypes order, in particular that concatenating
outer substitutions with inner substitutions makes sense.
This assumption is about to go away, so this patch refactors
the optimizer to use some new abstractions for remapping
substitution lists.
Shaves about 19% of the time from the construction of these sets. The
SmallVector size was chosen to minimize the number of dynamic
allocations we end up doing while building the stdlib. This should be a
reasonable size for most projects, too. It's a bit wasteful in space,
but the total amount of allocated space here is pretty small to begin
with.
(libraries now)
It has been generally agreed that we need to do this reorg, and now
seems like the perfect time. Some major pass reorganization is in the
works.
This does not have to be the final word on the matter. The consensus
among those working on the code is that it's much better than what we
had and a better starting point for future bike shedding.
Note that the previous organization was designed to allow separate
analysis and optimization libraries. It turns out this is an
artificial distinction and not an important goal.