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,
|
mapDefaultSignature: Boolean,
|
||||||
typeSystem: TypeSystemCommonBackendContext,
|
typeSystem: TypeSystemCommonBackendContext,
|
||||||
registerLineNumberAfterwards: Boolean,
|
registerLineNumberAfterwards: Boolean,
|
||||||
isCallOfFunctionInCorrespondingDefaultDispatch: Boolean
|
|
||||||
) {
|
) {
|
||||||
var nodeAndSmap: SMAPAndMethodNode? = null
|
var nodeAndSmap: SMAPAndMethodNode? = null
|
||||||
try {
|
try {
|
||||||
nodeAndSmap = createInlineMethodNode(
|
nodeAndSmap = createInlineMethodNode(
|
||||||
functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler
|
functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler
|
||||||
)
|
)
|
||||||
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas, isCallOfFunctionInCorrespondingDefaultDispatch), registerLineNumberAfterwards)
|
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas), registerLineNumberAfterwards)
|
||||||
} catch (e: CompilationException) {
|
} catch (e: CompilationException) {
|
||||||
throw e
|
throw e
|
||||||
} catch (e: InlineException) {
|
} catch (e: InlineException) {
|
||||||
@@ -206,7 +205,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
?: error("No stack value for continuation parameter of suspend function")
|
?: 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)'" }
|
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
|
||||||
val node = nodeAndSmap.node
|
val node = nodeAndSmap.node
|
||||||
if (inlineDefaultLambda) {
|
if (inlineDefaultLambda) {
|
||||||
@@ -234,7 +233,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
val inliner = MethodInliner(
|
val inliner = MethodInliner(
|
||||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||||
"Method inlining " + sourceCompiler.callElementText,
|
"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,
|
info.callSiteInfo, if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||||
!isInlinedToInlineFunInKotlinRuntime()
|
!isInlinedToInlineFunInKotlinRuntime()
|
||||||
) //with captured
|
) //with captured
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class PsiInlineCodegen(
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val registerLineNumber = registerLineNumberAfterwards(resolvedCall)
|
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 {
|
} finally {
|
||||||
state.globalInlineContext.exitFromInlining()
|
state.globalInlineContext.exitFromInlining()
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -1168,11 +1168,16 @@ class ExpressionCodegen(
|
|||||||
element: IrFunctionAccessExpression, data: BlockInfo, signature: JvmMethodSignature
|
element: IrFunctionAccessExpression, data: BlockInfo, signature: JvmMethodSignature
|
||||||
): IrCallGenerator {
|
): IrCallGenerator {
|
||||||
if (!element.symbol.owner.isInlineFunctionCall(context) ||
|
if (!element.symbol.owner.isInlineFunctionCall(context) ||
|
||||||
classCodegen.irClass.fileParent.fileEntry is MultifileFacadeFileEntry
|
classCodegen.irClass.fileParent.fileEntry is MultifileFacadeFileEntry ||
|
||||||
|
irFunction.isInvokeSuspendOfContinuation()
|
||||||
) {
|
) {
|
||||||
return IrCallGenerator.DefaultCallGenerator
|
return IrCallGenerator.DefaultCallGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (irFunction == context.mapping.defaultArgumentsDispatchFunction[element.symbol.owner]) {
|
||||||
|
return IrInlineDefaultCodegen
|
||||||
|
}
|
||||||
|
|
||||||
val callee = element.symbol.owner
|
val callee = element.symbol.owner
|
||||||
val typeArgumentContainer = if (callee is IrConstructor) callee.parentAsClass else callee
|
val typeArgumentContainer = if (callee is IrConstructor) callee.parentAsClass else callee
|
||||||
val typeArguments =
|
val typeArguments =
|
||||||
|
|||||||
-9
@@ -72,14 +72,6 @@ class IrInlineCodegen(
|
|||||||
codegen: ExpressionCodegen,
|
codegen: ExpressionCodegen,
|
||||||
blockInfo: BlockInfo
|
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()
|
val isInlineParameter = irValueParameter.isInlineParameter()
|
||||||
if (isInlineParameter && isInlineIrExpression(argumentExpression)) {
|
if (isInlineParameter && isInlineIrExpression(argumentExpression)) {
|
||||||
val irReference: IrFunctionReference =
|
val irReference: IrFunctionReference =
|
||||||
@@ -165,7 +157,6 @@ class IrInlineCodegen(
|
|||||||
false,
|
false,
|
||||||
codegen.typeMapper.typeSystem,
|
codegen.typeMapper.typeSystem,
|
||||||
registerLineNumberAfterwards = isInsideIfCondition,
|
registerLineNumberAfterwards = isInsideIfCondition,
|
||||||
isCallOfFunctionInCorrespondingDefaultDispatch = codegen.irFunction == codegen.context.mapping.defaultArgumentsDispatchFunction[function]
|
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
state.globalInlineContext.exitFromInlining()
|
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 {
|
inline fun test(p: String = "OK"): String {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,6 @@ fun box(): String {
|
|||||||
return "OK"
|
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
|
// LINENUMBERS
|
||||||
// test.kt:24 box
|
// test.kt:24 box
|
||||||
// test.kt:15 box
|
// test.kt:15 box
|
||||||
@@ -43,9 +37,6 @@ fun box(): String {
|
|||||||
// test.kt:16 box
|
// test.kt:16 box
|
||||||
// test.kt:17 box
|
// test.kt:17 box
|
||||||
// test.kt:21 box
|
// test.kt:21 box
|
||||||
// LINENUMBERS JVM_IR
|
|
||||||
// test.kt:15 box
|
|
||||||
// LINENUMBERS
|
|
||||||
// test.kt:25 box
|
// test.kt:25 box
|
||||||
// test.kt:6 box
|
// test.kt:6 box
|
||||||
// test.kt:3 getMASSERTIONS_ENABLED
|
// test.kt:3 getMASSERTIONS_ENABLED
|
||||||
|
|||||||
@@ -11,11 +11,6 @@ fun foo(i: Int = 1) {
|
|||||||
inline fun bar(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
|
// FORCE_STEP_INTO
|
||||||
// LINENUMBERS
|
// LINENUMBERS
|
||||||
// test.kt:4 box
|
// test.kt:4 box
|
||||||
@@ -25,7 +20,4 @@ inline fun bar(i: Int = 1) {
|
|||||||
// test.kt:5 box
|
// test.kt:5 box
|
||||||
// test.kt:11 box
|
// test.kt:11 box
|
||||||
// test.kt:12 box
|
// test.kt:12 box
|
||||||
// LINENUMBERS JVM_IR
|
|
||||||
// test.kt:11 box
|
|
||||||
// LINENUMBERS
|
|
||||||
// test.kt:6 box
|
// test.kt:6 box
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: test.kt
|
// FILE: test.kt
|
||||||
inline fun foo(stringMaker: () -> String = { "OK" }): String {
|
inline fun foo(stringMaker: () -> String = { "OK" }): String {
|
||||||
return stringMaker()
|
return stringMaker()
|
||||||
@@ -17,19 +17,6 @@ fun box(): String {
|
|||||||
return "OK"
|
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
|
// LINENUMBERS
|
||||||
// test.kt:15 box
|
// test.kt:15 box
|
||||||
// test.kt:3 box
|
// test.kt:3 box
|
||||||
|
|||||||
@@ -13,18 +13,11 @@ fun box(): String {
|
|||||||
return ifoo2()
|
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
|
// FORCE_STEP_INTO
|
||||||
// LINENUMBERS
|
// LINENUMBERS
|
||||||
// test.kt:12 box
|
// test.kt:12 box
|
||||||
// test.kt:3 box
|
// test.kt:3 box
|
||||||
// test.kt:4 box
|
// test.kt:4 box
|
||||||
// LINENUMBERS JVM_IR
|
|
||||||
// test.kt:3 box
|
|
||||||
// LINENUMBERS
|
|
||||||
// test.kt:13 box
|
// test.kt:13 box
|
||||||
// test.kt:7 ifoo2$default (synthetic)
|
// test.kt:7 ifoo2$default (synthetic)
|
||||||
// test.kt:8 ifoo2
|
// test.kt:8 ifoo2
|
||||||
|
|||||||
Reference in New Issue
Block a user