Add a common JVM/JS lowering for Array(size, function)
and remove the hack from JVM_IR codegen that replaces this call with hardcoded inline function bytecode.
This commit is contained in:
@@ -38,6 +38,12 @@ private fun makePatchParentsPhase(number: Int) = namedIrFilePhase(
|
||||
nlevels = 0
|
||||
)
|
||||
|
||||
private val arrayConstructorPhase = makeIrFilePhase(
|
||||
::ArrayConstructorLowering,
|
||||
name = "ArrayConstructor",
|
||||
description = "Transform `Array(size) { index -> value }` into a loop"
|
||||
)
|
||||
|
||||
private val expectDeclarationsRemovingPhase = makeIrFilePhase(
|
||||
::ExpectDeclarationsRemoving,
|
||||
name = "ExpectDeclarationsRemoving",
|
||||
@@ -61,6 +67,7 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
|
||||
lower = expectDeclarationsRemovingPhase then
|
||||
fileClassPhase then
|
||||
kCallableNamePropertyPhase then
|
||||
arrayConstructorPhase then
|
||||
|
||||
jvmLateinitPhase then
|
||||
|
||||
|
||||
+40
-112
@@ -19,13 +19,11 @@ import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -246,64 +244,30 @@ class ExpressionCodegen(
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data).coerce(expression.asmType)
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): PromisedValue {
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
mv.load(0, OBJECT_TYPE) // HACK
|
||||
return generateCall(expression, null, data)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
if (expression.symbol.owner is IrConstructor) {
|
||||
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
|
||||
}
|
||||
return generateCall(expression, expression.superQualifierSymbol, data)
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall, data: BlockInfo): PromisedValue {
|
||||
val type = expression.asmType
|
||||
if (type.sort == Type.ARRAY) {
|
||||
//noinspection ConstantConditions
|
||||
return generateNewArray(expression, data)
|
||||
}
|
||||
|
||||
mv.anew(expression.asmType)
|
||||
mv.dup()
|
||||
generateCall(expression, null, data)
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
private fun generateNewArray(expression: IrConstructorCall, data: BlockInfo): PromisedValue {
|
||||
val args = expression.symbol.owner.valueParameters
|
||||
assert(args.size == 1 || args.size == 2) { "Unknown constructor called: " + args.size + " arguments" }
|
||||
|
||||
if (args.size == 1) {
|
||||
// TODO move to the intrinsic
|
||||
expression.getValueArgument(0)!!.accept(this, data).coerce(Type.INT_TYPE).materialize()
|
||||
newArrayInstruction(expression.type)
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
return generateCall(expression, null, data)
|
||||
}
|
||||
|
||||
private fun generateCall(expression: IrFunctionAccessExpression, superQualifierSymbol: IrClassSymbol?, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it.coerce(expression.asmType) }
|
||||
val isSuperCall = superQualifierSymbol != null
|
||||
val callable = resolveToCallable(expression, isSuperCall)
|
||||
return generateCall(expression, callable, data, isSuperCall)
|
||||
}
|
||||
|
||||
fun generateCall(
|
||||
expression: IrFunctionAccessExpression,
|
||||
callable: Callable,
|
||||
data: BlockInfo,
|
||||
isSuperCall: Boolean = false
|
||||
): PromisedValue {
|
||||
val isSuperCall = (expression as? IrCall)?.superQualifier != null
|
||||
val callable = resolveToCallable(expression, isSuperCall)
|
||||
val callee = expression.symbol.owner
|
||||
val callGenerator = getOrCreateCallGenerator(expression, data)
|
||||
|
||||
when {
|
||||
expression is IrConstructorCall -> {
|
||||
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
|
||||
// instance methods named <init>.
|
||||
mv.anew(expression.asmType)
|
||||
mv.dup()
|
||||
}
|
||||
expression is IrDelegatingConstructorCall ->
|
||||
// In this case the receiver is `this` (not specified in IR) and the return value is discarded anyway.
|
||||
mv.load(0, OBJECT_TYPE)
|
||||
expression.descriptor is ConstructorDescriptor ->
|
||||
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
|
||||
}
|
||||
|
||||
val receiver = expression.dispatchReceiver
|
||||
receiver?.apply {
|
||||
callGenerator.genValueAndPut(
|
||||
@@ -374,17 +338,19 @@ class ExpressionCodegen(
|
||||
)
|
||||
|
||||
val returnType = callee.returnType.substitute(typeSubstitutionMap)
|
||||
if (returnType.isNothing()) {
|
||||
mv.aconst(null)
|
||||
mv.athrow()
|
||||
return voidValue
|
||||
} else if (callee is IrConstructor) {
|
||||
return voidValue
|
||||
} else if (expression.type.isUnit()) {
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
return MaterialValue(mv, callable.returnType).discard().coerce(expression.asmType)
|
||||
return when {
|
||||
returnType.isNothing() -> {
|
||||
mv.aconst(null)
|
||||
mv.athrow()
|
||||
voidValue
|
||||
}
|
||||
expression is IrConstructorCall -> expression.onStack
|
||||
expression is IrDelegatingConstructorCall -> voidValue
|
||||
expression.type.isUnit() ->
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
MaterialValue(mv, callable.returnType).discard().coerce(expression.asmType)
|
||||
else -> MaterialValue(mv, callable.returnType).coerce(expression.asmType)
|
||||
}
|
||||
return MaterialValue(mv, callable.returnType).coerce(expression.asmType)
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
|
||||
@@ -1040,50 +1006,23 @@ class ExpressionCodegen(
|
||||
return typeMapper.mapToCallableMethod(irCall.symbol.owner, isSuper)
|
||||
}
|
||||
|
||||
private fun getOrCreateCallGenerator(
|
||||
irFunction: IrFunction,
|
||||
element: IrMemberAccessExpression?,
|
||||
typeParameterMappings: IrTypeParameterMappings?,
|
||||
isDefaultCompilation: Boolean,
|
||||
data: BlockInfo
|
||||
): IrCallGenerator {
|
||||
if (element == null) return IrCallGenerator.DefaultCallGenerator
|
||||
|
||||
// We should inline callable containing reified type parameters even if inline is disabled
|
||||
// because they may contain something to reify and straight call will probably fail at runtime
|
||||
val isInline = irFunction.isInlineCall(state)
|
||||
|
||||
if (!isInline) return IrCallGenerator.DefaultCallGenerator
|
||||
|
||||
val original = (irFunction as? IrSimpleFunction)?.resolveFakeOverride() ?: irFunction
|
||||
return if (isDefaultCompilation) {
|
||||
TODO()
|
||||
} else {
|
||||
IrInlineCodegen(this, state, original.descriptor, typeParameterMappings!!, IrSourceCompilerForInline(state, element, this, data))
|
||||
private fun getOrCreateCallGenerator(element: IrFunctionAccessExpression, data: BlockInfo): IrCallGenerator {
|
||||
if (!element.symbol.owner.isInlineFunctionCall(context)) {
|
||||
return IrCallGenerator.DefaultCallGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateCallGenerator(
|
||||
functionAccessExpression: IrFunctionAccessExpression,
|
||||
data: BlockInfo
|
||||
): IrCallGenerator {
|
||||
val callee = functionAccessExpression.symbol.owner
|
||||
val callee = element.symbol.owner
|
||||
val typeArgumentContainer = if (callee is IrConstructor) callee.parentAsClass else callee
|
||||
val typeArguments =
|
||||
if (functionAccessExpression.typeArgumentsCount == 0) {
|
||||
if (element.typeArgumentsCount == 0) {
|
||||
//avoid ambiguity with type constructor type parameters
|
||||
emptyMap()
|
||||
} else typeArgumentContainer.typeParameters.keysToMap {
|
||||
functionAccessExpression.getTypeArgumentOrDefault(it)
|
||||
element.getTypeArgumentOrDefault(it)
|
||||
}
|
||||
|
||||
val mappings = IrTypeParameterMappings()
|
||||
for (entry in typeArguments.entries) {
|
||||
val key = entry.key
|
||||
val type = entry.value
|
||||
|
||||
val isReified = key.isReified || callee.isArrayConstructorWithLambda()
|
||||
|
||||
for ((key, type) in typeArguments.entries) {
|
||||
val reificationArgument = extractReificationArgument(type)
|
||||
if (reificationArgument == null) {
|
||||
// type is not generic
|
||||
@@ -1091,16 +1030,17 @@ class ExpressionCodegen(
|
||||
val asmType = typeMapper.mapTypeParameter(type, signatureWriter)
|
||||
|
||||
mappings.addParameterMappingToType(
|
||||
key.name.identifier, type, asmType, signatureWriter.toString(), isReified
|
||||
key.name.identifier, type, asmType, signatureWriter.toString(), key.isReified
|
||||
)
|
||||
} else {
|
||||
mappings.addParameterMappingForFurtherReification(
|
||||
key.name.identifier, type, reificationArgument, isReified
|
||||
key.name.identifier, type, reificationArgument, key.isReified
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return getOrCreateCallGenerator(callee, functionAccessExpression, mappings, false, data)
|
||||
val original = (callee as? IrSimpleFunction)?.resolveFakeOverride() ?: irFunction
|
||||
return IrInlineCodegen(this, state, original.descriptor, mappings, IrSourceCompilerForInline(state, element, this, data))
|
||||
}
|
||||
|
||||
override fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor) {
|
||||
@@ -1213,21 +1153,9 @@ fun DefaultCallArgs.generateOnStackIfNeeded(callGenerator: IrCallGenerator, isCo
|
||||
return toInts.isNotEmpty()
|
||||
}
|
||||
|
||||
internal fun IrFunction.isInlineCall(state: GenerationState) =
|
||||
(!state.isInlineDisabled || containsReifiedTypeParameters()) &&
|
||||
(isInline || isArrayConstructorWithLambda())
|
||||
|
||||
val IrType.isReifiedTypeParameter: Boolean
|
||||
get() = this.classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
|
||||
|
||||
/* Copied and modified from InlineUtil.java */
|
||||
fun isInline(declaration: IrDeclaration?): Boolean = declaration is IrSimpleFunction && declaration.isInline
|
||||
|
||||
fun IrFunction.containsReifiedTypeParameters(): Boolean =
|
||||
typeParameters.any { it.isReified }
|
||||
|
||||
fun IrClass.isArrayOrPrimitiveArray() = this.defaultType.let { it.isArray() || it.isPrimitiveArray() }
|
||||
|
||||
/* From typeUtil.java */
|
||||
fun IrType.getTypeParameterOrNull() = classifierOrNull?.owner?.safeAs<IrTypeParameter>()
|
||||
|
||||
|
||||
+1
-2
@@ -165,8 +165,7 @@ fun isInlineIrExpression(argumentExpression: IrExpression) =
|
||||
(argumentExpression.origin == IrStatementOrigin.LAMBDA || argumentExpression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION)
|
||||
|
||||
fun IrFunction.isInlineFunctionCall(context: JvmBackendContext) =
|
||||
(!context.state.isInlineDisabled || typeParameters.any { it.isReified }) &&
|
||||
(isInline || isArrayConstructorWithLambda())
|
||||
(!context.state.isInlineDisabled || typeParameters.any { it.isReified }) && isInline
|
||||
|
||||
fun IrValueParameter.isInlineParameter() =
|
||||
!isNoinline && !type.isNullable() && type.isFunctionOrKFunction()
|
||||
@@ -89,15 +89,6 @@ fun JvmBackendContext.getSourceMapper(declaration: IrClass): DefaultSourceMapper
|
||||
val IrType.isExtensionFunctionType: Boolean
|
||||
get() = isFunctionTypeOrSubtype() && hasAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType)
|
||||
|
||||
/**
|
||||
* @return true if the function is a constructor of one of 9 array classes (Array<T>, IntArray, FloatArray, ...)
|
||||
* which takes the size and an initializer lambda as parameters. Such constructors are marked as 'inline' but they are not loaded
|
||||
* as such because the 'inline' flag is not stored for constructors in the binary metadata. Therefore we pretend that they are inline
|
||||
*/
|
||||
fun IrFunction.isArrayConstructorWithLambda(): Boolean = this is IrConstructor &&
|
||||
valueParameters.size == 2 &&
|
||||
parentAsClass.isArrayOrPrimitiveArray()
|
||||
|
||||
|
||||
/* Borrowed from MemberCodegen.java */
|
||||
|
||||
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object ArrayConstructor : IntrinsicMethod() {
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
return object : IrIntrinsicFunction(expression, signature, context, expression.argTypes(context)) {
|
||||
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
codegen.generateCall(expression, this, data).materialize()
|
||||
return StackValue.onStack(Type.getObjectType("[" + AsmTypes.OBJECT_TYPE.internalName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -213,9 +213,9 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
|
||||
}
|
||||
|
||||
private fun arrayMethods(arrayClass: IrClassSymbol) = listOf(
|
||||
arrayClass.constructors.single { it.owner.valueParameters.size == 2 }.toKey()!! to ArrayConstructor,
|
||||
arrayClass.owner.properties.single { it.name.asString() == "size" }.getter!!.symbol.toKey()!! to ArraySize
|
||||
) +
|
||||
arrayClass.constructors.filter { it.owner.valueParameters.size == 1 }.map { it.toKey()!! to NewArray } +
|
||||
methodWithArity(arrayClass, "set", 2, ArraySet) +
|
||||
methodWithArity(arrayClass, "get", 1, ArrayGet) +
|
||||
methodWithArity(arrayClass, "clone", 0, Clone) +
|
||||
|
||||
+6
-21
@@ -6,30 +6,15 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object NewArray : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val irType = expression.type
|
||||
return object : IrIntrinsicFunction(expression, signature, context) {
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
super.invoke(v, codegen, data)
|
||||
codegen.newArrayInstruction(irType)
|
||||
return StackValue.onStack(returnType)
|
||||
}
|
||||
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
}
|
||||
}
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
codegen.gen(expression.getValueArgument(0)!!, Type.INT_TYPE, data)
|
||||
codegen.newArrayInstruction(expression.type)
|
||||
return with(codegen) { expression.onStack }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user