[JVM] Implement new inlined variable naming format

^KT-65478 fixed
This commit is contained in:
Nikita Nazarov
2023-09-18 16:27:46 +02:00
committed by Alexander Udalov
parent 9ea775cbed
commit 407448d8e3
104 changed files with 88125 additions and 92 deletions
@@ -26,10 +26,7 @@ import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.JvmBackendConfig
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
import org.jetbrains.kotlin.config.JvmAnalysisFlags
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
@@ -416,10 +413,25 @@ class ClassCodegen private constructor(
)
val mv = with(node) { visitor.newMethod(method.descriptorOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
val smapCopier = SourceMapCopier(classSMAP, smap)
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
override fun visitLineNumber(line: Int, start: Label) =
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
private val lineNumberMapping = mutableMapOf<Int, Int>()
override fun visitLineNumber(line: Int, start: Label) {
val newLine = smapCopier.mapLineNumber(line)
lineNumberMapping[line] = newLine
super.visitLineNumber(newLine, start)
}
override fun visitLocalVariable(name: String, descriptor: String, signature: String?, start: Label, end: Label, index: Int) {
if (state.configuration.getBoolean(JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS) && isFakeLocalVariableForInline(name)) {
val newName = updateCallSiteLineNumber(name, lineNumberMapping)
return super.visitLocalVariable(newName, descriptor, signature, start, end, index)
}
super.visitLocalVariable(name, descriptor, signature, start, end, index)
}
}
if (method.hasContinuation()) {
// Generate a state machine within this method. The continuation class for it should be generated
// lazily so that if tail call optimization kicks in, the unused class will not be written to the output.
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.JvmBackendConfig
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.diagnostics.BackendErrors
@@ -50,6 +51,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
@@ -156,6 +158,13 @@ class ExpressionCodegen(
val state: GenerationState = context.state
val config: JvmBackendConfig = context.config
override val inlineScopesGenerator =
if (state.configuration.getBoolean(JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS)) {
InlineScopesGenerator()
} else {
null
}
override val visitor: InstructionAdapter
get() = mv
@@ -755,6 +764,15 @@ class ExpressionCodegen(
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
val varType = typeMapper.mapType(declaration)
val index = frameMap.enter(declaration.symbol, varType)
val name = declaration.name.asString()
if (state.configuration.getBoolean(JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS) &&
state.configuration.getBoolean(JVMConfigurationKeys.ENABLE_IR_INLINER) &&
isFakeLocalVariableForInline(name) &&
name.contains(INLINE_SCOPE_NUMBER_SEPARATOR)
) {
declaration.name = Name.identifier(updateCallSiteLineNumber(name, lineNumberMapper.getLineNumber()))
}
val initializer = declaration.initializer
if (initializer != null) {
@@ -12,8 +12,9 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.irInlinerIsEnabled
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX
import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.config.JVMConfigurationKeys.USE_INLINE_SCOPES_NUMBERS
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.createTmpVariable
@@ -21,9 +22,7 @@ import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlock
import org.jetbrains.kotlin.ir.expressions.IrInlinedFunctionBlock
import org.jetbrains.kotlin.ir.util.inlineDeclaration
import org.jetbrains.kotlin.ir.util.isFunctionInlining
import org.jetbrains.kotlin.ir.util.isLambdaInlining
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -56,8 +55,12 @@ internal class FakeLocalVariablesForIrInlinerLowering(
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
irFile.acceptVoid(FunctionParametersProcessor())
irFile.accept(LocalVariablesProcessor(), LocalVariablesProcessor.Data(processingOriginalDeclarations = false))
if (context.configuration.getBoolean(USE_INLINE_SCOPES_NUMBERS)) {
irFile.acceptVoid(ScopeNumberVariableProcessor())
} else {
irFile.acceptVoid(FunctionParametersProcessor())
irFile.accept(LocalVariablesProcessor(), LocalVariablesProcessor.Data(processingOriginalDeclarations = false))
}
}
override fun visitElement(element: IrElement) {
@@ -150,12 +153,16 @@ private class LocalVariablesProcessor : IrElementVisitor<Unit, LocalVariablesPro
val varName = declaration.name.asString()
val varSuffix = when {
inlinedStack.size == 1 && !varName.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) -> INLINE_FUN_VAR_SUFFIX
else -> ""
inlinedStack.size == 1 && !varName.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ->
INLINE_FUN_VAR_SUFFIX
else ->
""
}
val newName = when {
varSuffix.isNotEmpty() && varName == SpecialNames.THIS.asStringStripSpecialMarkers() -> "this_"
else -> varName
varSuffix.isNotEmpty() && varName == SpecialNames.THIS.asStringStripSpecialMarkers() ->
AsmUtil.INLINE_DECLARATION_SITE_THIS
else ->
varName
}
declaration.name = Name.identifier(newName + varSuffix)
super.visitVariable(declaration, data)
@@ -181,16 +188,83 @@ private class FunctionParametersProcessor : IrElementVisitorVoid {
private fun IrStatement.processFunctionParameter(inlinedBlock: IrInlinedFunctionBlock) {
if (this !is IrVariable || !this.isTmpForInline) return
val varName = this.name.asString().substringAfterLast("_")
val varNewName = when {
this.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER -> {
val functionName = (inlinedBlock.inlineDeclaration as? IrDeclarationWithName)?.name
functionName?.let { name -> "\$this$$name" } ?: AsmUtil.RECEIVER_PARAMETER_NAME
}
varName == SpecialNames.THIS.asStringStripSpecialMarkers() -> "this_"
else -> varName
}
this.name = Name.identifier(varNewName + INLINE_FUN_VAR_SUFFIX)
this.name = Name.identifier(calculateNewName(inlinedBlock) + INLINE_FUN_VAR_SUFFIX)
this.origin = IrDeclarationOrigin.DEFINED
}
}
private class ScopeNumberVariableProcessor : IrElementVisitorVoid {
private val inlinedStack = mutableListOf<Pair<IrInlinedFunctionBlock, Int>>()
private var lastInlineScopeNumber = 0
private inline fun IrInlinedFunctionBlock.insertInStackAndProcess(block: IrInlinedFunctionBlock.() -> Unit) {
lastInlineScopeNumber += 1
inlinedStack += Pair(this, lastInlineScopeNumber)
block()
inlinedStack.removeLast()
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
val processor = ScopeNumberVariableProcessor()
declaration.acceptChildrenVoid(processor)
}
override fun visitBlock(expression: IrBlock) {
if (expression !is IrInlinedFunctionBlock) {
return super.visitBlock(expression)
}
expression.insertInStackAndProcess {
super.visitBlock(expression)
}
}
override fun visitVariable(declaration: IrVariable) {
if (inlinedStack.isEmpty()) {
return super.visitVariable(declaration)
}
val (inlinedBlock, scopeNumber) = inlinedStack.last()
val newName = declaration.calculateNewName(inlinedBlock)
declaration.name = Name.identifier(addInlineScopeInfo(newName, scopeNumber))
super.visitVariable(declaration)
}
}
private fun IrVariable.calculateNewName(inlinedBlock: IrInlinedFunctionBlock): String {
val varName = name.asString().substringAfterLast("_")
return when {
this.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE_FOR_INLINED_EXTENSION_RECEIVER ->
inlinedBlock.getReceiverParameterName()
varName == SpecialNames.THIS.asStringStripSpecialMarkers() ->
AsmUtil.INLINE_DECLARATION_SITE_THIS
else ->
varName
}
}
private fun addInlineScopeInfo(name: String, scopeNumber: Int): String {
val nameWithScopeNumber = name.addScopeInfo(scopeNumber)
if (isFakeLocalVariableForInline(name)) {
// During IR inlining we can't fetch call site line numbers because the line number mapping
// has not been calculated yet. To keep the inline scope info format consistent, we will add
// a mock call site line number instead, which will be replaced with the real one during the
// expression codegen phase.
val nameWithCallSiteLineNumber = nameWithScopeNumber.addScopeInfo(0)
if (name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)) {
val surroundingScopeNumber = name.getInlineScopeInfo()?.surroundingScopeNumber ?: 0
return nameWithCallSiteLineNumber.addScopeInfo(surroundingScopeNumber)
}
return nameWithCallSiteLineNumber
}
return nameWithScopeNumber
}
private fun IrInlinedFunctionBlock.getReceiverParameterName(): String {
val functionName = (inlineDeclaration as? IrDeclarationWithName)?.name
return functionName?.let { "this$$it" } ?: AsmUtil.RECEIVER_PARAMETER_NAME
}