JVM IR: Make inlining in $default stubs compatible with the JVM BE
The JVM BE inlines calls to the underlying function in a $default stub verbatim, e.g., without renaming LVT entries or regenerating anonymous objects. This commit introduces the same behavior in the JVM IR BE. Fixes KT-36769.
This commit is contained in:
committed by
max-kammerer
parent
bef0437edb
commit
5e27d9b089
@@ -133,14 +133,13 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
mapDefaultSignature: Boolean,
|
||||
typeSystem: TypeSystemCommonBackendContext,
|
||||
registerLineNumberAfterwards: Boolean,
|
||||
isCallOfFunctionInCorrespondingDefaultDispatch: Boolean
|
||||
) {
|
||||
var nodeAndSmap: SMAPAndMethodNode? = null
|
||||
try {
|
||||
nodeAndSmap = createInlineMethodNode(
|
||||
functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler
|
||||
)
|
||||
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas, isCallOfFunctionInCorrespondingDefaultDispatch), registerLineNumberAfterwards)
|
||||
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas), registerLineNumberAfterwards)
|
||||
} catch (e: CompilationException) {
|
||||
throw e
|
||||
} catch (e: InlineException) {
|
||||
@@ -206,7 +205,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
?: error("No stack value for continuation parameter of suspend function")
|
||||
}
|
||||
|
||||
protected fun inlineCall(nodeAndSmap: SMAPAndMethodNode, inlineDefaultLambda: Boolean, isCallOfFunctionInCorrespondingDefaultDispatch: Boolean): InlineResult {
|
||||
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, inlineDefaultLambda: Boolean): InlineResult {
|
||||
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
|
||||
val node = nodeAndSmap.node
|
||||
if (inlineDefaultLambda) {
|
||||
@@ -234,7 +233,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
val inliner = MethodInliner(
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + sourceCompiler.callElementText,
|
||||
SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP, callSite.takeIf { !isCallOfFunctionInCorrespondingDefaultDispatch }),
|
||||
SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP, callSite),
|
||||
info.callSiteInfo, if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
!isInlinedToInlineFunInKotlinRuntime()
|
||||
) //with captured
|
||||
|
||||
@@ -64,7 +64,7 @@ class PsiInlineCodegen(
|
||||
}
|
||||
try {
|
||||
val registerLineNumber = registerLineNumberAfterwards(resolvedCall)
|
||||
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem, registerLineNumber, false)
|
||||
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem, registerLineNumber)
|
||||
} finally {
|
||||
state.globalInlineContext.exitFromInlining()
|
||||
}
|
||||
|
||||
+6
-1
@@ -1168,11 +1168,16 @@ class ExpressionCodegen(
|
||||
element: IrFunctionAccessExpression, data: BlockInfo, signature: JvmMethodSignature
|
||||
): IrCallGenerator {
|
||||
if (!element.symbol.owner.isInlineFunctionCall(context) ||
|
||||
classCodegen.irClass.fileParent.fileEntry is MultifileFacadeFileEntry
|
||||
classCodegen.irClass.fileParent.fileEntry is MultifileFacadeFileEntry ||
|
||||
irFunction.isInvokeSuspendOfContinuation()
|
||||
) {
|
||||
return IrCallGenerator.DefaultCallGenerator
|
||||
}
|
||||
|
||||
if (irFunction == context.mapping.defaultArgumentsDispatchFunction[element.symbol.owner]) {
|
||||
return IrInlineDefaultCodegen
|
||||
}
|
||||
|
||||
val callee = element.symbol.owner
|
||||
val typeArgumentContainer = if (callee is IrConstructor) callee.parentAsClass else callee
|
||||
val typeArguments =
|
||||
|
||||
-9
@@ -72,14 +72,6 @@ class IrInlineCodegen(
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
if (codegen.irFunction.isInvokeSuspendOfContinuation()) {
|
||||
// In order to support java interop of inline suspend functions, we generate continuations for these inline suspend functions.
|
||||
// These functions should behave as ordinary suspend functions, i.e. we should not inline the content of the inline function
|
||||
// into continuation.
|
||||
// Thus, we should put its arguments to stack.
|
||||
super.genValueAndPut(irValueParameter, argumentExpression, parameterType, codegen, blockInfo)
|
||||
}
|
||||
|
||||
val isInlineParameter = irValueParameter.isInlineParameter()
|
||||
if (isInlineParameter && isInlineIrExpression(argumentExpression)) {
|
||||
val irReference: IrFunctionReference =
|
||||
@@ -165,7 +157,6 @@ class IrInlineCodegen(
|
||||
false,
|
||||
codegen.typeMapper.typeSystem,
|
||||
registerLineNumberAfterwards = isInsideIfCondition,
|
||||
isCallOfFunctionInCorrespondingDefaultDispatch = codegen.irFunction == codegen.context.mapping.defaultArgumentsDispatchFunction[function]
|
||||
)
|
||||
} finally {
|
||||
state.globalInlineContext.exitFromInlining()
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.MethodBodyVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.SourceMapCopier
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
/**
|
||||
* A specialization of IrInlineCodegen for calls to the underlying method in a $default handler.
|
||||
* Such calls are inlined verbatim in the JVM backend (see InlineCodegenForDefaultBody.kt).
|
||||
* For compatibility we have to do the same thing in the JVM IR backend.
|
||||
*/
|
||||
object IrInlineDefaultCodegen : IrCallGenerator {
|
||||
override fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
// This codegen is only used for calls to the underlying function in a $default stub.
|
||||
// For such calls we know that we are passing along the value parameters and reusing the same indices.
|
||||
// There is no need to generate any code.
|
||||
assert(argumentExpression is IrGetValue || argumentExpression is IrTypeOperatorCall && argumentExpression.argument is IrGetValue)
|
||||
}
|
||||
|
||||
override fun genCall(callableMethod: IrCallableMethod, codegen: ExpressionCodegen, expression: IrFunctionAccessExpression) {
|
||||
val function = expression.symbol.owner
|
||||
val nodeAndSmap = codegen.classCodegen.generateMethodNode(function, codegen.delegatedPropertyOptimizer)
|
||||
val childSourceMapper = SourceMapCopier(codegen.smap, nodeAndSmap.classSMAP)
|
||||
|
||||
val argsSize =
|
||||
(Type.getArgumentsAndReturnSizes(callableMethod.asmMethod.descriptor) ushr 2) - if (function.isStatic) 1 else 0
|
||||
nodeAndSmap.node.accept(object : MethodBodyVisitor(codegen.visitor) {
|
||||
override fun visitLocalVariable(name: String, desc: String, signature: String?, start: Label, end: Label, index: Int) {
|
||||
// We only copy LVT entries for local variables, since we already generated entries for the method parameters,
|
||||
if (index >= argsSize) super.visitLocalVariable(name, desc, signature, start, end, index)
|
||||
}
|
||||
|
||||
override fun visitLineNumber(line: Int, start: Label?) {
|
||||
super.visitLineNumber(childSourceMapper.mapLineNumber(line), start)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36769 Missing LVT entries for inline function (default) parameters at call site
|
||||
|
||||
inline fun test(p: String = "OK"): String {
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@ fun box(): String {
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// JVM_IR steps on line 15 both on the way in and on the way out
|
||||
// of the massert method. This is consistent with what would
|
||||
// happen if massert was not inline and we used force-step-into
|
||||
// to step through the $default method. JVM only hits line 15 on
|
||||
// the way in.
|
||||
|
||||
// LINENUMBERS
|
||||
// test.kt:24 box
|
||||
// test.kt:15 box
|
||||
@@ -43,9 +37,6 @@ fun box(): String {
|
||||
// test.kt:16 box
|
||||
// test.kt:17 box
|
||||
// test.kt:21 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:15 box
|
||||
// LINENUMBERS
|
||||
// test.kt:25 box
|
||||
// test.kt:6 box
|
||||
// test.kt:3 getMASSERTIONS_ENABLED
|
||||
|
||||
@@ -11,11 +11,6 @@ fun foo(i: Int = 1) {
|
||||
inline fun bar(i: Int = 1) {
|
||||
}
|
||||
|
||||
// The JVM_IR backend has line number 11 for the inlined
|
||||
// default argument handling both before and after the actual
|
||||
// body of bar. That is consistent with what happens with the
|
||||
// $default method in the non-inlined case.
|
||||
|
||||
// FORCE_STEP_INTO
|
||||
// LINENUMBERS
|
||||
// test.kt:4 box
|
||||
@@ -25,7 +20,4 @@ inline fun bar(i: Int = 1) {
|
||||
// test.kt:5 box
|
||||
// test.kt:11 box
|
||||
// test.kt:12 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:11 box
|
||||
// LINENUMBERS
|
||||
// test.kt:6 box
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
// FILE: test.kt
|
||||
inline fun foo(stringMaker: () -> String = { "OK" }): String {
|
||||
return stringMaker()
|
||||
@@ -17,19 +17,6 @@ fun box(): String {
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// The IR Backend does the following:
|
||||
// test.kt:15
|
||||
// test.kt:3
|
||||
// test.kt:4
|
||||
// test.kt:3
|
||||
// test.kt:3 <---
|
||||
// test.kt:16
|
||||
// test.kt:7
|
||||
// test.kt:11
|
||||
// test.kt:8
|
||||
// test.kt:7 <---
|
||||
// test.kt:17
|
||||
|
||||
// LINENUMBERS
|
||||
// test.kt:15 box
|
||||
// test.kt:3 box
|
||||
|
||||
@@ -13,18 +13,11 @@ fun box(): String {
|
||||
return ifoo2()
|
||||
}
|
||||
|
||||
// JVM_IR backend has the same stepping behavior as
|
||||
// the non-inlined case when using force step into to step
|
||||
// through $default method. JVM does not.
|
||||
|
||||
// FORCE_STEP_INTO
|
||||
// LINENUMBERS
|
||||
// test.kt:12 box
|
||||
// test.kt:3 box
|
||||
// test.kt:4 box
|
||||
// LINENUMBERS JVM_IR
|
||||
// test.kt:3 box
|
||||
// LINENUMBERS
|
||||
// test.kt:13 box
|
||||
// test.kt:7 ifoo2$default (synthetic)
|
||||
// test.kt:8 ifoo2
|
||||
|
||||
Reference in New Issue
Block a user