[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:
committed by
Space Team
parent
a3e396e7e4
commit
40f38c8adb
+70
-11
@@ -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
|
||||
}
|
||||
|
||||
+35
-7
@@ -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)
|
||||
|
||||
+5
-7
@@ -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 ->
|
||||
|
||||
+175
-36
@@ -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 ->
|
||||
|
||||
+5
@@ -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)
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+3
-1
@@ -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 {
|
||||
|
||||
+155
-95
@@ -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 =
|
||||
|
||||
+4
-2
@@ -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)
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+12
-7
@@ -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`())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user