JVM_IR. support big arity

This commit is contained in:
Georgy Bronnikov
2018-08-31 12:00:32 +03:00
parent 03f092fd39
commit f04733ef33
22 changed files with 473 additions and 37 deletions
@@ -169,6 +169,8 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, private val
abstract val coroutineSuspendedGetter: IrSimpleFunctionSymbol
fun getFunction(parameterCount: Int) = symbolTable.referenceClass(context.builtIns.getFunction(parameterCount))
val functionReference = calc { symbolTable.referenceClass(context.getInternalClass("FunctionReference")) }
fun getFunction(name: Name, receiverType: KotlinType, vararg argTypes: KotlinType) =
@@ -57,6 +57,7 @@ class JvmLower(val context: JvmBackendContext) {
true
).runOnFilePostfix(irFile)
CallableReferenceLowering(context).lower(irFile)
FunctionNVarargInvokeLowering(context).runOnFilePostfix(irFile)
InnerClassesLowering(context).runOnFilePostfix(irFile)
InnerClassConstructorCallsLowering(context).runOnFilePostfix(irFile)
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class IrIllegalArgumentException : IntrinsicMethod() {
val exceptionTypeDescriptor = Type.getInternalName(IllegalArgumentException::class.java)
override fun toCallable(
expression: IrMemberAccessExpression,
signature: JvmMethodSignature,
context: JvmBackendContext
): IrIntrinsicFunction {
return object : IrIntrinsicFunction(expression, signature, context, listOf(JAVA_STRING_TYPE)) {
override fun genInvokeInstruction(v: InstructionAdapter) {
v.invokespecial(exceptionTypeDescriptor, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, JAVA_STRING_TYPE), false)
v.athrow()
}
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
v.anew(Type.getType(exceptionTypeDescriptor))
v.dup()
return super.invoke(v, codegen, data)
}
}
}
}
@@ -52,6 +52,7 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) {
irMapping[irBuiltIns.enumValueOf] = IrEnumValueOf()
irMapping[irBuiltIns.noWhenBranchMatchedException] = IrNoWhenBranchMatchedException()
irMapping[irBuiltIns.illegalArgumentException] = IrIllegalArgumentException()
irMapping[irBuiltIns.throwNpe] = ThrowNPE()
}
@@ -37,20 +37,14 @@ import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.toIrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaVisibilities
@@ -99,8 +93,41 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
}
}
val argumentsCount = expression.valueArgumentsCount
// Change calls to FunctionN with large N to varargs calls.
val newCall = if (argumentsCount > MAX_ARGCOUNT_WITHOUT_VARARG &&
descriptor.containingDeclaration in listOf(
context.builtIns.getFunction(argumentsCount),
context.reflectionTypes.getKFunction(argumentsCount)
)
) {
val vararg = IrVarargImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
context.ir.symbols.array.typeWith(),
context.ir.symbols.any.typeWith(),
(0 until argumentsCount).map { i -> expression.getValueArgument(i)!! }
)
val invokeFunDescriptor = context.getClass(FqName("kotlin.jvm.functions.FunctionN"))
.getFunction("invoke", listOf(expression.type.toKotlinType()))
val invokeFunSymbol = context.ir.symbols.externalSymbolTable.referenceSimpleFunction(invokeFunDescriptor)
IrCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
expression.type,
invokeFunSymbol, invokeFunDescriptor,
1,
expression.origin,
expression.superQualifier?.let { context.ir.symbols.externalSymbolTable.referenceClass(it) }
).apply {
putTypeArgument(0, expression.type)
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
putValueArgument(0, vararg)
}
} else expression
//TODO: clean
return super.visitCall(expression)
return super.visitCall(newCall)
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
@@ -155,6 +182,8 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
private val functionReferenceOrLambda = if (isLambda) context.ir.symbols.lambdaClass else context.ir.symbols.functionReference
var useVararg: Boolean = false
fun build(): BuiltFunctionReference {
val startOffset = irFunctionReference.startOffset
val endOffset = irFunctionReference.endOffset
@@ -165,9 +194,17 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
)
val numberOfParameters = unboundFunctionParameters.size
val functionClassDescriptor = context.getClass(FqName("kotlin.jvm.functions.Function$numberOfParameters"))
useVararg = (numberOfParameters > MAX_ARGCOUNT_WITHOUT_VARARG)
val functionClassDescriptor = if (useVararg)
context.getClass(FqName("kotlin.jvm.functions.FunctionN"))
else
context.getClass(FqName("kotlin.jvm.functions.Function$numberOfParameters"))
val functionParameterTypes = unboundFunctionParameters.map { it.type }
val functionClassTypeParameters = functionParameterTypes + returnType
val functionClassTypeParameters = if (useVararg)
listOf(returnType)
else
functionParameterTypes + returnType
superTypes += functionClassDescriptor.defaultType.replace(functionClassTypeParameters)
var suspendFunctionClassDescriptor: ClassDescriptor? = null
@@ -197,7 +234,17 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
endOffset = endOffset,
origin = DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL,
descriptor = functionReferenceClassDescriptor
)
).apply {
createParameterDeclarations()
val typeTranslator = TypeTranslator(context.ir.symbols.externalSymbolTable, context.state.languageVersionSettings,
enterTableScope=true)
val constantValueGenerator = ConstantValueGenerator(context.state.module, context.ir.symbols.externalSymbolTable)
typeTranslator.constantValueGenerator = constantValueGenerator
constantValueGenerator.typeTranslator = typeTranslator
functionReferenceClassDescriptor.typeConstructor.supertypes.mapTo(this.superTypes) {
typeTranslator.translateType(it)
}
}
val contributedDescriptors = mutableListOf<DeclarationDescriptor>()
val constructorBuilder = createConstructorBuilder()
@@ -205,8 +252,6 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
SimpleMemberScope(contributedDescriptors), setOf(constructorBuilder.symbol.descriptor), null
)
functionReferenceClass.createParameterDeclarations()
functionReferenceThis = functionReferenceClass.thisReceiver!!.symbol
val invokeFunctionDescriptor = functionClassDescriptor.getFunction("invoke", functionClassTypeParameters)
@@ -219,7 +264,6 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
invokeMethodBuilder.initialize()
functionReferenceClass.declarations.add(invokeMethodBuilder.ir)
if (!isLambda) {
val getSignatureBuilder =
createGetSignatureMethodBuilder(functionReferenceOrLambda.owner.descriptor.getFunction("getSignature", emptyList()))
@@ -245,7 +289,6 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
).filterNotNull()
)
getSignatureBuilder.initialize()
functionReferenceClass.declarations.add(getSignatureBuilder.ir)
@@ -391,31 +434,74 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
createParameterDeclarations()
body = irBuilder.irBlockBody(startOffset, endOffset) {
val arrayGetFun = context.irBuiltIns.arrayClass.owner
.declarations.find {
(it as? IrSimpleFunction)?.name?.toString() == "get"
}!! as IrSimpleFunction
if (useVararg) {
val varargParam = valueParameters.single()
val arraySizeProperty = context.irBuiltIns.arrayClass.owner.declarations.find {
(it as? IrProperty)?.name?.toString() == "size"
} as IrProperty
+irIfThen(
irNotEquals(
irCall(arraySizeProperty.getter!!).apply {
dispatchReceiver = irGet(varargParam)
},
irInt(unboundFunctionParameters.size)
),
irCall(context.irBuiltIns.illegalArgumentExceptionFun).apply {
putValueArgument(0, irString("Expected ${unboundFunctionParameters.size} arguments"))
}
)
}
+irReturn(
irCall(irFunctionReference.symbol).apply {
var unboundIndex = 0
val unboundArgsSet = unboundFunctionParameters.toSet()
functionParameters.forEach {
val argument =
if (!unboundArgsSet.contains(it))
// Bound parameter - read from field.
irGetField(irGet(functionReferenceThis.owner), argumentToPropertiesMap[it]!!.owner)
else {
if (ourSymbol.descriptor.isSuspend && unboundIndex == valueParameters.size)
functionParameters.forEach { parameter ->
val argument = when {
!unboundArgsSet.contains(parameter) ->
// Bound parameter - read from field.
irGetField(irGet(functionReferenceThis.owner), argumentToPropertiesMap[parameter]!!.owner)
ourSymbol.descriptor.isSuspend && unboundIndex == valueParameters.size ->
// For suspend functions the last argument is continuation and it is implicit.
TODO()
TODO()
// irCall(getContinuationSymbol,
// listOf(ourSymbol.descriptor.returnType!!))
else
irGet(valueParameters[unboundIndex++])
useVararg -> {
val type = parameter.type.toIrType()!!
val varargParam = valueParameters.single()
irBlock(resultType = type) {
val argValue = irTemporary(
irCall(arrayGetFun).apply {
dispatchReceiver = irGet(varargParam)
putValueArgument(0, irInt(unboundIndex++))
}
)
+irIfThen(
irNotIs(irGet(argValue), type),
irCall(context.irBuiltIns.illegalArgumentExceptionFun).apply {
putValueArgument(0, irString("Wrong type, expected $type"))
}
)
+irGet(argValue)
}
}
when (it) {
else -> {
irGet(valueParameters[unboundIndex++])
}
}
when (parameter) {
functionDescriptor.dispatchReceiverParameter -> dispatchReceiver = argument
functionDescriptor.extensionReceiverParameter -> extensionReceiver = argument
else -> putValueArgument((it as ValueParameterDescriptor).index, argument)
else -> putValueArgument((parameter as ValueParameterDescriptor).index, argument)
}
}
assert(unboundIndex == valueParameters.size) { "Not all arguments of <invoke> are used" }
if (!useVararg) assert(unboundIndex == valueParameters.size) { "Not all arguments of <invoke> are used" }
}
)
}
@@ -703,6 +789,10 @@ class CallableReferenceLowering(val context: JvmBackendContext) : FileLoweringPa
Name.identifier("$${name.substring(1, name.length - 1)}")
} else this
}
companion object {
const val MAX_ARGCOUNT_WITHOUT_VARARG = 22
}
}
// Copied from K/Native IrUtils2.kt
@@ -0,0 +1,144 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.filterDeclarations
import org.jetbrains.kotlin.ir.util.findDeclaration
import org.jetbrains.kotlin.ir.util.isSubclassOf
import org.jetbrains.kotlin.name.Name
class FunctionNVarargInvokeLowering(var context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
val invokeFunctions = irClass.filterDeclarations<IrSimpleFunction> { it.name.toString() == "invoke" }
if (invokeFunctions.isEmpty() ||
invokeFunctions.any { it.valueParameters.size > 0 && it.valueParameters.last().varargElementType != null } ||
invokeFunctions.all { it.valueParameters.size + (if (it.extensionReceiverParameter != null) 1 else 0) <= CallableReferenceLowering.MAX_ARGCOUNT_WITHOUT_VARARG }
) {
// No need to add a new vararg invoke method
return
}
val functionInvokes = invokeFunctions.filter { irClass.isSubclassOf(context.ir.symbols.getFunction(it.valueParameters.size).owner) }
if (functionInvokes.isEmpty()) return
irClass.declarations.add(generateVarargInvoke(irClass, functionInvokes))
}
private fun generateVarargInvoke(irClass: IrClass, invokesToDelegateTo: List<IrSimpleFunction>): IrFunction {
val backendContext = context
val descriptor = WrappedSimpleFunctionDescriptor()
return IrFunctionImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
origin = CallableReferenceLowering.DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL,
symbol = IrSimpleFunctionSymbolImpl(descriptor),
name = Name.identifier("invoke"),
visibility = Visibilities.PUBLIC,
modality = Modality.OPEN,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false
).apply {
descriptor.bind(this)
returnType = context.irBuiltIns.anyNType
dispatchReceiverParameter = irClass.thisReceiver
val varargParameterDescriptor = WrappedValueParameterDescriptor()
val varargParam = IrValueParameterImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
origin = CallableReferenceLowering.DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL,
symbol = IrValueParameterSymbolImpl(varargParameterDescriptor),
name = Name.identifier("args"),
index = 0,
type = context.irBuiltIns.arrayClass.typeWith(),
varargElementType = context.irBuiltIns.anyClass.typeWith(),
isCrossinline = false,
isNoinline = false
).apply {
varargParameterDescriptor.bind(this)
}
valueParameters.add(varargParam)
val irBuilder = context.createIrBuilder(symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
body = irBuilder.irBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
val arrayGetFun =
backendContext.irBuiltIns.arrayClass.owner.findDeclaration<IrSimpleFunction> { it.name.toString() == "get" }!!
val arraySizeProperty = context.irBuiltIns.arrayClass.owner.findDeclaration<IrProperty> { it.name.toString() == "size" }!!
val numberOfArguments = irTemporary(irCall(arraySizeProperty.getter!!).apply {
dispatchReceiver = irGet(varargParam)
})
for (target in invokesToDelegateTo) {
+irIfThen(
backendContext.irBuiltIns.unitType,
irEquals(irGet(numberOfArguments), irInt(target.valueParameters.size)),
irReturn(
IrTypeOperatorCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
backendContext.irBuiltIns.anyNType,
IrTypeOperator.CAST,
target.returnType,
target.returnType.classifierOrFail,
irCall(target).apply {
dispatchReceiver = irGet(irClass.thisReceiver!!)
target.valueParameters.forEachIndexed { i, irValueParameter ->
val type = irValueParameter.type
putValueArgument(
i,
irBlock(resultType = type) {
val argValue = irTemporary(
irCallOp(
arrayGetFun.symbol,
context.irBuiltIns.anyNType,
irGet(varargParam),
irInt(i)
)
)
+irIfThen(
irNotIs(irGet(argValue), type),
irCall(context.irBuiltIns.illegalArgumentExceptionFun).apply {
putValueArgument(0, irString("Wrong type, expected $type"))
}
)
+irGet(argValue)
}
)
}
}
)
)
)
}
val throwMessage = invokesToDelegateTo.map { it.valueParameters.size.toString() }.joinToString(
prefix = "Expected ",
separator = " or ",
postfix = " arguments to invoke call"
)
+irCall(context.irBuiltIns.illegalArgumentExceptionFun.symbol).apply {
putValueArgument(0, irString(throwMessage))
}
}
}
}
}
@@ -124,6 +124,8 @@ class IrBuiltIns(
val stringType = string.toIrType()
val stringClass = builtIns.string.toIrSymbol()
val arrayClass = builtIns.array.toIrSymbol()
val throwableType = builtIns.throwable.defaultType.toIrType()
val throwableClass = builtIns.throwable.toIrSymbol()
@@ -152,6 +154,7 @@ class IrBuiltIns(
val throwIseFun = defineOperator("THROW_ISE", nothing, listOf())
val booleanNotFun = defineOperator("NOT", bool, listOf(bool))
val noWhenBranchMatchedExceptionFun = defineOperator("noWhenBranchMatchedException", nothing, listOf())
val illegalArgumentExceptionFun = defineOperator("illegalArgumentException", nothing, listOf(string))
val eqeqeq = eqeqeqFun.descriptor
val eqeq = eqeqFun.descriptor
@@ -159,6 +162,7 @@ class IrBuiltIns(
val throwCce = throwCceFun.descriptor
val booleanNot = booleanNotFun.descriptor
val noWhenBranchMatchedException = noWhenBranchMatchedExceptionFun.descriptor
val illegalArgumentException = illegalArgumentExceptionFun.descriptor
val eqeqeqSymbol = eqeqeqFun.symbol
val eqeqSymbol = eqeqFun.symbol
@@ -167,6 +171,7 @@ class IrBuiltIns(
val throwIseSymbol = throwIseFun.symbol
val booleanNotSymbol = booleanNotFun.symbol
val noWhenBranchMatchedExceptionSymbol = noWhenBranchMatchedExceptionFun.symbol
val illegalArgumentExceptionSymbol = illegalArgumentExceptionFun.symbol
val enumValueOfFun = createEnumValueOfFun()
val enumValueOf = enumValueOfFun.descriptor
@@ -266,6 +266,26 @@ val IrClass.defaultType: IrType
val IrSimpleFunction.isReal: Boolean get() = descriptor.kind.isReal
fun IrClass.isImmediateSubClassOf(ancestor: IrClass) = ancestor.symbol in superTypes.mapNotNull {
(it as? IrSimpleType)?.classifier
}
fun IrClass.isSubclassOf(ancestor: IrClass): Boolean {
val alreadyVisited = mutableSetOf<IrClass>()
fun IrClass.hasAncestorInSuperTypes(): Boolean = when {
this === ancestor -> true
this in alreadyVisited -> false
else -> {
alreadyVisited.add(this)
superTypes.mapNotNull { ((it as? IrSimpleType)?.classifier as? IrClassSymbol)?.owner }.any { it.hasAncestorInSuperTypes() }
}
}
return this.hasAncestorInSuperTypes()
}
// This implementation is from kotlin-native
// TODO: use this implementation instead of any other
fun IrSimpleFunction.resolveFakeOverride(): IrSimpleFunction? {
@@ -339,6 +359,12 @@ fun IrDeclaration.isEffectivelyExternal(): Boolean {
fun IrDeclaration.isDynamic() = this is IrFunction && dispatchReceiverParameter?.type is IrDynamicType
inline fun <reified T : IrDeclaration> IrDeclarationContainer.findDeclaration(predicate: (T) -> Boolean): T? =
declarations.find { it is T && predicate(it) } as? T
inline fun <reified T : IrDeclaration> IrDeclarationContainer.filterDeclarations(predicate: (T) -> Boolean): List<T> =
declarations.filter { it is T && predicate(it) } as List<T>
fun IrValueParameter.copy(newDescriptor: ParameterDescriptor): IrValueParameter {
assert(this.descriptor.type == newDescriptor.type)