JVM_IR: Generate fake inlining local variables for debugging.
This commit is contained in:
@@ -291,6 +291,7 @@ private val jvmFilePhases =
|
||||
checkLocalNamesWithOldBackendPhase then
|
||||
|
||||
mainMethodGenerationPhase then
|
||||
fakeInliningLocalVariablesLowering then
|
||||
|
||||
makePatchParentsPhase(3)
|
||||
|
||||
|
||||
+95
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test() {
|
||||
val a = inlineFunInt { 1 }
|
||||
val b = simpleFunInt { 1 }
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
interface ApplicationCall
|
||||
|
||||
|
||||
-1
@@ -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
|
||||
}
|
||||
|
||||
+13
-3
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user