In general, `InliningContext.findAnonymousTransformationInfo` was not
reliable because it mapped each type to *some* info for that type,
preferring ones with `shouldRegenerate == true` if those exist. Thus, it
returned incorrect results if one type was regenerated multiple times,
e.g. in a nested inlining context or because of a `finally` (which
duplicates anonymous objects). The solution is to avoid a global map and
attach the current transformation info directly to the current inlining
context.
Otherwise, the assumption that coroutine codegen makes about every
inlined function already having the markers breaks and it is no longer
true that calls to inline lambdas do not require them.
This is one example of a function replaced by a lowering after
AddContinuationLowering mentioned in the last commit: the lowering that
makes default stubs static is further down. Although this is not a
lambda, calling getOrCreateSuspendFunctionViewIfNeeded on it in the
inliner is fatal all the same.
Private $$forInline versions of public suspend functions should not
be mangled. (Note that there are no $$forInline versions of private
suspend functions, as those are effectively inline-only.)
* unify various checks for whether a suspend function needs a
continuation;
* mark the continuation parameter with an origin (this also allows
correctly computing the offset of the local in codegen -- see the
new test);
* when wrapping `suspend fun main`, use a suspend reference instead of
a synthetic non-suspend lambda (required to correctly implement the
previous point, as previously the continuation parameter was passed
to main() itself correctly only because of very lenient checks in
AddContinuationLowering).
* TailCallOptimizationLowering should go into local classes in order to
transform their suspend methods;
* the check for invokes of noinline lambda arguments in codegen was
incorrect, as it also returned true for calls of lambdas stored in
local variables;
* IrInlineCodegen should mark non-inlinable arguments used as inline
suspend parameters;
* detection of suspend/inline call sites was incorrect (or maybe it's
the `compilationContextDescriptor` that was incorrect?..)
parameters of inline function. Otherwise, it leads to AnalyzerException,
when inlined lambda contains try-catch block. The reason is simple:
in try block, we leave some variables on stack, while on catch block the
stack is empty. Spilling the variables before try block does the trick.
#KT-34708 Fixed
Now AddContinuationLowering is responsible for both adding continuation
classes to suspend functions and adding continuation parameters to
them.
Because we cannot create a view if inline suspend function is defined
in another file, we generate a stub without body when we encounter call
to it. And then, when we lower the file containing the function we add
the body. This way we have no unlowered views after the lowering.
Thus, after the lowering there should be no suspend function, which
are not views, therefore, remove VIEW origins.
Because transformations of suspend functions can copy them into another
object, use attribute as a key inside function to view map.
Since the markers replace ALOAD 0 as continuations, passed to suspend calls, in
JVM_IR we do not need this, since in JVM_IR all inline lambdas are static
functions.
The main idea is the following: since we need to generate
(fake)continuations before inlining, we move IrClasses of suspend
lambdas and continuation classes of named functions into the functions.
Thus, it allows the codegen to generate them prior to inlining and
the inliner will happily transform them for us.
Because of that, lowerings which transform call-site function are likely
to change reference to lowered suspend lambdas or functions.
Hence, do not rely on references to lowered suspend lambdas or
functions, instead, rely on attributes.
Do not generate continuation for inline suspend lambdas.
Previously, inline suspend lambdas were treated like suspend functions,
thus we generated continuations for them. Now we just do not treat them
as suspend functions or lambdas during AddContinuationLowering.
We should add continuation parameter to them, however.
Do not generate secondary constructor for suspend lambdas, otherwise,
the inliner is unable to transform them (it requires only one
constructor to be present).
Generate continuation classes for suspend functions as first statement
inside the function.
This enables suspend functions in local object inside inline functions.
Since we already have attributes inside suspend named functions, we
just reuse them to generate continuation class names. This allows us
to close the gap between code generated by old back-end and the new
one.
If a suspend named function captures crossinline lambda, we should
generate a template for inliner: a copy of the function without
state-machine and a continuation constructor call. The call is needed
so the inliner transforms the continuation as well.
Refactor CoroutineTransformerMethodVisitor, so it no longer depends on
PSI.
Previously it was linear scan, failing on unbalanced suspension markers.
Now, I use CFG to find end markers, which are reachable from start
markers. Using CFG allows to walk through suspension point instructions
only, since they form region.
If, for some reason, end marker does not exist (inliner or unreachable
code elimination pass remove unreachable code) or is unreachable,
just ignore the whole suspension point, as before.
#KT-33172 Fixed
#KT-28507 Fixed
Unlike previously, this optimisation works on every callee return type.
Tail-calls inside unit functions can be either
INVOKE...
ARETURN
or
INVOKE
POP
GETSTATIC kotlin/Unit.INSTANCE
ARETURN
The first pattern is already covered. The second one is a bit tricky,
since we cannot just assume than the function is tail-call, we also need
to check whether the callee returned COROUTINE_SUSPENDED marker.
Thus, resulting bytecode of function's 'epilogue' look like
DUP
INVOKESTATIC getCOROUTINE_SUSPENDED
IF_ACMPNE LN
ARETURN
LN:
POP
#KT-28938 Fixed
in OUTERCLASS field.
The inliner generates two versions of suspend functions/lambdas in
inline functions: with state-machine and without. The former is used
to call the function from Java or via reflection and have ordinary
name, while the latter is used by inliner and have $$forInline suffix.
The inliner throws the state-machine version away, duplicates
$$forInline version and then call state-machine generator.
If these suspend functions/lambdas are not going to be inlined,
$$forInline version is not generated. However, all objects, which are
used in these suspend functions/lambdas, have $$forInline version
written to OUTERCLASS field. This leads to errors by proguard.
Since they are used in both state-machine version and for-inline ones,
we can simply remove $$forInline suffix from OUTERCLASS field and this
fixes the issue.
#KT-31242 Fixed
if they are not inlined, but directly called.
Previously, all inline and crossinline lambda calls were treated by
codegen as if they are always going to be inlined. However, this is not
always the case.
Note, that we cannot generate these markers during codegen, since we
can inline code with no suspension points, but the whole inlined code
will become one giant suspension point. This, of course, breaks
tail-call optimization and, hence, slows down cold streams.
Because of that, we generate these markers, when we are sure, that they
are not going to be inlined. The only place, in which we know that, is
the inliner. During inlining of the inline function, we check, whether
the parameter is inline or crossinline and whether it is not an inline
lambda. If these checks pass, we generate the markers. Noinline
parameters are already covered by the codegen.
#KT-30706 Fixed
#KT-26925 Fixed
#KT-26418 Fixed
These tests were added for suspend-conversions, it worked only
with new inference, but implementation was incorrect and had other
bugs, which were fixed in 1ac25259.
Support of suspend-conversions will be addressed later with a different
implementation (#KT-30703)
After cold stream related fixes, we do not generate state machine until
the very last transformation of the lambda. Thus, it is safe to
generate debug metadata for that lambda.
#KT-30694 Fixed