[IR] Eliminate redundant boxing/unboxing of MFVC after inlining

Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com>

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-11-13 21:57:15 +01:00
committed by Space Team
parent a3e396e7e4
commit 40f38c8adb
25 changed files with 726 additions and 2292 deletions
@@ -20,10 +20,16 @@ import com.intellij.openapi.util.Pair
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.GenerationState.MultiFieldValueClassUnboxInfo
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.InsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
abstract val descriptor: BoxedValueDescriptor
@@ -55,22 +61,33 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas
class BoxedValueDescriptor(
boxedType: Type,
val boxedType: Type,
val boxingInsn: AbstractInsnNode,
val progressionIterator: ProgressionIteratorBasicValue?,
val generationState: GenerationState
) {
private val associatedInsns = HashSet<AbstractInsnNode>()
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
private val associatedInsns = LinkedHashSet<AbstractInsnNode>()
private val unboxingWithCastInsns = LinkedHashSet<Pair<AbstractInsnNode, Type>>()
private val associatedVariables = HashSet<Int>()
private val mergedWith = HashSet<BoxedValueDescriptor>()
var isSafeToRemove = true; private set
val unboxedType: Type = getUnboxedType(boxedType, generationState)
val isInlineClassValue = isInlineClassValue(boxedType)
val multiFieldValueClassUnboxInfo = getMultiFieldValueClassUnboxInfo(boxedType, generationState)
val unboxedTypes: List<Type> = getUnboxedTypes(boxedType, generationState, multiFieldValueClassUnboxInfo)
fun getUnboxTypeOrOtherwiseMethodReturnType(methodInsnNode: MethodInsnNode?) =
unboxedTypes.singleOrNull() ?: Type.getReturnType(methodInsnNode!!.desc)
val isValueClassValue = isValueClassValue(boxedType)
fun getAssociatedInsns() = associatedInsns.toList()
fun sortAssociatedInsns(indexes: Map<AbstractInsnNode, Int>) {
val newOrder = associatedInsns.sortedBy { indexes[it]!! }
associatedInsns.clear()
associatedInsns.addAll(newOrder)
}
fun addInsn(insnNode: AbstractInsnNode) {
associatedInsns.add(insnNode)
}
@@ -93,7 +110,7 @@ class BoxedValueDescriptor(
isSafeToRemove = false
}
fun isDoubleSize() = unboxedType.size == 2
fun getTotalUnboxSize() = unboxedTypes.sumOf { it.size }
fun isFromProgressionIterator() = progressionIterator != null
@@ -103,16 +120,51 @@ class BoxedValueDescriptor(
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
unboxingWithCastInsns
fun sortUnboxingWithCastInsns(indexes: Map<AbstractInsnNode, Int>) {
val newInsnsAndResultTypes = unboxingWithCastInsns.sortedBy { insnAndResultType -> indexes[insnAndResultType.first]!! }
unboxingWithCastInsns.clear()
unboxingWithCastInsns.addAll(newInsnsAndResultTypes)
}
}
internal fun makePops(unboxedTypes: List<Type>) = InsnList().apply {
var restSingleSize = false
for (unboxType in unboxedTypes.asReversed()) {
restSingleSize = when (unboxType.size) {
1 -> {
if (restSingleSize) add(InsnNode(Opcodes.POP2))
!restSingleSize
}
2 -> {
if (restSingleSize) add(InsnNode(Opcodes.POP))
add(InsnNode(Opcodes.POP2))
false
}
else -> error("Illegal type size: ${unboxType.size}")
}
}
if (restSingleSize) {
add(InsnNode(Opcodes.POP))
}
}
fun getUnboxedType(boxedType: Type, state: GenerationState): Type {
fun getUnboxedTypes(
boxedType: Type,
state: GenerationState,
multiFieldValueClassUnboxInfo: MultiFieldValueClassUnboxInfo?
): List<Type> {
val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType)
if (primitiveType != null) return primitiveType
if (primitiveType != null) return listOf(primitiveType)
if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE
if (boxedType == AsmTypes.K_CLASS_TYPE) return listOf(AsmTypes.JAVA_CLASS_TYPE)
unboxedTypeOfInlineClass(boxedType, state)?.let { return it }
unboxedTypeOfInlineClass(boxedType, state)?.let { return listOf(it) }
multiFieldValueClassUnboxInfo?.let { return it.unboxedTypes }
throw IllegalArgumentException("Expected primitive type wrapper or KClass or inline class wrapper, got: $boxedType")
}
@@ -123,6 +175,13 @@ fun unboxedTypeOfInlineClass(boxedType: Type, state: GenerationState): Type? {
return state.mapInlineClass(descriptor)
}
private fun isInlineClassValue(boxedType: Type): Boolean {
fun getMultiFieldValueClassUnboxInfo(boxedType: Type, state: GenerationState): MultiFieldValueClassUnboxInfo? {
val descriptor =
state.jvmBackendClassResolver.resolveToClassDescriptors(boxedType).singleOrNull()?.takeIf { it.isMultiFieldValueClass() }
?: return null
return state.multiFieldValueClassUnboxInfo(descriptor)
}
private fun isValueClassValue(boxedType: Type): Boolean {
return !AsmUtil.isBoxedPrimitiveType(boxedType) && boxedType != AsmTypes.K_CLASS_TYPE
}
@@ -180,7 +180,7 @@ open class BoxingInterpreter(
v is TaintedBoxedValue -> v
w is TaintedBoxedValue -> w
v.type != w.type -> mergeBoxedHazardous(v, w, isLocalVariable)
else -> v
else -> v // two clean boxed values with the same type are equal
}
}
v is BoxedBasicValue ->
@@ -222,10 +222,10 @@ private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYP
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
fun AbstractInsnNode.isUnboxing(state: GenerationState) =
isPrimitiveUnboxing() || isJavaLangClassUnboxing() || isInlineClassUnboxing(state)
isPrimitiveUnboxing() || isJavaLangClassUnboxing() || isInlineClassUnboxing(state) || isMultiFieldValueClassUnboxing(state)
fun AbstractInsnNode.isBoxing(state: GenerationState) =
isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state) || isCoroutinePrimitiveBoxing()
isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state) || isCoroutinePrimitiveBoxing() || isMultiFieldValueClassBoxing(state)
fun AbstractInsnNode.isPrimitiveUnboxing() =
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
@@ -292,11 +292,21 @@ private fun AbstractInsnNode.isInlineClassBoxing(state: GenerationState) =
isInlineClassBoxingMethodDescriptor(state)
}
private fun AbstractInsnNode.isMultiFieldValueClassBoxing(state: GenerationState) =
isMethodInsnWith(Opcodes.INVOKESTATIC) {
isMultiFieldValueClassBoxingMethodDescriptor(state)
}
private fun AbstractInsnNode.isInlineClassUnboxing(state: GenerationState) =
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
isInlineClassUnboxingMethodDescriptor(state)
}
private fun AbstractInsnNode.isMultiFieldValueClassUnboxing(state: GenerationState) =
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
isMultiFieldValueClassUnboxingMethodDescriptor(state)
}
private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: GenerationState): Boolean {
if (name != KotlinTypeMapper.BOX_JVM_METHOD_NAME) return false
@@ -305,6 +315,14 @@ private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: Generation
return desc == Type.getMethodDescriptor(ownerType, unboxedType)
}
private fun MethodInsnNode.isMultiFieldValueClassBoxingMethodDescriptor(state: GenerationState): Boolean {
if (name != KotlinTypeMapper.BOX_JVM_METHOD_NAME) return false
val ownerType = Type.getObjectType(owner)
val multiFieldValueClassUnboxInfo = getMultiFieldValueClassUnboxInfo(ownerType, state) ?: return false
return desc == Type.getMethodDescriptor(ownerType, *multiFieldValueClassUnboxInfo.unboxedTypes.toTypedArray())
}
private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
if (name != KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) return false
@@ -313,6 +331,14 @@ private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: Generati
return desc == Type.getMethodDescriptor(unboxedType)
}
private fun MethodInsnNode.isMultiFieldValueClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
val ownerType = Type.getObjectType(owner)
val multiFieldValueClassUnboxInfo = getMultiFieldValueClassUnboxInfo(ownerType, state) ?: return false
return multiFieldValueClassUnboxInfo.unboxedTypesAndMethodNamesAndFieldNames.any { (type, methodName) ->
name == methodName && desc == Type.getMethodDescriptor(type)
}
}
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
values.firstOrNull() is ProgressionIteratorBasicValue &&
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
@@ -349,8 +375,8 @@ fun areSameTypedPrimitiveBoxedValues(values: List<BasicValue>): Boolean {
val (v1, v2) = values
return v1 is BoxedBasicValue &&
v2 is BoxedBasicValue &&
v1.descriptor.unboxedType == v2.descriptor.unboxedType &&
!v1.descriptor.isInlineClassValue && !v2.descriptor.isInlineClassValue
!v1.descriptor.isValueClassValue && !v2.descriptor.isValueClassValue &&
v1.descriptor.unboxedTypes.single() == v2.descriptor.unboxedTypes.single()
}
fun AbstractInsnNode.isAreEqualIntrinsic() =
@@ -362,8 +388,10 @@ fun AbstractInsnNode.isAreEqualIntrinsic() =
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>, generationState: GenerationState): Boolean =
values.none { getUnboxedType(it.type, generationState) in shouldUseEqualsForWrappers }
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>, generationState: GenerationState): Boolean = values.none {
val unboxedType = getUnboxedTypes(it.type, generationState, getMultiFieldValueClassUnboxInfo(it.type, generationState)).singleOrNull()
unboxedType == null || unboxedType in shouldUseEqualsForWrappers
}
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
isJavaLangComparableCompareTo() && areSameTypedPrimitiveBoxedValues(values)
@@ -20,16 +20,13 @@ import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
internal class RedundantBoxingInterpreter(
insnList: InsnList,
methodNode: MethodNode,
generationState: GenerationState
) : BoxingInterpreter(insnList, generationState) {
) : BoxingInterpreter(methodNode.instructions, generationState) {
val candidatesBoxedValues = RedundantBoxedValuesCollection()
@@ -81,6 +78,7 @@ internal class RedundantBoxingInterpreter(
override fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {
value.descriptor.run {
val unboxedType = getUnboxTypeOrOtherwiseMethodReturnType(insn as? MethodInsnNode)
if (unboxedType == resultType)
addAssociatedInsn(value, insn)
else
@@ -142,7 +140,7 @@ internal class RedundantBoxingInterpreter(
Type.getInternalName(Any::class.java) ->
true
Type.getInternalName(Number::class.java) ->
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
value.descriptor.unboxedTypes.singleOrNull()?.sort?.let { PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(it) } == true
"java/lang/Comparable" ->
true
else ->
@@ -41,7 +41,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
if (insns.none { it.isBoxing(generationState) || it.isMethodInsnWith(Opcodes.INVOKEINTERFACE) { name == "next" } })
return
val interpreter = RedundantBoxingInterpreter(node.instructions, generationState)
val interpreter = RedundantBoxingInterpreter(node, generationState)
val frames = BoxingAnalyzer(internalClassName, node, interpreter).analyze()
interpretPopInstructionsForBoxedValues(interpreter, node, frames)
@@ -55,14 +55,34 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
// has side effect on valuesToOptimize and frames, containing BoxedBasicValues that are unsafe to remove
removeValuesClashingWithVariables(valuesToOptimize, node, frames)
adaptLocalVariableTableForBoxedValues(node, frames)
// cannot replace them inplace because replaced variables indexes are known after remapping
val variablesForReplacement = adaptLocalSingleVariableTableForBoxedValuesAndPrepareMultiVariables(node, frames)
node.remapLocalVariables(buildVariablesRemapping(valuesToOptimize, node))
replaceVariables(node, variablesForReplacement)
sortAdaptableInstructionsForBoxedValues(node, valuesToOptimize)
adaptInstructionsForBoxedValues(node, valuesToOptimize)
}
}
private fun sortAdaptableInstructionsForBoxedValues(node: MethodNode, valuesToOptimize: RedundantBoxedValuesCollection) {
val indexes = node.instructions.withIndex().associate { (index, insn) -> insn to index }
for (value in valuesToOptimize) {
value.sortAssociatedInsns(indexes)
value.sortUnboxingWithCastInsns(indexes)
}
}
private fun replaceVariables(node: MethodNode, variablesForReplacement: Map<LocalVariableNode, List<LocalVariableNode>>) {
if (variablesForReplacement.isEmpty()) return
node.localVariables = node.localVariables.flatMap { oldVar ->
variablesForReplacement[oldVar]?.also { newVars -> for (newVar in newVars) newVar.index += oldVar.index } ?: listOf(oldVar)
}.toMutableList()
}
private fun interpretPopInstructionsForBoxedValues(
interpreter: RedundantBoxingInterpreter,
node: MethodNode,
@@ -114,7 +134,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
if (boxed.isEmpty()) continue
val firstBoxed = boxed.first().descriptor
if (isUnsafeToRemoveBoxingForConnectedValues(variableValues, firstBoxed.unboxedType)) {
if (isUnsafeToRemoveBoxingForConnectedValues(variableValues, firstBoxed.unboxedTypes)) {
for (value in boxed) {
val descriptor = value.descriptor
if (descriptor.isSafeToRemove) {
@@ -137,16 +157,19 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
}
}
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean =
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedTypes: List<Type>): Boolean =
usedValues.any { input ->
if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false
if (input !is CleanBoxedValue) return@any true
val descriptor = input.descriptor
!descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType
!descriptor.isSafeToRemove || descriptor.unboxedTypes != unboxedTypes
}
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>?>) {
private fun adaptLocalSingleVariableTableForBoxedValuesAndPrepareMultiVariables(
node: MethodNode, frames: Array<Frame<BasicValue>?>
): Map<LocalVariableNode, List<LocalVariableNode>> {
val localVariablesReplacement = mutableMapOf<LocalVariableNode, List<LocalVariableNode>>()
for (localVariableNode in node.localVariables) {
if (Type.getType(localVariableNode.desc).sort != Type.OBJECT) {
continue
@@ -157,9 +180,24 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
val descriptor = value.descriptor
if (!descriptor.isSafeToRemove) continue
localVariableNode.desc = descriptor.unboxedType.descriptor
val unboxedType = descriptor.unboxedTypes.singleOrNull()
if (unboxedType == null) {
var offset = 0
localVariablesReplacement[localVariableNode] =
descriptor.multiFieldValueClassUnboxInfo!!.unboxedTypesAndMethodNamesAndFieldNames.map { (type, _, fieldName) ->
val newVarName = "${localVariableNode.name}-$fieldName"
val newStart = localVariableNode.start
val newEnd = localVariableNode.end
val newOffset = offset
offset += type.size
LocalVariableNode(newVarName, type.descriptor, null, newStart, newEnd, newOffset)
}
} else {
localVariableNode.desc = unboxedType.descriptor
}
}
}
return localVariablesReplacement
}
private fun getValuesStoredOrLoadedToVariable(
@@ -197,22 +235,24 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
}
private fun buildVariablesRemapping(values: RedundantBoxedValuesCollection, node: MethodNode): IntArray {
val doubleSizedVars = HashSet<Int>()
val wideVars2SizeMinusOne = HashMap<Int, Int>()
for (valueDescriptor in values) {
if (valueDescriptor.isDoubleSize()) {
doubleSizedVars.addAll(valueDescriptor.getVariablesIndexes())
val size = valueDescriptor.getTotalUnboxSize()
if (size < 2) continue
for (index in valueDescriptor.getVariablesIndexes()) {
wideVars2SizeMinusOne.merge(index, size - 1, ::maxOf)
}
}
node.maxLocals += doubleSizedVars.size
node.maxLocals += wideVars2SizeMinusOne.values.sum()
val remapping = IntArray(node.maxLocals)
for (i in remapping.indices) {
remapping[i] = i
}
for (varIndex in doubleSizedVars) {
for ((varIndex, shift) in wideVars2SizeMinusOne) {
for (i in varIndex + 1..remapping.lastIndex) {
remapping[i]++
remapping[i] += shift
}
}
@@ -235,9 +275,11 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
adaptCastInstruction(node, value, cast)
}
var extraSlotsUsed = 0
for (insn in value.getAssociatedInsns()) {
adaptInstruction(node, insn, value)
extraSlotsUsed = maxOf(extraSlotsUsed, adaptInstruction(node, insn, value))
}
node.maxLocals += extraSlotsUsed
}
private fun adaptBoxingInstruction(node: MethodNode, value: BoxedValueDescriptor) {
@@ -268,7 +310,9 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
) {
val castInsn = castWithType.getFirst()
val castInsnsListener = MethodNode(Opcodes.API_VERSION)
InstructionAdapter(castInsnsListener).cast(value.unboxedType, castWithType.getSecond())
InstructionAdapter(castInsnsListener)
.cast(value.getUnboxTypeOrOtherwiseMethodReturnType(castInsn as? MethodInsnNode), castWithType.getSecond())
for (insn in castInsnsListener.instructions.toArray()) {
node.instructions.insertBefore(castInsn, insn)
@@ -279,30 +323,75 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
private fun adaptInstruction(
node: MethodNode, insn: AbstractInsnNode, value: BoxedValueDescriptor
) {
val isDoubleSize = value.isDoubleSize()
): Int {
var usedExtraSlots = 0
when (insn.opcode) {
Opcodes.POP ->
if (isDoubleSize) {
node.instructions.set(insn, InsnNode(Opcodes.POP2))
}
Opcodes.POP -> {
val newPops = makePops(value.unboxedTypes)
node.instructions.insert(insn, newPops)
node.instructions.remove(insn)
}
Opcodes.DUP ->
if (isDoubleSize) {
node.instructions.set(insn, InsnNode(Opcodes.DUP2))
Opcodes.DUP -> when (value.getTotalUnboxSize()) {
1 -> Unit
2 -> node.instructions.set(insn, InsnNode(Opcodes.DUP2))
else -> {
usedExtraSlots = value.getTotalUnboxSize()
var currentSlot = node.maxLocals
val slotIndices = value.unboxedTypes.map { type -> currentSlot.also { currentSlot += type.size } }
for ((type, index) in (value.unboxedTypes zip slotIndices).asReversed()) {
node.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), index))
}
repeat(2) {
for ((type, index) in (value.unboxedTypes zip slotIndices)) {
node.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), index))
}
}
node.instructions.remove(insn)
}
}
Opcodes.ASTORE, Opcodes.ALOAD -> {
val storeOpcode = value.unboxedType.getOpcode(if (insn.opcode == Opcodes.ASTORE) Opcodes.ISTORE else Opcodes.ILOAD)
node.instructions.set(insn, VarInsnNode(storeOpcode, (insn as VarInsnNode).`var`))
val isStore = insn.opcode == Opcodes.ASTORE
val singleUnboxedType = value.unboxedTypes.singleOrNull()
if (singleUnboxedType == null) {
val newInstructions = mutableListOf<VarInsnNode>()
var offset = 0
for (unboxedType in value.unboxedTypes) {
val opcode = unboxedType.getOpcode(if (isStore) Opcodes.ISTORE else Opcodes.ILOAD)
val newIndex = (insn as VarInsnNode).`var` + offset
newInstructions.add(VarInsnNode(opcode, newIndex))
offset += unboxedType.size
}
if (isStore) {
val previousInstructions = generateSequence(insn.previous) { it.previous }
.take(value.unboxedTypes.size).toList().asReversed()
if (value.unboxedTypes.map { it.getOpcode(Opcodes.ILOAD) } == previousInstructions.map { it.opcode }) {
// help optimizer and put each xSTORE after the corresponding xLOAD
for ((load, store) in previousInstructions zip newInstructions) {
newInstructions.remove(store)
node.instructions.insert(load, store)
}
} else {
for (newInstruction in newInstructions.asReversed()) {
node.instructions.insertBefore(insn, newInstruction)
}
}
} else {
for (newInstruction in newInstructions) {
node.instructions.insertBefore(insn, newInstruction)
}
}
node.instructions.remove(insn)
} else {
val storeOpcode = singleUnboxedType.getOpcode(if (isStore) Opcodes.ISTORE else Opcodes.ILOAD)
node.instructions.set(insn, VarInsnNode(storeOpcode, (insn as VarInsnNode).`var`))
}
}
Opcodes.INSTANCEOF -> {
node.instructions.insertBefore(
insn,
InsnNode(if (isDoubleSize) Opcodes.POP2 else Opcodes.POP)
)
node.instructions.insertBefore(insn, makePops(value.unboxedTypes))
node.instructions.set(insn, InsnNode(Opcodes.ICONST_1))
}
@@ -328,13 +417,63 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
}
}
Opcodes.CHECKCAST,
Opcodes.INVOKEVIRTUAL ->
Opcodes.CHECKCAST -> node.instructions.remove(insn)
Opcodes.INVOKEVIRTUAL -> {
if (value.unboxedTypes.size != 1) {
val unboxMethodCall = insn as MethodInsnNode
val unboxMethodIndex = value.multiFieldValueClassUnboxInfo!!.unboxedMethodNames.indexOf(unboxMethodCall.name)
val unboxedType = value.unboxedTypes[unboxMethodIndex]
var canRemoveInsns = true
var savedToVariable = false
for ((i, type) in value.unboxedTypes.withIndex().toList().asReversed()) {
fun canRemoveInsn(includeDup: Boolean): Boolean {
if (!canRemoveInsns) return false
val insnToCheck = if (i < unboxMethodIndex) unboxMethodCall.previous.previous else unboxMethodCall.previous
val result = when (insnToCheck.opcode) {
type.getOpcode(Opcodes.ILOAD) -> true
Opcodes.DUP2 -> includeDup && type.size == 2
Opcodes.DUP -> includeDup && type.size == 1
else -> false
}
canRemoveInsns = result
return result
}
fun insertPopInstruction() =
node.instructions.insertBefore(unboxMethodCall, InsnNode(if (type.size == 2) Opcodes.POP2 else Opcodes.POP))
fun saveToVariableIfNecessary() {
if (savedToVariable) return
if (i > unboxMethodIndex) return
savedToVariable = true
usedExtraSlots = unboxedType.size
node.instructions.insertBefore(insn, VarInsnNode(unboxedType.getOpcode(Opcodes.ISTORE), node.maxLocals))
}
if (i == unboxMethodIndex) {
if (unboxMethodIndex > 0 && !canRemoveInsn(includeDup = false)) {
saveToVariableIfNecessary()
}
} else if (canRemoveInsn(includeDup = i > unboxMethodIndex)) {
node.instructions.remove(if (i < unboxMethodIndex) unboxMethodCall.previous.previous else unboxMethodCall.previous)
} else {
saveToVariableIfNecessary()
insertPopInstruction()
}
}
if (savedToVariable) {
node.instructions.insertBefore(insn, VarInsnNode(unboxedType.getOpcode(Opcodes.ILOAD), node.maxLocals))
}
}
node.instructions.remove(insn)
}
else ->
throwCannotAdaptInstruction(insn)
}
return usedExtraSlots
}
private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing =
@@ -345,14 +484,14 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
insn: AbstractInsnNode,
value: BoxedValueDescriptor
) {
val unboxedType = value.unboxedType
val unboxedType = value.unboxedTypes.singleOrNull()
when (unboxedType.sort) {
when (unboxedType?.sort) {
Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.INT, Type.CHAR ->
adaptAreEqualIntrinsicForInt(node, insn)
Type.LONG ->
adaptAreEqualIntrinsicForLong(node, insn)
Type.OBJECT -> {
Type.OBJECT, null -> {
}
else ->
throw AssertionError("Unexpected unboxed type kind: $unboxedType")
@@ -427,7 +566,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
insn: AbstractInsnNode,
value: BoxedValueDescriptor
) {
val unboxedType = value.unboxedType
val unboxedType = value.unboxedTypes.single()
when (unboxedType.sort) {
Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.INT, Type.CHAR ->
@@ -236,6 +236,11 @@ open class FastMethodAnalyzer<V : Value>
}
}
/**
* Updates frame at the index [dest] with its old value if provided and previous control flow node frame [frame].
* Reuses old frame when possible and when [canReuse] is true.
* If updated, adds the frame to the queue
*/
private fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean = false) {
val oldFrame = frames[dest]
val changes = when {
@@ -9,7 +9,6 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.`when`.MappingsClassesForWhenByEnum
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.RootContext
@@ -20,9 +19,13 @@ import org.jetbrains.kotlin.codegen.inline.InlineCache
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.codegen.`when`.MappingsClassesForWhenByEnum
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.config.LanguageVersion.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
@@ -354,6 +357,14 @@ class GenerationState private constructor(
val globalSerializationBindings = JvmSerializationBindings()
var mapInlineClass: (ClassDescriptor) -> Type = { descriptor -> typeMapper.mapType(descriptor.defaultType) }
class MultiFieldValueClassUnboxInfo(val unboxedTypesAndMethodNamesAndFieldNames: List<Triple<Type, String, String>>) {
val unboxedTypes = unboxedTypesAndMethodNamesAndFieldNames.map { (type, _, _) -> type }
val unboxedMethodNames = unboxedTypesAndMethodNamesAndFieldNames.map { (_, methodName, _) -> methodName }
val unboxedFieldNames = unboxedTypesAndMethodNamesAndFieldNames.map { (_, _, fieldName) -> fieldName }
}
var multiFieldValueClassUnboxInfo: (ClassDescriptor) -> MultiFieldValueClassUnboxInfo? = { null }
val typeApproximator: TypeApproximator? =
if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference))
TypeApproximator(module.builtIns, languageVersionSettings)
@@ -50501,6 +50501,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/valueClasses/functionReferences.kt");
}
@Test
@TestMetadata("inlineFunctions.kt")
public void testInlineFunctions() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/inlineFunctions.kt");
}
@Test
@TestMetadata("mfvcBothEqualsOverride.kt")
public void testMfvcBothEqualsOverride() throws Exception {
@@ -413,7 +413,9 @@ class ExpressionCodegen(
get() = origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE &&
origin != IrDeclarationOrigin.FOR_LOOP_ITERATOR &&
origin != IrDeclarationOrigin.UNDERSCORE_PARAMETER &&
origin != IrDeclarationOrigin.DESTRUCTURED_OBJECT_PARAMETER
origin != IrDeclarationOrigin.DESTRUCTURED_OBJECT_PARAMETER &&
origin != JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE &&
origin != JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_PARAMETER
private fun writeLocalVariablesInTable(info: BlockInfo, endLabel: Label) {
info.variables.forEach {
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.ir.inline
import org.jetbrains.kotlin.backend.common.lower.irCatch
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
internal class JvmMultiFieldValueClassLowering(
context: JvmBackendContext,
@@ -382,7 +383,7 @@ internal class JvmMultiFieldValueClassLowering(
symbol = IrAnonymousInitializerSymbolImpl()
).apply {
parent = irClass
body = context.createIrBuilder(symbol).irBlockBody {
body = context.createJvmIrBuilder(symbol).irBlockBody {
+irSetField(
irClass.thisReceiver!!.takeUnless { element.isStatic }?.let { irGet(it) }, element, initializer.expression,
origin = UNSAFE_MFVC_SET_ORIGIN
@@ -407,7 +408,7 @@ internal class JvmMultiFieldValueClassLowering(
}
allScopes.push(createScope(replacement))
replacement.body = context.createIrBuilder(replacement.symbol).irBlockBody {
replacement.body = context.createJvmIrBuilder(replacement.symbol).irBlockBody {
val thisVar = irTemporary(irType = replacement.returnType, nameHint = "\$this")
constructor.body?.statements?.forEach { statement ->
+statement.transformStatement(object : IrElementTransformerVoid() {
@@ -499,7 +500,7 @@ internal class JvmMultiFieldValueClassLowering(
override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
allScopes.push(createScope(source))
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
source.body = context.createJvmIrBuilder(source.symbol).run {
val sourceExplicitParameters = source.explicitParameters
val targetExplicitParameters = target.explicitParameters
irExprBody(irBlock {
@@ -691,7 +692,7 @@ internal class JvmMultiFieldValueClassLowering(
}
override fun visitParameter(parameter: IrValueParameter) {
if (parameter.origin != IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER) {
if (parameter.origin != JvmLoweredDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER) {
super.visitParameter(parameter)
}
}
@@ -703,7 +704,7 @@ internal class JvmMultiFieldValueClassLowering(
val initializersBlocks = mfvc.declarations.filterIsInstance<IrAnonymousInitializer>()
val typeArguments = makeTypeParameterSubstitutionMap(mfvc, primaryConstructorImpl)
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
primaryConstructorImpl.body = context.createJvmIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
val mfvcNodeInstance =
ValueDeclarationMfvcNodeInstance(rootMfvcNode, typeArguments, primaryConstructorImpl.valueParameters)
valueDeclarationsRemapper.registerReplacement(
@@ -734,8 +735,8 @@ internal class JvmMultiFieldValueClassLowering(
require(irBlock.hasLambdaLikeOrigin() && irBlock.statements.size == 2) { "Illegal lambda: ${irBlock.dump()}" }
val (originalFunction, ref) = irBlock.statements
require(originalFunction is IrSimpleFunction && ref is IrFunctionReference && ref.symbol.owner == originalFunction) { "Illegal lambda: ${irBlock.dump()}" }
val replacement = originalFunction.getReplacement()
require(originalFunction == irBlock.statements.first()) { "Illegal lambda: ${irBlock.dump()}" }
val replacement = originalFunction.getReplacement()
if (replacement == null) {
irBlock.statements[0] = visitFunctionNew(originalFunction)
return irBlock
@@ -745,31 +746,64 @@ internal class JvmMultiFieldValueClassLowering(
"Expected ${replacement.render()}, got ${declarations?.map { it.render() }}"
}
}
postActionAfterTransformingClassDeclaration(replacement)
val newBlock = makeNewLambda(originalFunction, ref)
val newFunction = newBlock.statements[0] as IrFunction
replacement.origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
when (val body = newFunction.body) {
is IrBlockBody -> body.statements.add(0, replacement)
is IrExpressionBody -> body.expression = context.createJvmIrBuilder(newFunction.symbol).irBlock {
+replacement
+body.expression
return makeNewLambda(originalFunction, ref, makeBody = { wrapper ->
variablesToAdd[replacement]?.let {
variablesToAdd[wrapper] = it
variablesToAdd.remove(replacement)
}
}
replacement.patchDeclarationParents(newFunction)
return newBlock
if (replacement in possibleExtraBoxUsageGenerated) {
possibleExtraBoxUsageGenerated.add(wrapper)
possibleExtraBoxUsageGenerated.remove(replacement)
}
with(context.createJvmIrBuilder(wrapper.symbol)) {
irExprBody(irBlock {
val newArguments: List<IrValueDeclaration> = wrapper.explicitParameters.flatMap { parameter ->
if (!parameter.type.needsMfvcFlattening()) {
listOf(parameter)
} else {
// Old parameter value will be only used to set parameter of the lowered function,
// thus it is useless to show it in debugger
parameter.origin = JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_PARAMETER
val rootNode = replacements.getRootMfvcNode(parameter.type.erasedUpperBound)
rootNode.createInstanceFromBox(this, irGet(parameter), AccessType.AlwaysPublic, ::variablesSaver)
.makeFlattenedGetterExpressions(this, ::registerPossibleExtraBoxUsage)
.mapIndexed { index, expression ->
savableStandaloneVariableWithSetter(
expression = expression,
name = "${parameter.name.asString()}-${rootNode.leaves[index].fullFieldName}",
origin = JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE,
saveVariable = ::variablesSaver,
)
}
}
}
+replacement.inline(wrapper.parent, newArguments)
})
}
})
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
val originalFunction = expression.symbol.owner
if (originalFunction.getReplacement() == null) return super.visitFunctionReference(expression)
return makeNewLambda(originalFunction, expression)
return makeNewLambda(originalFunction, expression, makeBody = { wrapper ->
with(context.createJvmIrBuilder(wrapper.symbol)) {
irExprBody(irCall(originalFunction).apply {
passTypeArgumentsFrom(wrapper)
for ((newParam, originalParam) in wrapper.explicitParameters zip originalFunction.explicitParameters) {
putArgument(originalParam, irGet(newParam))
}
}).transform(this@JvmMultiFieldValueClassLowering, null)
}
})
}
private fun IrFunction.getReplacement(): IrFunction? =
replacements.getReplacementFunction(this) ?: (this as? IrConstructor)?.let { replacements.getReplacementForRegularClassConstructor(it) }
private fun makeNewLambda(originalFunction: IrFunction, expression: IrFunctionReference): IrContainerExpression {
private fun makeNewLambda(
originalFunction: IrFunction, expression: IrFunctionReference, makeBody: (wrapper: IrSimpleFunction) -> IrBody
): IrContainerExpression {
val currentDeclarationParent = currentDeclarationParent!!
val wrapper = context.irFactory.buildFun {
updateFrom(originalFunction)
@@ -778,7 +812,7 @@ internal class JvmMultiFieldValueClassLowering(
returnType = originalFunction.returnType
name = originalFunction.name
visibility = DescriptorVisibilities.LOCAL
}.apply newFunction@{
}.apply {
parent = currentDeclarationParent
assert(typeParameters.isEmpty())
copyTypeParametersFrom(originalFunction)
@@ -792,14 +826,7 @@ internal class JvmMultiFieldValueClassLowering(
param.copyTo(this, index = index, type = param.type.substitute(substitutionMap))
}
withinScope(this) {
body = with(context.createJvmIrBuilder(symbol)) {
irExprBody(irCall(originalFunction).apply {
passTypeArgumentsFrom(this@newFunction)
for ((newParam, originalParam) in explicitParameters zip originalFunction.explicitParameters) {
putArgument(originalParam, irGet(newParam))
}
}).transform(this@JvmMultiFieldValueClassLowering, null)
}
body = makeBody(this)
postActionAfterTransformingClassDeclaration(this)
}
}
@@ -836,11 +863,14 @@ internal class JvmMultiFieldValueClassLowering(
return when {
function is IrConstructor && function.isPrimary && function.constructedClass.isMultiFieldValueClass &&
currentScope.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
context.createIrBuilder(currentScope.symbol).irBlock {
context.createJvmIrBuilder(currentScope.symbol, expression).irBlock {
val rootNode = replacements.getRootMfvcNode(function.constructedClass)
val instance = rootNode.createInstanceFromValueDeclarationsAndBoxType(
this, function.constructedClassType as IrSimpleType, Name.identifier("constructor_tmp"), ::variablesSaver
)
for (valueDeclaration in instance.valueDeclarations) {
valueDeclaration.origin = JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE
}
flattenExpressionTo(expression, instance)
val getterExpression = instance.makeGetterExpression(this, ::registerPossibleExtraBoxUsage)
valueDeclarationsRemapper.registerReplacement(getterExpression, instance)
@@ -848,7 +878,7 @@ internal class JvmMultiFieldValueClassLowering(
}
}
replacement != null -> context.createIrBuilder(currentScope.symbol).irBlock {
replacement != null -> context.createJvmIrBuilder(currentScope.symbol, expression).irBlock {
buildReplacement(function, expression, replacement)
}.unwrapBlock()
@@ -856,7 +886,7 @@ internal class JvmMultiFieldValueClassLowering(
val newConstructor = (function as? IrConstructor)
?.let { replacements.getReplacementForRegularClassConstructor(it) }
?: return super.visitFunctionAccess(expression)
context.createIrBuilder(currentScope.symbol).irBlock {
context.createJvmIrBuilder(currentScope.symbol, expression).irBlock {
buildReplacement(function, expression, newConstructor)
}.unwrapBlock()
}
@@ -875,14 +905,14 @@ internal class JvmMultiFieldValueClassLowering(
) {
require(callee.valueParameters.isEmpty()) { "Unexpected getter:\n${callee.dump()}" }
expression.dispatchReceiver = expression.dispatchReceiver?.transform(this, null)
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
return context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).irBlock {
with(valueDeclarationsRemapper) {
addReplacement(expression) ?: return expression
}
}.unwrapBlock()
}
if (expression.isSpecializedMFVCEqEq) {
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
return context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).irBlock {
val leftArgument = expression.getValueArgument(0)!!
val rightArgument = expression.getValueArgument(1)!!
val leftClass = leftArgument.type.erasedUpperBound
@@ -971,7 +1001,7 @@ internal class JvmMultiFieldValueClassLowering(
for (i in expression.arguments.indices) {
val argument = expression.arguments[i]
if (argument.type.needsMfvcFlattening()) {
expression.arguments[i] = context.createIrBuilder(getCurrentScopeSymbol()).run {
expression.arguments[i] = context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).run {
val toString = argument.type.erasedUpperBound.functions.single { it.isToString() }
require(toString.typeParameters.isEmpty()) { "Bad toString: ${toString.render()}" }
irCall(toString).apply {
@@ -1087,7 +1117,7 @@ internal class JvmMultiFieldValueClassLowering(
override fun visitGetField(expression: IrGetField): IrExpression {
expression.receiver = expression.receiver?.transform(this, null)
with(valueDeclarationsRemapper) {
return context.createIrBuilder(expression.symbol).irBlock {
return context.createJvmIrBuilder(expression.symbol, expression).irBlock {
addReplacement(expression) ?: return expression
}.unwrapBlock()
}
@@ -1096,7 +1126,7 @@ internal class JvmMultiFieldValueClassLowering(
override fun visitSetField(expression: IrSetField): IrExpression {
expression.receiver = expression.receiver?.transform(this, null)
with(valueDeclarationsRemapper) {
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
return context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).irBlock {
addReplacement(expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
?: return expression.also { it.value = it.value.transform(this@JvmMultiFieldValueClassLowering, null) }
}.unwrapBlock()
@@ -1105,22 +1135,23 @@ internal class JvmMultiFieldValueClassLowering(
override fun visitGetValue(expression: IrGetValue): IrExpression =
with(valueDeclarationsRemapper) {
context.createIrBuilder(getCurrentScopeSymbol()).makeReplacement(expression) ?: super.visitGetValue(expression)
context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).makeReplacement(expression) ?: super.visitGetValue(expression)
}
override fun visitSetValue(expression: IrSetValue): IrExpression = context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
with(valueDeclarationsRemapper) {
addReplacement(expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
?: return super.visitSetValue(expression)
}
}.unwrapBlock()
override fun visitSetValue(expression: IrSetValue): IrExpression =
context.createJvmIrBuilder(getCurrentScopeSymbol(), expression).irBlock {
with(valueDeclarationsRemapper) {
addReplacement(expression, safe = expression.origin != UNSAFE_MFVC_SET_ORIGIN)
?: return super.visitSetValue(expression)
}
}.unwrapBlock()
override fun visitVariable(declaration: IrVariable): IrStatement {
val initializer = declaration.initializer
if (declaration.type.needsMfvcFlattening()) {
val irClass = declaration.type.erasedUpperBound
val rootNode = replacements.getRootMfvcNode(irClass)
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
return context.createJvmIrBuilder(getCurrentScopeSymbol(), declaration).irBlock {
val instance = rootNode.createInstanceFromValueDeclarationsAndBoxType(
this, declaration.type as IrSimpleType, declaration.name, ::variablesSaver
)
@@ -1147,7 +1178,7 @@ internal class JvmMultiFieldValueClassLowering(
val variables = rootMfvcNode.leaves.map {
savableStandaloneVariable(
type = it.type.substitute(typeArguments),
origin = IrDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE,
origin = IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
saveVariable = ::variablesSaver
)
}
@@ -1191,12 +1222,7 @@ internal class JvmMultiFieldValueClassLowering(
}
if (expression is IrTry) {
expression.tryResult = irBlock { flattenExpressionTo(expression.tryResult, instance) }.unwrapBlock()
expression.catches.replaceAll {
irCatch(
it.catchParameter,
irBlock { flattenExpressionTo(it.result, instance) }.unwrapBlock()
)
}
expression.catches.replaceAll { irCatch(it.catchParameter, irBlock { flattenExpressionTo(it.result, instance) }.unwrapBlock()) }
expression.finallyExpression = expression.finallyExpression?.transform(lowering, null)
+expression
return
@@ -1414,63 +1440,62 @@ private fun IrBody.makeBodyWithAddedVariables(context: JvmBackendContext, variab
*/
private fun BlockOrBody.makeBodyWithAddedVariables(context: JvmBackendContext, variables: Set<IrVariable>, symbol: IrSymbol): IrElement {
if (variables.isEmpty()) return element
extractVariablesSettersToOuterPossibleBlock(variables)
val nearestBlocks = findNearestBlocksForVariables(variables, this)
val containingVariables: Map<BlockOrBody, List<IrVariable>> = nearestBlocks.entries
.mapNotNull { (k, v) -> if (v != null) k to v else null }
.groupBy({ (_, v) -> v }, { (k, _) -> k })
return element.transform(object : IrElementTransformerVoid() {
private fun getFlattenedStatements(container: IrStatementContainer): Sequence<IrStatement> = sequence {
for (statement in container.statements) {
if (statement is IrStatementContainer) {
yieldAll(getFlattenedStatements(statement))
} else {
yield(statement)
}
private fun getFirstInnerStatement(statement: IrStatement): IrStatement? =
if (statement is IrStatementContainer) statement.statements.first().let(::getFirstInnerStatement) else statement
private fun removeFirstInnerStatement(statement: IrStatement): IrStatement? {
if (statement !is IrStatementContainer) return null
val innerResult = removeFirstInnerStatement(statement.statements[0])
return when {
innerResult != null -> statement.also { statement.statements[0] = innerResult }
statement.statements.size > 1 -> statement.also { statement.statements.removeAt(0) }
else -> null
}
}
private fun removeFlattenedStatements(container: IrStatementContainer, toRemove: Int): Int {
var removed = 0
var removedDirectly = 0
for (statement in container.statements) {
require(removed <= toRemove) { "Removed: $removed, To remove: $toRemove" }
if (removed == toRemove) break
if (statement is IrStatementContainer) {
val nestedRemoved = removeFlattenedStatements(statement, toRemove - removed)
removed += nestedRemoved
if (statement.statements.isEmpty()) {
removedDirectly++
}
} else {
removed++
removedDirectly++
}
private fun IrStatement.removeInnerEmptyBlocks() {
if (this !is IrContainerExpression || statements.isEmpty()) return
val emptyBlocks = statements.mapNotNull {
it.removeInnerEmptyBlocks()
if (it is IrContainerExpression && it.statements.isEmpty()) it else null
}
require(removed <= toRemove) { "Removed: $removed, To remove: $toRemove" }
if (removedDirectly > 0) container.statements.replaceAll(container.statements.drop(removedDirectly))
return removed
statements.removeAll(emptyBlocks)
}
private fun replaceSetVariableWithInitialization(variables: List<IrVariable>, container: IrStatementContainer) {
val variablesSet = variables.toSet()
val statementsWithoutUsages = container.statements.takeWhile { !it.containsUsagesOf(variablesSet) }
container.statements.replaceAll(container.statements.drop(statementsWithoutUsages.size))
val values = buildList {
for ((variable, statement) in variables.asSequence() zip getFlattenedStatements(container)) {
when {
variable.initializer != null -> break
statement !is IrSetValue -> break
statement.symbol.owner != variable -> break
else -> add(statement.value)
require(variables.all { it.initializer == null }) { "Variables must have no initializer" }
val variableFirstUsage = variables.associateWith { v -> container.statements.firstOrNull { it.containsUsagesOf(setOf(v)) } }
val variableDeclarationPerStatement = variableFirstUsage.entries
.mapNotNull { (variable, firstUsage) -> if (firstUsage == null) null else firstUsage to variable }
.groupBy({ (k, _) -> k }, { (_, v) -> v })
if (variableDeclarationPerStatement.isEmpty()) return
val newStatements = buildList {
for (statement in container.statements) {
statement.removeInnerEmptyBlocks()
if (statement is IrContainerExpression && statement.statements.isEmpty()) continue
val varsBefore = variableDeclarationPerStatement[statement]
if (varsBefore != null) {
addAll(varsBefore)
val innerStatement = getFirstInnerStatement(statement)
if (innerStatement is IrSetValue) {
val assignedVariable = innerStatement.symbol.owner
if (assignedVariable is IrVariable && assignedVariable in varsBefore && assignedVariable.initializer == null) {
assignedVariable.initializer = innerStatement.value
addIfNotNull(removeFirstInnerStatement(statement))
continue
}
}
}
add(statement)
}
}
for ((variable, value) in variables zip values) {
variable.initializer = value
}
removeFlattenedStatements(container, values.size)
container.statements.addAll(0, statementsWithoutUsages + variables)
container.statements.replaceAll(newStatements)
}
override fun visitBlock(expression: IrBlock): IrExpression {
@@ -1505,6 +1530,41 @@ private fun BlockOrBody.makeBodyWithAddedVariables(context: JvmBackendContext, v
}, null)
}
private fun BlockOrBody.extractVariablesSettersToOuterPossibleBlock(variables: Set<IrVariable>) {
element.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitContainerExpression(expression: IrContainerExpression) {
super.visitContainerExpression(expression)
visitStatementContainer(expression)
}
override fun visitBlockBody(body: IrBlockBody) {
super.visitBlockBody(body)
visitStatementContainer(body)
}
private fun visitStatementContainer(container: IrStatementContainer) {
val newStatements = buildList {
for (statement in container.statements) {
if (statement is IrStatementContainer) {
val extracted = statement.statements.takeWhile { it is IrSetValue && it.symbol.owner in variables }
if (extracted.isNotEmpty()) {
statement.statements.replaceAll(statement.statements.drop(extracted.size))
addAll(extracted)
}
if (statement.statements.isEmpty()) continue
}
add(statement)
}
}
container.statements.replaceAll(newStatements)
}
})
}
private fun <T> MutableList<T>.replaceAll(replacement: List<T>) {
clear()
addAll(replacement)
@@ -159,6 +159,14 @@ class JvmBackendContext(
state.mapInlineClass = { descriptor ->
defaultTypeMapper.mapType(referenceClass(descriptor).defaultType)
}
state.multiFieldValueClassUnboxInfo = lambda@{ descriptor ->
val irClass = symbolTable.lazyWrapper.referenceClass(descriptor).owner
val node = multiFieldValueClassReplacements.getRootMfvcNodeOrNull(irClass) ?: return@lambda null
val leavesInfo =
node.leaves.map { Triple(defaultTypeMapper.mapType(it.type), it.fullMethodName.asString(), it.fullFieldName.asString()) }
GenerationState.MultiFieldValueClassUnboxInfo(leavesInfo)
}
}
fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol =
@@ -15,8 +15,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
object LAMBDA_IMPL : IrDeclarationOriginImpl("LAMBDA_IMPL")
object FUNCTION_REFERENCE_IMPL : IrDeclarationOriginImpl("FUNCTION_REFERENCE_IMPL", isSynthetic = true)
object MFVC_PRIMARY_CONSTRUCTOR_REFERENCE_HELPER :
IrDeclarationOriginImpl("MFVC_PRIMARY_CONSTRUCTOR_REFERENCE_HELPER", isSynthetic = true)
object SYNTHETIC_ACCESSOR : IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR", isSynthetic = true)
object SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR :
IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR", isSynthetic = true)
@@ -40,6 +38,10 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object STATIC_INLINE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_CONSTRUCTOR")
object STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR")
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
object GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER")
object TEMPORARY_MULTI_FIELD_VALUE_CLASS_PARAMETER : IrDeclarationOriginImpl("TEMPORARY_MULTI_FIELD_VALUE_CLASS_PARAMETER")
object TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE : IrDeclarationOriginImpl("TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE")
object MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE")
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true)
@@ -142,7 +142,7 @@ class MemoizedMultiFieldValueClassReplacements(
newFlattenedParameters.add(newParameters)
}
newFlattenedParameters += sourceFunction.valueParameters.drop(sourceFunction.contextReceiverParametersCount)
.grouped(name = null, substitutionMap, targetFunction, IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER)
.grouped(name = null, substitutionMap, targetFunction, JvmLoweredDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER)
return newFlattenedParameters
}
@@ -58,7 +58,7 @@ fun MfvcNode.createInstanceFromValueDeclarations(
scope.savableStandaloneVariable(
type = it.type,
name = listOf(name, it.fullFieldName).joinToString("-"),
origin = IrDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE,
origin = JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE,
saveVariable = saveVariable
)
}
@@ -103,7 +103,7 @@ internal class ExpressionCopierImpl(
} else SavedToVariable(
scope.savableStandaloneVariableWithSetter(
this@orSavedToVariable,
origin = IrDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
origin = JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
saveVariable = saveVariable,
isTemporary = true,
)
@@ -178,7 +178,7 @@ class ReceiverBasedMfvcNodeInstance(
val value = makeGetterExpression(scope, registerPossibleExtraBoxCreation = { /* The box is definitely useful */ })
val asVariable = scope.savableStandaloneVariableWithSetter(
value,
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER,
origin = JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
saveVariable = saveVariable,
isTemporary = true,
)
@@ -243,7 +243,9 @@ fun IrBuilderWithScope.savableStandaloneVariable(
name: String? = null,
isMutable: Boolean = false,
origin: IrDeclarationOrigin,
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
|| origin == JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE
|| origin == JvmLoweredDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_PARAMETER,
saveVariable: (IrVariable) -> Unit,
): IrVariable {
val variable = if (isTemporary || name == null) scope.createTemporaryVariableDeclaration(
@@ -43,9 +43,6 @@ interface IrDeclarationOrigin {
object GENERATED_DATA_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_DATA_CLASS_MEMBER")
object GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER")
object TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE : IrDeclarationOriginImpl("TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE")
object MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE")
object LOCAL_FUNCTION : IrDeclarationOriginImpl("LOCAL_FUNCTION")
object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA")
object CATCH_PARAMETER : IrDeclarationOriginImpl("CATCH_PARAMETER")
@@ -1,7 +1,6 @@
@kotlin.Metadata
public final class ClassFlatteningKt {
// source: 'classFlattening.kt'
private final static method box$lambda$3$lambda$2(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String, p6: int, p7: int, p8: java.lang.String): void
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method gmfvc-Ket90g4(p0: int, p1: int, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String, p4: int, p5: int, p6: int, @org.jetbrains.annotations.NotNull p7: java.lang.String, p8: int): int
public final static method ic-K5cTq2M(p0: int): int
@@ -63,7 +63,6 @@ public final class DefaultParametersKt {
public final static @org.jetbrains.annotations.NotNull method complexFun-552ch2I(p0: double, p1: double, p2: double, p3: double, p4: double, p5: double): java.lang.String
public synthetic static method complexInlineFun-552ch2I$default(p0: double, p1: double, p2: double, p3: double, p4: double, p5: double, p6: int, p7: java.lang.Object): java.lang.String
public final static @org.jetbrains.annotations.NotNull method complexInlineFun-552ch2I(p0: double, p1: double, p2: double, p3: double, p4: double, p5: double): java.lang.String
public final inner class kotlin/jvm/internal/Ref$DoubleRef
}
@kotlin.Metadata
File diff suppressed because it is too large Load Diff
@@ -3,38 +3,13 @@
// WITH_COROUTINES
// TARGET_BACKEND: JVM_IR
// LANGUAGE: +ValueClasses
// FIR_IDENTICAL
// FILE: caller.kt
import kotlin.coroutines.*
fun runSuspend(block: suspend () -> Unit) {
val run = RunSuspend()
block.startCoroutine(run)
run.await()
}
private class RunSuspend : Continuation<Unit> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
var result: Result<Unit>? = null
override fun resumeWith(result: Result<Unit>) = synchronized(this) {
this.result = result
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).notifyAll()
}
fun await() = synchronized(this) {
while (true) {
when (val result = this.result) {
null -> @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).wait()
else -> {
result.getOrThrow() // throw up failure
return
}
}
}
}
block.startCoroutine(Continuation(EmptyCoroutineContext) { it.getOrThrow() })
}
// FILE: test.kt
@@ -233,6 +233,17 @@ public final class AnotherKt {
public final static @org.jetbrains.annotations.Nullable method requiresF-lIoT8es(p0: double, p1: double, @org.jetbrains.annotations.NotNull p2: F, @org.jetbrains.annotations.NotNull p3: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
public final class CallerKt$runSuspend$$inlined$Continuation$1 {
// source: 'Continuation.kt'
enclosing method CallerKt.runSuspend(Lkotlin/jvm/functions/Function1;)V
synthetic final field $context: kotlin.coroutines.CoroutineContext
inner (anonymous) class CallerKt$runSuspend$$inlined$Continuation$1
public method <init>(p0: kotlin.coroutines.CoroutineContext): void
public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext
public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void
}
@kotlin.Metadata
public final class CallerKt {
// source: 'caller.kt'
@@ -306,18 +317,6 @@ public interface F {
public abstract @org.jetbrains.annotations.Nullable method run-GPBa7dw(p0: double, p1: double, @org.jetbrains.annotations.NotNull p2: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
final class RunSuspend {
// source: 'caller.kt'
private @org.jetbrains.annotations.Nullable field result: kotlin.Result
public method <init>(): void
public final method await(): void
public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext
public final @org.jetbrains.annotations.Nullable method getResult-xLWZpok(): kotlin.Result
public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void
public final method setResult(@org.jetbrains.annotations.Nullable p0: kotlin.Result): void
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class TestKt$box$10 {
@@ -2049,11 +2048,6 @@ public final class TestKt {
inner (anonymous) class TestKt$box$97
inner (anonymous) class TestKt$box$98
inner (anonymous) class TestKt$box$99
private final static method box$lambda$2$lambda$1(p0: double, p1: double, p2: double, p3: double): DPoint
private final static method box$lambda$4$lambda$3(p0: double, p1: double, p2: double, p3: double): DPoint
private final static method box$lambda$7$lambda$6(p0: double, p1: double): DPoint
private final static method box$stub_for_inlining$0$stub_for_inlining(p0: double, p1: double, p2: double, p3: double): DPoint
private final static method box$stub_for_inlining$9$stub_for_inlining$8(p0: double, p1: double): DPoint
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static @org.jetbrains.annotations.NotNull method consume-lIoT8es(p0: double, p1: double, p2: double, p3: double, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function2): DPoint
public final static @org.jetbrains.annotations.NotNull method consumeInline-lIoT8es(p0: double, p1: double, p2: double, p3: double, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function2): DPoint
@@ -0,0 +1,85 @@
// LANGUAGE: +ValueClasses
// TARGET_BACKEND: JVM_IR
// CHECK_BYTECODE_LISTING
// WITH_STDLIB
// CHECK_BYTECODE_TEXT
// FIR_IDENTICAL
// FILE: caller.kt
import kotlin.coroutines.*
fun runSuspend(block: suspend () -> Unit) {
block.startCoroutine(Continuation(EmptyCoroutineContext) { it.getOrThrow() })
}
// FILE: dependency.kt
@JvmInline
value class DPoint(val x: Double, val y: Double)
@JvmInline
value class DSegment(val p1: DPoint, val p2: DPoint)
inline fun DSegment.myLet(f: (DSegment) -> DPoint) = f(this)
// FILE: test.kt
import kotlin.coroutines.suspendCoroutine
import kotlin.coroutines.resume
fun supply(x: Any?) = Unit
fun box(): String {
val point = DPoint(1.0, 2.0)
val pointX2 = DPoint(2.0, 4.0)
val segment = DSegment(point, pointX2)
supply("a")
point.let { it.x }
supply("b")
point.let { it }
supply("c")
run { DPoint (1.0, 2.0) }
supply("d")
val x = run { DPoint(100.0, 200.0) }
supply("e")
require(x == DPoint(100.0, 200.0))
supply("f")
point.let { DPoint(2 * it.x, 2 * it.y) }
supply("g")
require(point == point.let { it })
supply("h")
require(pointX2 == point.let { DPoint(2 * it.x, 2 * it.y) })
supply("i")
segment.myLet { it.p1 }
supply("j")
segment.myLet { it.p2 }
supply("k")
require(segment.myLet { it.p1 } == point)
supply("l")
require(segment.myLet { it.p2 } == pointX2)
supply("m")
require(segment.let { it.let { it } } == segment)
supply("n")
var a = 1
segment.let { a++ }
val b = segment.let { ++a }
require(a == 3)
supply("o")
runSuspend { require(suspendFun() == DPoint(1.0, 2.0).toString()) }
supply("p")
return "OK"
}
suspend fun f() = suspendCoroutine { it.resume(Unit) }
suspend fun suspendFun(): String {
val x = run { f(); DPoint(1.0, 2.0) }
return x.toString()
}
// @TestKt.class:
// 0 valueOf
// 0 INVOKE(STATIC|VIRTUAL) (DPoint|DSegment).*\.(un)?box
// 0 INVOKE(STATIC|VIRTUAL) .*(stub_for_inlining|lambda)
// 0 DCONST_0
@@ -0,0 +1,110 @@
@kotlin.Metadata
public final class CallerKt$runSuspend$$inlined$Continuation$1 {
// source: 'Continuation.kt'
enclosing method CallerKt.runSuspend(Lkotlin/jvm/functions/Function1;)V
synthetic final field $context: kotlin.coroutines.CoroutineContext
inner (anonymous) class CallerKt$runSuspend$$inlined$Continuation$1
public method <init>(p0: kotlin.coroutines.CoroutineContext): void
public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext
public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void
}
@kotlin.Metadata
public final class CallerKt {
// source: 'caller.kt'
public final static method runSuspend(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class DPoint {
// source: 'dependency.kt'
private final field x: double
private final field y: double
private synthetic method <init>(p0: double, p1: double): void
public synthetic final static method box-impl(p0: double, p1: double): DPoint
public final static method constructor-impl(p0: double, p1: double): void
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
public static method equals-impl(p0: double, p1: double, p2: java.lang.Object): boolean
public final static method equals-impl0(p0: double, p1: double, p2: double, p3: double): boolean
public final method getX(): double
public final method getY(): double
public method hashCode(): int
public static method hashCode-impl(p0: double, p1: double): int
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
public static method toString-impl(p0: double, p1: double): java.lang.String
public synthetic final method unbox-impl-x(): double
public synthetic final method unbox-impl-y(): double
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class DSegment {
// source: 'dependency.kt'
private final field p1-x: double
private final field p1-y: double
private final field p2-x: double
private final field p2-y: double
private synthetic method <init>(p0: double, p1: double, p2: double, p3: double): void
public synthetic final static method box-impl(p0: double, p1: double, p2: double, p3: double): DSegment
public final static method constructor-impl(p0: double, p1: double, p2: double, p3: double): void
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
public static method equals-impl(p0: double, p1: double, p2: double, p3: double, p4: java.lang.Object): boolean
public final static method equals-impl0(p0: double, p1: double, p2: double, p3: double, p4: double, p5: double, p6: double, p7: double): boolean
public final @org.jetbrains.annotations.NotNull method getP1(): DPoint
public final @org.jetbrains.annotations.NotNull method getP2(): DPoint
public method hashCode(): int
public static method hashCode-impl(p0: double, p1: double, p2: double, p3: double): int
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
public static method toString-impl(p0: double, p1: double, p2: double, p3: double): java.lang.String
public synthetic final method unbox-impl-p1(): DPoint
public synthetic final method unbox-impl-p1-x(): double
public synthetic final method unbox-impl-p1-y(): double
public synthetic final method unbox-impl-p2(): DPoint
public synthetic final method unbox-impl-p2-x(): double
public synthetic final method unbox-impl-p2-y(): double
}
@kotlin.Metadata
public final class DependencyKt {
// source: 'dependency.kt'
public final static @org.jetbrains.annotations.NotNull method myLet-GPBa7dw(p0: double, p1: double, p2: double, p3: double, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function1): DPoint
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class TestKt$box$13 {
// source: 'test.kt'
enclosing method TestKt.box()Ljava/lang/String;
field label: int
inner (anonymous) class TestKt$box$13
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): java.lang.Object
public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class TestKt$suspendFun$1 {
// source: 'test.kt'
enclosing method TestKt.suspendFun(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
field label: int
synthetic field result: java.lang.Object
inner (anonymous) class TestKt$suspendFun$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class TestKt {
// source: 'test.kt'
inner (anonymous) class TestKt$box$13
inner (anonymous) class TestKt$suspendFun$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static @org.jetbrains.annotations.Nullable method f(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final static method supply(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void
public final static @org.jetbrains.annotations.Nullable method suspendFun(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final inner class kotlin/jvm/internal/Ref$IntRef
}
@@ -12,13 +12,23 @@ class Box(var value: DPoint)
fun supplier(index: Int) {} // to make usage of the argument
fun supplier(index: Int, x: DPoint) {} // to make usage of the argument
fun `1`() = 1.0
fun `2`() = 2.0
fun `3`() = 3.0
fun `4`() = 4.0
fun `5`() = 5.0
fun `6`() = 6.0
fun `7`() = 7.0
fun `8`() = 8.0
fun reassignVariable(x: DPoint, box: Box) {
supplier(100)
var p = DPoint(1.0, 2.0) // should not use temporary variables
var p = DPoint(`1`(), `2`()) // should not use temporary variables
supplier(101, p)
p = p // should not use temporary variables
supplier(102, p)
p = DPoint(3.0, 4.0) // should use tempVars
p = DPoint(`3`(), `4`()) // should use tempVars
supplier(103, p)
p = x // should not use temporary variables
supplier(104, p)
@@ -30,13 +40,13 @@ fun reassignVariable(x: DPoint, box: Box) {
fun reassignField(x: DPoint, box: Box) {
supplier(107)
val p = DPoint(5.0, 6.0)
val p = DPoint(`5`(), `6`())
supplier(108, p)
var b = Box(p) // should not use temporary variables
supplier(109)
b.value = b.value // should not use temporary variables
supplier(110)
b.value = DPoint(7.0, 8.0) // should use tempVars
b.value = DPoint(`7`(), `8`()) // should use tempVars
supplier(111)
b.value = x // should not use temporary variables
supplier(112)
@@ -7,12 +7,17 @@
@JvmInline
value class DPoint(val x: Double, val y: Double)
fun `1`() = 1.0
fun `2`() = 2.0
fun `3`() = 3.0
fun `4`() = 4.0
fun acceptBoxed(x: Any?) {}
fun acceptFlattened(x: DPoint) {}
fun returnBoxed() = DPoint(3.0, 4.0)
fun returnBoxed() = DPoint(`3`(), `4`())
fun testFlattened2Boxed() {
acceptBoxed(DPoint(1.0, 2.0))
acceptBoxed(DPoint(`1`(), `2`()))
}
fun testBoxed2Boxed() {
@@ -20,7 +25,7 @@ fun testBoxed2Boxed() {
}
fun testFlattened2Flattened() {
acceptFlattened(DPoint(1.0, 2.0))
acceptFlattened(DPoint(`1`(), `2`()))
}
fun testBoxed2Flattened() {
@@ -28,8 +33,8 @@ fun testBoxed2Flattened() {
}
fun testIgnoredFlattened() {
DPoint(1.0, 2.0)
DPoint(1.0, 2.0)
DPoint(`1`(), `2`())
DPoint(`1`(), `2`())
}
fun testIgnoredBoxed() {
@@ -38,8 +43,8 @@ fun testIgnoredBoxed() {
object Init {
init {
DPoint(1.0, 2.0)
DPoint(1.0, 2.0)
DPoint(`1`(), `2`())
DPoint(`1`(), `2`())
}
}
@@ -50501,6 +50501,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/valueClasses/functionReferences.kt");
}
@Test
@TestMetadata("inlineFunctions.kt")
public void testInlineFunctions() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/inlineFunctions.kt");
}
@Test
@TestMetadata("mfvcBothEqualsOverride.kt")
public void testMfvcBothEqualsOverride() throws Exception {