From e2a1cb107736f155888745eb3fa50ebbb2aef12f Mon Sep 17 00:00:00 2001 From: Mads Ager Date: Thu, 28 Nov 2019 15:10:09 +0100 Subject: [PATCH] JVM_IR: Generate fake inlining local variables for debugging. --- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 1 + .../FakeInliningLocalVariablesLowering.kt | 95 +++++++++++++++++++ .../boxingOptimization/severalInlines.kt | 1 - .../nopInlineFuns.kt | 1 - .../inline/inlineSuspendReifiedNoSpilling.kt | 1 - .../linenumberForOneParametersArgumentCall.kt | 1 - .../localInitializationLVT/genericsVar.kt | 1 - .../storeStackBeforeInline/simple.kt | 2 - .../AbstractIrCheckLocalVariablesTableTest.kt | 16 +++- 9 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FakeInliningLocalVariablesLowering.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index d745f0e42fc..79eca13b695 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -291,6 +291,7 @@ private val jvmFilePhases = checkLocalNamesWithOldBackendPhase then mainMethodGenerationPhase then + fakeInliningLocalVariablesLowering then makePatchParentsPhase(3) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FakeInliningLocalVariablesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FakeInliningLocalVariablesLowering.kt new file mode 100644 index 00000000000..d8b40eb18f3 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FakeInliningLocalVariablesLowering.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2019 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.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.mapClass +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.load.java.JvmAbi + +internal val fakeInliningLocalVariablesLowering = makeIrFilePhase( + ::FakeInliningLocalVariablesLowering, + name = "FakeInliningLocalVariablesLowering", + description = "Add fake locals to identify the range of inlined functions and lambdas" +) + +internal class FakeInliningLocalVariablesLowering(val context: JvmBackendContext) : IrElementVisitorVoid, FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.acceptChildrenVoid(this) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitCall(expression: IrCall) { + expression.acceptChildrenVoid(this) + val callee = expression.symbol.owner + if (callee.isInline) { + for (i in 0 until expression.valueArgumentsCount) { + val argument = expression.getValueArgument(i) + if ((argument is IrBlock) && argument.origin == IrStatementOrigin.LAMBDA) { + val lastStatement = argument.statements.last() + if (lastStatement is IrFunctionReference) { + val localFunForLambda = lastStatement.symbol.owner + if (localFunForLambda.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) { + localFunForLambda.addFakeInliningLocalVariablesForArguments(callee) + } + } + } + } + } + } + + override fun visitFunction(declaration: IrFunction) { + declaration.acceptChildrenVoid(this) + if (declaration.isInline && !declaration.origin.isSynthetic && declaration.body != null) { + declaration.addFakeInliningLocalVariables() + } + } + + private fun IrFunction.addFakeInliningLocalVariables() { + val currentFunctionName = context.methodSignatureMapper.mapFunctionName(this) + val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION}$currentFunctionName" + addFakeLocalVariable(localName) + } + + private fun IrFunction.addFakeInliningLocalVariablesForArguments(callee: IrFunction) { + val currentFunctionName = context.methodSignatureMapper.mapFunctionName(this) + val argumentToFunctionName = context.methodSignatureMapper.mapFunctionName(callee) + val internalName = context.typeMapper.mapClass(parentAsClass).internalName + val thisType = internalName.substringAfterLast('/', internalName) + val lambdaReference = "$thisType\$$currentFunctionName" + val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT}-$argumentToFunctionName-$lambdaReference" + addFakeLocalVariable(localName) + } + + private fun IrFunction.addFakeLocalVariable(name: String) { + context.createIrBuilder(symbol).run { + body = irBlockBody { + // Create temporary variable, but make sure it's origin is `DEFINED` so that + // it will materialize in the code. + createTmpVariable(irInt(0), name, origin = IrDeclarationOrigin.DEFINED) + if (body is IrExpressionBody) { + +(body as IrExpressionBody).expression + } else { + (body as IrBlockBody).statements.forEach { +it } + } + } + } + } +} diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/severalInlines.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/severalInlines.kt index 6fd8730aa37..9078de27d20 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/severalInlines.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/severalInlines.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR inline fun foo(x : R, y : R, block : (R, R) -> T) : T { return block(x, y) diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt index d02d500e202..17d8063be40 100644 --- a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test() { val a = inlineFunInt { 1 } val b = simpleFunInt { 1 } diff --git a/compiler/testData/codegen/bytecodeText/inline/inlineSuspendReifiedNoSpilling.kt b/compiler/testData/codegen/bytecodeText/inline/inlineSuspendReifiedNoSpilling.kt index 0bde89ef84d..d054a939bed 100644 --- a/compiler/testData/codegen/bytecodeText/inline/inlineSuspendReifiedNoSpilling.kt +++ b/compiler/testData/codegen/bytecodeText/inline/inlineSuspendReifiedNoSpilling.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface ApplicationCall diff --git a/compiler/testData/codegen/bytecodeText/inline/linenumberForOneParametersArgumentCall.kt b/compiler/testData/codegen/bytecodeText/inline/linenumberForOneParametersArgumentCall.kt index 0c6c594bd4f..e1ff9ff84ac 100644 --- a/compiler/testData/codegen/bytecodeText/inline/linenumberForOneParametersArgumentCall.kt +++ b/compiler/testData/codegen/bytecodeText/inline/linenumberForOneParametersArgumentCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box() { lookAtMe { diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt index 1c7f01ae41a..0d3288e7e53 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR inline fun foo(default: T): T { var t: T diff --git a/compiler/testData/codegen/bytecodeText/storeStackBeforeInline/simple.kt b/compiler/testData/codegen/bytecodeText/storeStackBeforeInline/simple.kt index dd2947797e9..5a4f6634e82 100644 --- a/compiler/testData/codegen/bytecodeText/storeStackBeforeInline/simple.kt +++ b/compiler/testData/codegen/bytecodeText/storeStackBeforeInline/simple.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - inline fun bar(x: Int) : Int { return x } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCheckLocalVariablesTableTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCheckLocalVariablesTableTest.kt index 6e6829ef139..951bf9cb82a 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCheckLocalVariablesTableTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCheckLocalVariablesTableTest.kt @@ -30,15 +30,25 @@ abstract class AbstractIrCheckLocalVariablesTableTest : AbstractCheckLocalVariab private fun getActualVariablesAsList(list: List): List { return list.map { it.toString() } - .map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } // Ignore index + // Ignore local index. + .map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } + // Ignore the names of local functions which have integer names in + // the current backend and more descriptive names with the JVM_IR + // backend. + .map { line -> line.replace("\\\$\\d+".toRegex(), "\\\$*") } + .map { line -> line.replace("\\\$lambda-\\d+".toRegex(), "\\\$*") } .sorted() } private fun getExpectedVariablesAsList(testFile: File): List { return testFile.readLines() .filter { line -> line.startsWith("// VARIABLE ") } - .filter { !it.contains("NAME=\$i\$") } - .map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } // Ignore index + // Ignore local index. + .map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } + // Ignore the names of local functions which have integer names in + // the current backend and more descriptive names with the JVM_IR + // backend. + .map { line -> line.replace("\\\$\\d+".toRegex(), "\\\$*") } .sorted() }