IR: make vararg lowering more specific in array type creation (e.g. instaed of Array<Int> use IntArray)

This commit is contained in:
Vasily Levchenko
2017-02-10 13:43:28 +03:00
committed by vvlevchenko
parent 1eebae5c4e
commit be75e1bce9
@@ -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)