IR: make vararg lowering more specific in array type creation (e.g. instaed of Array<Int> use IntArray)
This commit is contained in:
committed by
vvlevchenko
parent
1eebae5c4e
commit
be75e1bce9
+70
-28
@@ -1,13 +1,12 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPlatform
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.konan.lower.VarargInjectionLowering.Companion.kIntType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
@@ -16,7 +15,8 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -59,7 +59,6 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
|
||||
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<ValueParameterDescriptor, IrBlock> {
|
||||
val varargArgs = mutableMapOf<ValueParameterDescriptor, IrBlock>()
|
||||
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }!!
|
||||
val scope = owner.scope()
|
||||
val calleeDescriptor = expression.descriptor
|
||||
calleeDescriptor.valueParameters
|
||||
@@ -73,10 +72,12 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
log("skipped vararg expression because it's string array literal")
|
||||
return@forEach
|
||||
}
|
||||
val block = irBlock(kArrayType.defaultType)
|
||||
val arrayHandle = arrayType(type)
|
||||
val arrayConstructor = arrayHandle.arrayDescriptor.constructors.find { it.valueParameters.size == 1 }!!
|
||||
val block = irBlock(arrayHandle.arrayDescriptor.defaultType)
|
||||
val arrayConstructorCall = irCall(
|
||||
descriptor = arrayConstructor,
|
||||
typeArguments = mapOf(arrayConstructor.typeParameters[0] to type))
|
||||
typeArguments = typeArgument(arrayConstructor, type))
|
||||
|
||||
if (parameterExpression == null) {
|
||||
arrayConstructorCall.putValueArgument(0, kIntZero)
|
||||
@@ -90,7 +91,7 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
block.statements.add(initVar)
|
||||
it to initVar
|
||||
}.toMap()
|
||||
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, parameterExpression, vars))
|
||||
arrayConstructorCall.putValueArgument(0, calculateArraySize(arrayHandle, hasSpreadElement, scope, parameterExpression, vars))
|
||||
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
|
||||
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
|
||||
block.statements.add(arrayTmpVariable)
|
||||
@@ -103,7 +104,7 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
val dst = vars[element]!!
|
||||
if (element !is IrSpreadElement) {
|
||||
val setArrayElementCall = irCall(
|
||||
descriptor = kArraySetFunctionDescriptor,
|
||||
descriptor = arrayHandle.setMethodDescriptor,
|
||||
typeArguments = null
|
||||
)
|
||||
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
|
||||
@@ -114,9 +115,9 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
block.statements.add(incrementVariable(indexTmpVariable.descriptor, kIntOne))
|
||||
}
|
||||
} else {
|
||||
val arraySizeVariable = scope.createTemporaryVariable(irArraySize(irGet(dst.descriptor)), "__length\$")
|
||||
val arraySizeVariable = scope.createTemporaryVariable(irArraySize(arrayHandle, irGet(dst.descriptor)), "__length\$")
|
||||
block.statements.add(arraySizeVariable)
|
||||
val copyCall = irCall(kCopyRangeToDescriptor, null).apply {
|
||||
val copyCall = irCall(arrayHandle.copyRangeToDescriptor, null).apply {
|
||||
extensionReceiver = irGet(dst.descriptor)
|
||||
putValueArgument(0, irGet(arrayTmpVariable.descriptor)) /* destination */
|
||||
putValueArgument(1, kIntZero) /* fromIndex */
|
||||
@@ -138,6 +139,26 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
return varargArgs
|
||||
}
|
||||
|
||||
private fun typeArgument(arrayConstructor: ClassConstructorDescriptor, type: KotlinType):Map<TypeParameterDescriptor, KotlinType>? {
|
||||
return if (!arrayConstructor.typeParameters.isEmpty())
|
||||
mapOf(arrayConstructor.typeParameters.first() to type)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
private fun arrayType(type: KotlinType): ArrayHandle {
|
||||
return when {
|
||||
KotlinBuiltIns.isByte(type) -> kByteArrayHandler
|
||||
KotlinBuiltIns.isShort(type) -> kShortArrayHandler
|
||||
KotlinBuiltIns.isChar(type) -> kCharArrayHandler
|
||||
KotlinBuiltIns.isInt(type) -> kIntArrayHandler
|
||||
KotlinBuiltIns.isLong(type) -> kLongArrayHandler
|
||||
KotlinBuiltIns.isFloat(type) -> kFloatArrayHandler
|
||||
KotlinBuiltIns.isDouble(type) -> kDoubleArrayHandler
|
||||
else -> kArrayHandler
|
||||
}
|
||||
}
|
||||
|
||||
private fun Offset.intPlus() = irCall(kIntPlusDescriptor, null)
|
||||
private fun Offset.increment(expression: IrExpression, value: IrExpression): IrExpression {
|
||||
return intPlus().apply {
|
||||
@@ -152,22 +173,22 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
putValueArgument(0, value)
|
||||
})
|
||||
}
|
||||
private fun calculateArraySize(hasSpreadElement: Boolean, scope:Scope, expression: IrVararg, vars: Map<IrVarargElement, IrVariable>): IrExpression? {
|
||||
private fun calculateArraySize(arrayHandle: ArrayHandle, hasSpreadElement: Boolean, scope:Scope, expression: IrVararg, vars: Map<IrVarargElement, IrVariable>): IrExpression? {
|
||||
offset(expression, scope) {
|
||||
if (!hasSpreadElement)
|
||||
return irConstInt(expression.elements.size)
|
||||
val notSpreadElementCount = expression.elements.filter { it !is IrSpreadElement}.size
|
||||
val initialValue = irConstInt(notSpreadElementCount) as IrExpression
|
||||
return vars.filter{it.key is IrSpreadElement}.toList().fold( initial = initialValue) { result, it ->
|
||||
val arraySize = irArraySize(irGet(it.second.descriptor))
|
||||
val arraySize = irArraySize(arrayHandle, irGet(it.second.descriptor))
|
||||
increment(result, arraySize)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Offset.irArraySize(expression: IrExpression): IrExpression {
|
||||
val arraySize = irCall((kArraySizeFunctionDescriptor as PropertyDescriptor).getter as FunctionDescriptor, null).apply {
|
||||
private fun Offset.irArraySize(arrayHandle: ArrayHandle, expression: IrExpression): IrExpression {
|
||||
val arraySize = irCall((arrayHandle.sizeDescriptor as PropertyDescriptor).getter as FunctionDescriptor, null).apply {
|
||||
dispatchReceiver = expression
|
||||
}
|
||||
return arraySize
|
||||
@@ -180,24 +201,45 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
||||
context.log("VARARG-INJECTOR: $msg")
|
||||
}
|
||||
|
||||
companion object {
|
||||
val kArrayType = KonanPlatform.builtIns.array
|
||||
val kCollectionsPackageDescriptor = KonanPlatform.builtIns.builtInsModule.getPackage(FqName("kotlin.collections")).memberScope
|
||||
val kCopyRangeToDescriptor = DescriptorUtils.getAllDescriptors(kCollectionsPackageDescriptor).filter {
|
||||
it.name.asString() == "copyRangeTo"
|
||||
&& DescriptorUtils.getClassDescriptorForType((it as FunctionDescriptor).extensionReceiverParameter!!.type) == kArrayType}.first() as CallableDescriptor
|
||||
val unsubstitutedMemberScope = kArrayType.unsubstitutedMemberScope
|
||||
val kArraySetFunctionDescriptor = DescriptorUtils.getFunctionByName(unsubstitutedMemberScope, Name.identifier("set"))
|
||||
val kArraySizeFunctionDescriptor = DescriptorUtils.getAllDescriptors(unsubstitutedMemberScope).find { it.name.asString() == "size" }
|
||||
val kInt = KonanPlatform.builtIns.int
|
||||
val kIntType = KonanPlatform.builtIns.intType
|
||||
val kIntPlusDescriptor = DescriptorUtils.getAllDescriptors(kInt.unsubstitutedMemberScope).find {
|
||||
data class ArrayHandle(val arrayDescriptor:ClassDescriptor,
|
||||
val setMethodDescriptor: FunctionDescriptor,
|
||||
val sizeDescriptor:DeclarationDescriptor,
|
||||
val copyRangeToDescriptor:FunctionDescriptor)
|
||||
val kKotlinPackage = context.builtIns.builtInsModule.getPackage(FqName("kotlin"))
|
||||
val kByteArrayHandler = handle(arrayClassDescriptor("ByteArray"))
|
||||
val kCharArrayHandler = handle(arrayClassDescriptor("CharArray"))
|
||||
val kShortArrayHandler = handle(arrayClassDescriptor("ShortArray"))
|
||||
val kIntArrayHandler = handle(arrayClassDescriptor("IntArray"))
|
||||
val kLongArrayHandler = handle(arrayClassDescriptor("LongArray"))
|
||||
val kFloatArrayHandler = handle(arrayClassDescriptor("FloatArray"))
|
||||
val kDoubleArrayHandler = handle(arrayClassDescriptor("DoubleArray"))
|
||||
val kArrayHandler = handle(context.builtIns.array)
|
||||
|
||||
val kInt = context.builtIns.int
|
||||
val kIntType = context.builtIns.intType
|
||||
val kIntPlusDescriptor = DescriptorUtils.getAllDescriptors(kInt.unsubstitutedMemberScope).find {
|
||||
it.name.asString() == "plus"
|
||||
&& (it as FunctionDescriptor).valueParameters[0].type == kIntType} as FunctionDescriptor
|
||||
|
||||
private fun handle(descriptor:ClassDescriptor) = ArrayHandle(
|
||||
arrayDescriptor = descriptor,
|
||||
setMethodDescriptor = setMethodDescriptor(descriptor),
|
||||
sizeDescriptor = sizeMethodDescriptor(descriptor)!!,
|
||||
copyRangeToDescriptor = copyRangeToFunctionDescriptor(descriptor))
|
||||
|
||||
private fun copyRangeToFunctionDescriptor(descriptor:ClassDescriptor):FunctionDescriptor {
|
||||
val packageViewDescriptor = KonanPlatform.builtIns.builtInsModule.getPackage(FqName("kotlin.collections"))
|
||||
return packageViewDescriptor.memberScope.getContributedFunctions(Name.identifier("copyRangeTo"), NoLookupLocation.FROM_BACKEND).filter {
|
||||
it.extensionReceiverParameter != null && DescriptorUtils.getClassDescriptorForType(it.extensionReceiverParameter!!.type) == descriptor
|
||||
}.first()
|
||||
}
|
||||
private fun setMethodDescriptor(descriptor:ClassDescriptor) = methodDescriptor(descriptor, "set")
|
||||
private fun sizeMethodDescriptor(descriptor:ClassDescriptor) = descriptor(descriptor, "size")
|
||||
private fun methodDescriptor(descriptor:ClassDescriptor, methodName:String) = DescriptorUtils.getFunctionByName(descriptor.unsubstitutedMemberScope, Name.identifier(methodName))
|
||||
private fun arrayClassDescriptor(name:String) = kKotlinPackage.memberScope.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||
private fun descriptor(descriptor: ClassDescriptor, name:String) = DescriptorUtils.getAllDescriptors(descriptor.unsubstitutedMemberScope).find{it.name.asString() == name}
|
||||
inline internal fun <R> offset(ir:IrElement, scope:Scope, block: Offset.() -> R):R = offset(irContext, scope, ir.startOffset, ir.endOffset, block)
|
||||
}
|
||||
|
||||
internal class Offset(context:IrGeneratorContext, scope: Scope, startOffset:Int, endOfset:Int) : IrBuilderWithScope(context, scope, startOffset, endOfset) {
|
||||
val kIntZero = irConstInt(0)
|
||||
val kIntOne = irConstInt(1)
|
||||
@@ -208,7 +250,7 @@ internal class Offset(context:IrGeneratorContext, scope: Scope, startOffset:Int,
|
||||
|
||||
internal fun CallableDescriptor.scope() = Scope(this)
|
||||
|
||||
private fun Offset.irConstInt(value: Int): IrConst<Int> = irConst<Int>(kind = IrConstKind.Int, value = value, type = kIntType)
|
||||
private fun Offset.irConstInt(value: Int): IrConst<Int> = irConst<Int>(kind = IrConstKind.Int, value = value, type = KonanPlatform.builtIns.intType)
|
||||
private fun <T> Offset.irConst(kind: IrConstKind<T>, value: T, type: KotlinType): IrConst<T> = IrConstImpl<T>(startOffset, endOffset,type, kind,value)
|
||||
private fun Offset.irBlock(type: KotlinType): IrBlock = IrBlockImpl(startOffset, endOffset, type)
|
||||
private fun Offset.irCall(descriptor: CallableDescriptor, typeArguments: Map<TypeParameterDescriptor, KotlinType>?): IrCall = IrCallImpl(startOffset, endOffset, descriptor, typeArguments)
|
||||
|
||||
Reference in New Issue
Block a user