JVM_IR: Generate fake inlining local variables for debugging.

This commit is contained in:
Mads Ager
2019-11-28 15:10:09 +01:00
committed by max-kammerer
parent fa8cb7b6e0
commit e2a1cb1077
9 changed files with 109 additions and 10 deletions
@@ -291,6 +291,7 @@ private val jvmFilePhases =
checkLocalNamesWithOldBackendPhase then
mainMethodGenerationPhase then
fakeInliningLocalVariablesLowering then
makePatchParentsPhase(3)
@@ -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 }
}
}
}
}
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
inline fun <R, T> foo(x : R, y : R, block : (R, R) -> T) : T {
return block(x, y)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun test() {
val a = inlineFunInt { 1 }
val b = simpleFunInt { 1 }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface ApplicationCall
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun box() {
lookAtMe {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
inline fun <reified T> foo(default: T): T {
var t: T
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
inline fun bar(x: Int) : Int {
return x
}
@@ -30,15 +30,25 @@ abstract class AbstractIrCheckLocalVariablesTableTest : AbstractCheckLocalVariab
private fun getActualVariablesAsList(list: List<LocalVariable>): List<String> {
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<String> {
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()
}