From be75e1bce9711cfb93303d32817894e5f2f227d2 Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Fri, 10 Feb 2017 13:43:28 +0300 Subject: [PATCH] IR: make vararg lowering more specific in array type creation (e.g. instaed of Array use IntArray) --- .../backend/konan/lower/VarargLowering.kt | 98 +++++++++++++------ 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt index 9140613dc43..3aa2539871d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt @@ -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 { val varargArgs = mutableMapOf() - 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? { + 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): IrExpression? { + private fun calculateArraySize(arrayHandle: ArrayHandle, hasSpreadElement: Boolean, scope:Scope, expression: IrVararg, vars: Map): 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 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 = irConst(kind = IrConstKind.Int, value = value, type = kIntType) +private fun Offset.irConstInt(value: Int): IrConst = irConst(kind = IrConstKind.Int, value = value, type = KonanPlatform.builtIns.intType) private fun Offset.irConst(kind: IrConstKind, value: T, type: KotlinType): IrConst = IrConstImpl(startOffset, endOffset,type, kind,value) private fun Offset.irBlock(type: KotlinType): IrBlock = IrBlockImpl(startOffset, endOffset, type) private fun Offset.irCall(descriptor: CallableDescriptor, typeArguments: Map?): IrCall = IrCallImpl(startOffset, endOffset, descriptor, typeArguments)