From e74e0ea013cad1db39c848fd8eb91c9f139db6e2 Mon Sep 17 00:00:00 2001 From: max-kammerer Date: Thu, 9 May 2019 11:48:58 +0200 Subject: [PATCH] Revert "Add a common JVM/JS lowering for Array(size, function)" This reverts commit 4a29e3cfcf1bc0d35e8d8a122e1b22ec089a2895. --- .../common/lower/ArrayConstructorLowering.kt | 143 ---------------- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 - .../lower/ArrayInlineConstructorLowering.kt | 65 ++++++++ .../js/lower/inline/FunctionInlining.kt | 30 ++-- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 7 - .../backend/jvm/codegen/ExpressionCodegen.kt | 154 +++++++++++++----- .../backend/jvm/codegen/IrInlineCodegen.kt | 3 +- .../backend/jvm/codegen/irCodegenUtils.kt | 9 + .../jvm/intrinsics/ArrayConstructor.kt | 43 +++++ .../jvm/intrinsics/IrIntrinsicMethods.kt | 2 +- .../kotlin/backend/jvm/intrinsics/NewArray.kt | 27 ++- .../kotlin/ir/builders/ExpressionHelpers.kt | 6 +- .../kotlin/ir/descriptors/IrBuiltIns.kt | 2 - libraries/stdlib/js-ir/builtins/Arrays.kt | 17 +- 14 files changed, 287 insertions(+), 228 deletions(-) delete mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ArrayInlineConstructorLowering.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayConstructor.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt deleted file mode 100644 index cd13cd84c74..00000000000 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2010-2019 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.common.lower - -import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.types.getClass -import org.jetbrains.kotlin.ir.types.toKotlinType -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.util.OperatorNameConventions - -class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(this) - } - - // Array(size, init) -> Array(size) - private val arrayInlineToSizeCtor: Map = - (context.irBuiltIns.primitiveArrays + context.irBuiltIns.arrayClass).associate { arrayClass -> - val fromInit = arrayClass.constructors.single { it.owner.valueParameters.size == 2 } - val fromSize = arrayClass.constructors.find { it.owner.valueParameters.size == 1 } - ?: context.ir.symbols.arrayOfNulls // Array has no unary constructor: it can only exist for Array - fromInit to fromSize - } - - // Generate `array[index] = value`. - private fun IrBuilderWithScope.setItem(array: IrVariable, index: IrVariable, value: IrExpression) = - irCall(array.type.getClass()!!.functions.single { it.name.toString() == "set" }).apply { - dispatchReceiver = irGet(array) - putValueArgument(0, irGet(index)) - putValueArgument(1, value) - } - - // Generate `for (index in 0 until end) { element }`. - private fun IrBlockBuilder.fromZeroTo(end: IrVariable, element: IrBlockBuilder.(IrLoop, IrVariable) -> Unit) { - val index = irTemporaryVar(irInt(0)) - +irWhile().apply { - condition = irCall(context.irBuiltIns.lessFunByOperandType[index.type.toKotlinType()]!!).apply { - putValueArgument(0, irGet(index)) - putValueArgument(1, irGet(end)) - } - body = irBlock { - val currentIndex = irTemporary(irGet(index)) - val inc = index.type.getClass()!!.functions.single { it.name.asString() == "inc" } - +irSetVar(index.symbol, irCall(inc).apply { dispatchReceiver = irGet(index) }) - element(this@apply, currentIndex) - } - } - } - - private fun IrExpression.asSingleArgumentLambda(): IrSimpleFunction? { - // A lambda is represented as a block with a function declaration and a reference to it. - if (this !is IrBlock || statements.size != 2) - return null - val (function, reference) = statements - if (function !is IrSimpleFunction || reference !is IrFunctionReference || function.symbol != reference.symbol) - return null - // Only match the one that has exactly one non-vararg argument, as the code below - // does not handle defaults or varargs. - if (function.valueParameters.size != 1 || function.valueParameters[0].isVararg || reference.getValueArgument(0) != null) - return null - return function - } - - override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { - val sizeConstructor = arrayInlineToSizeCtor[expression.symbol] - ?: return super.visitConstructorCall(expression) - val arrayOfReferences = sizeConstructor == context.ir.symbols.arrayOfNulls - - // inline fun Array(size: Int, invokable: (Int) -> T): Array { - // val result = arrayOfNulls(size) - // for (i in 0 until size) { - // result[i] = invokable(i) - // } - // return result as Array - // } - // (and similar for primitive arrays) - val loweringContext = context - val size = expression.getValueArgument(0)!!.transform(this, null) - val invokable = expression.getValueArgument(1)!!.transform(this, null) - return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).irBlock(expression.startOffset, expression.endOffset) { - val sizeVar = irTemporary(size) - val result = irTemporary(irCall(sizeConstructor, expression.type).apply { - if (arrayOfReferences) { - putTypeArgument(0, expression.getTypeArgument(0)) - } - putValueArgument(0, irGet(sizeVar)) - }) - - val lambda = invokable.asSingleArgumentLambda() - if (lambda == null) { - val invokableVar = irTemporary(invokable) - val invoke = invokable.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE } - fromZeroTo(sizeVar) { _, index -> - +setItem(result, index, irCall(invoke).apply { - dispatchReceiver = irGet(invokableVar) - putValueArgument(0, irGet(index)) - }) - } - } else { - // Inline `invokable` by replacing the argument with `i` and `return x` with `result[i] = x; continue`. - fromZeroTo(sizeVar) { loop, index -> - val body = lambda.body!!.transform(object : IrElementTransformerVoidWithContext() { - override fun visitGetValue(expression: IrGetValue) = - if (expression.symbol == lambda.valueParameters[0].symbol) - IrGetValueImpl(expression.startOffset, expression.endOffset, index.symbol) - else - super.visitGetValue(expression) - - override fun visitReturn(expression: IrReturn) = - if (expression.returnTargetSymbol == lambda.symbol) { - val value = expression.value.transform(this, null) - val scope = currentScope?.scope?.scopeOwnerSymbol ?: lambda.symbol - loweringContext.createIrBuilder(scope).irBlock(expression.startOffset, expression.endOffset) { - +setItem(result, index, value) - +irContinue(loop) - } - } else { - super.visitReturn(expression) - } - }, null) - - when (body) { - is IrExpressionBody -> +setItem(result, index, body.expression) - is IrBlockBody -> body.statements.forEach { +it } - else -> throw AssertionError("unexpected function body type: $body") - } - } - } - +irGet(result) - } - } -} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 5a34a0d4448..ba3400bd2b5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -84,12 +84,6 @@ private val lateinitLoweringPhase = makeJsModulePhase( description = "Insert checks for lateinit field references" ) -private val arrayConstructorPhase = makeJsModulePhase( - ::ArrayConstructorLowering, - name = "ArrayConstructor", - description = "Transform `Array(size) { index -> value }` into a loop" -) - private val functionInliningPhase = makeCustomJsModulePhase( { context, module -> FunctionInlining(context).inline(module) @@ -357,7 +351,6 @@ val jsPhases = namedIrModulePhase( description = "IR module lowering", lower = testGenerationPhase then expectDeclarationsRemovingPhase then - arrayConstructorPhase then functionInliningPhase then lateinitLoweringPhase then tailrecLoweringPhase then diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ArrayInlineConstructorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ArrayInlineConstructorLowering.kt new file mode 100644 index 00000000000..028fdb3a94f --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ArrayInlineConstructorLowering.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.ir.backend.js.lower + +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.util.irCall + +// Replace array inline constructors with stdlib function invocations +class ArrayConstructorTransformer( + val context: JsIrBackendContext +) { + // Inline constructor for CharArray is implemented in runtime + private val primitiveArrayInlineToSizeConstructorMap = + context.intrinsics.primitiveArrays.filter { it.value != PrimitiveType.CHAR }.keys.associate { + it.inlineConstructor to it.sizeConstructor + } + + fun transformConstructorCall(expression: IrConstructorCall): IrFunctionAccessExpression { + if (expression.symbol == context.intrinsics.array.inlineConstructor) { + return irCall(expression, context.intrinsics.jsArray).apply { + copyTypeArgumentsFrom(expression) + } + } else { + primitiveArrayInlineToSizeConstructorMap[expression.symbol]?.let { sizeConstructor -> + return IrCallImpl( + expression.startOffset, + expression.endOffset, + expression.type, + context.intrinsics.jsFillArray + ).apply { + putValueArgument(0, IrConstructorCallImpl.fromSymbolOwner( + expression.startOffset, + expression.endOffset, + expression.type, + sizeConstructor + ).apply { + putValueArgument(0, expression.getValueArgument(0)) + }) + putValueArgument(1, expression.getValueArgument(1)) + } + } + } + + return expression + } +} + +// TODO it.isInline doesn't work =( +private val IrClassSymbol.inlineConstructor + get() = owner.declarations.filterIsInstance().first { it.valueParameters.size == 2 }.symbol + +private val IrClassSymbol.sizeConstructor + get() = owner.declarations.filterIsInstance().first { it.valueParameters.size == 1 }.symbol diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt index 1fb5430c57d..c00c5a23402 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/inline/FunctionInlining.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.lower.ArrayConstructorTransformer import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.* @@ -42,31 +43,38 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW return irModule.accept(this, data = null) } + private val arrayConstructorTransformer = ArrayConstructorTransformer(context) + override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression { expression.transformChildrenVoid(this) - if (!(expression is IrCall || expression is IrConstructorCall) || !expression.symbol.owner.needsInlining) - return expression + val callSite = when (expression) { + is IrCall -> expression + is IrConstructorCall -> arrayConstructorTransformer.transformConstructorCall(expression) + else -> return expression + } + if (!callSite.symbol.owner.needsInlining) + return callSite val languageVersionSettings = context.configuration.languageVersionSettings when { - Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol) -> - return expression + Symbols.isLateinitIsInitializedPropertyGetter(callSite.symbol) -> + return callSite // Handle coroutine intrinsics // TODO These should actually be inlined. - expression.descriptor.isBuiltInIntercepted(languageVersionSettings) -> + callSite.descriptor.isBuiltInIntercepted(languageVersionSettings) -> error("Continuation.intercepted is not available with release coroutines") - expression.symbol.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) -> - return irCall(expression, context.coroutineSuspendOrReturn) - expression.symbol == context.intrinsics.jsCoroutineContext -> - return irCall(expression, context.coroutineGetContextJs) + callSite.symbol.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) -> + return irCall(callSite, context.coroutineSuspendOrReturn) + callSite.symbol == context.intrinsics.jsCoroutineContext -> + return irCall(callSite, context.coroutineGetContextJs) } - val callee = getFunctionDeclaration(expression.symbol) // Get declaration of the function to be inlined. + val callee = getFunctionDeclaration(callSite.symbol) // Get declaration of the function to be inlined. callee.transformChildrenVoid(this) // Process recursive inline. val parent = allScopes.map { it.irElement }.filterIsInstance().lastOrNull() - val inliner = Inliner(expression, callee, currentScope!!, parent, context) + val inliner = Inliner(callSite, callee, currentScope!!, parent, context) return inliner.inline() } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index a5cf494bd97..defef17b8f4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -38,12 +38,6 @@ 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", @@ -67,7 +61,6 @@ val jvmPhases = namedIrFilePhase( lower = expectDeclarationsRemovingPhase then fileClassPhase then kCallableNamePropertyPhase then - arrayConstructorPhase then jvmLateinitPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 697b3b456b3..abe72cde9e1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -19,11 +19,13 @@ 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.* @@ -244,30 +246,64 @@ class ExpressionCodegen( override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) = visitStatementContainer(expression, data).coerce(expression.asmType) - override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue { + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, 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 = (expression as? IrCall)?.superQualifier != null + 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 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 . - 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( @@ -338,19 +374,17 @@ class ExpressionCodegen( ) val returnType = callee.returnType.substitute(typeSubstitutionMap) - 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) + 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 MaterialValue(mv, callable.returnType).coerce(expression.asmType) } override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue { @@ -1006,23 +1040,50 @@ class ExpressionCodegen( return typeMapper.mapToCallableMethod(irCall.symbol.owner, isSuper) } - private fun getOrCreateCallGenerator(element: IrFunctionAccessExpression, data: BlockInfo): IrCallGenerator { - if (!element.symbol.owner.isInlineFunctionCall(context)) { - return IrCallGenerator.DefaultCallGenerator - } + private fun getOrCreateCallGenerator( + irFunction: IrFunction, + element: IrMemberAccessExpression?, + typeParameterMappings: IrTypeParameterMappings?, + isDefaultCompilation: Boolean, + data: BlockInfo + ): IrCallGenerator { + if (element == null) return IrCallGenerator.DefaultCallGenerator - val callee = element.symbol.owner + // 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( + functionAccessExpression: IrFunctionAccessExpression, + data: BlockInfo + ): IrCallGenerator { + val callee = functionAccessExpression.symbol.owner val typeArgumentContainer = if (callee is IrConstructor) callee.parentAsClass else callee val typeArguments = - if (element.typeArgumentsCount == 0) { + if (functionAccessExpression.typeArgumentsCount == 0) { //avoid ambiguity with type constructor type parameters emptyMap() } else typeArgumentContainer.typeParameters.keysToMap { - element.getTypeArgumentOrDefault(it) + functionAccessExpression.getTypeArgumentOrDefault(it) } val mappings = IrTypeParameterMappings() - for ((key, type) in typeArguments.entries) { + for (entry in typeArguments.entries) { + val key = entry.key + val type = entry.value + + val isReified = key.isReified || callee.isArrayConstructorWithLambda() + val reificationArgument = extractReificationArgument(type) if (reificationArgument == null) { // type is not generic @@ -1030,17 +1091,16 @@ class ExpressionCodegen( val asmType = typeMapper.mapTypeParameter(type, signatureWriter) mappings.addParameterMappingToType( - key.name.identifier, type, asmType, signatureWriter.toString(), key.isReified + key.name.identifier, type, asmType, signatureWriter.toString(), isReified ) } else { mappings.addParameterMappingForFurtherReification( - key.name.identifier, type, reificationArgument, key.isReified + key.name.identifier, type, reificationArgument, isReified ) } } - val original = (callee as? IrSimpleFunction)?.resolveFakeOverride() ?: irFunction - return IrInlineCodegen(this, state, original.descriptor, mappings, IrSourceCompilerForInline(state, element, this, data)) + return getOrCreateCallGenerator(callee, functionAccessExpression, mappings, false, data) } override fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor) { @@ -1153,9 +1213,21 @@ 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()?.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() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index 58c0c7430d9..6b3623a88e6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -165,7 +165,8 @@ 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 + (!context.state.isInlineDisabled || typeParameters.any { it.isReified }) && + (isInline || isArrayConstructorWithLambda()) fun IrValueParameter.isInlineParameter() = !isNoinline && !type.isNullable() && type.isFunctionOrKFunction() \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index 8c5da6b0fb9..b076ee903aa 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -89,6 +89,15 @@ 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 */ diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayConstructor.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayConstructor.kt new file mode 100644 index 00000000000..dd443543838 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/ArrayConstructor.kt @@ -0,0 +1,43 @@ +/* + * 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)) + } + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index 59fc63c1161..17e317e1855 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -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) + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/NewArray.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/NewArray.kt index cc6428ffc61..e67e953740c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/NewArray.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/NewArray.kt @@ -6,15 +6,30 @@ 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.backend.jvm.codegen.PromisedValue +import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression -import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter object NewArray : IntrinsicMethod() { - 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 } + + 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) { + } + } } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index 3099b20ff7d..28c6d892713 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -253,7 +253,7 @@ fun IrBuilderWithScope.irCall(callee: IrSimpleFunctionSymbol, type: IrType): IrC fun IrBuilderWithScope.irCall(callee: IrConstructorSymbol, type: IrType): IrConstructorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, type, callee) -fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType): IrFunctionAccessExpression = +fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType): IrMemberAccessExpression = when (callee) { is IrConstructorSymbol -> irCall(callee, type) is IrSimpleFunctionSymbol -> irCall(callee, type) @@ -266,13 +266,13 @@ fun IrBuilderWithScope.irCall(callee: IrSimpleFunctionSymbol): IrCall = fun IrBuilderWithScope.irCall(callee: IrConstructorSymbol): IrConstructorCall = irCall(callee, callee.owner.returnType) -fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrFunctionAccessExpression = +fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrMemberAccessExpression = irCall(callee, callee.owner.returnType) fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, descriptor: FunctionDescriptor, type: IrType): IrCall = IrCallImpl(startOffset, endOffset, type, callee as IrSimpleFunctionSymbol, descriptor) -fun IrBuilderWithScope.irCall(callee: IrFunction): IrFunctionAccessExpression = +fun IrBuilderWithScope.irCall(callee: IrFunction): IrMemberAccessExpression = irCall(callee.symbol) fun IrBuilderWithScope.irCall(callee: IrFunction, origin: IrStatementOrigin): IrCall = diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt index 0ee1f87bcca..0f40507822e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltIns.kt @@ -183,8 +183,6 @@ class IrBuiltIns( val primitiveTypes = listOf(bool, char, byte, short, int, long, float, double) val primitiveTypesWithComparisons = listOf(char, byte, short, int, long, float, double) val primitiveFloatingPointTypes = listOf(float, double) - val primitiveArrays = PrimitiveType.values().map { builtIns.getPrimitiveArrayClassDescriptor(it).toIrSymbol() } - val primitiveArrayElementTypes = primitiveArrays.zip(primitiveIrTypes).toMap() val primitiveTypeToIrType = mapOf( PrimitiveType.BOOLEAN to booleanType, diff --git a/libraries/stdlib/js-ir/builtins/Arrays.kt b/libraries/stdlib/js-ir/builtins/Arrays.kt index 4e95d2579f5..1428fb98eab 100644 --- a/libraries/stdlib/js-ir/builtins/Arrays.kt +++ b/libraries/stdlib/js-ir/builtins/Arrays.kt @@ -41,12 +41,6 @@ public class ByteArray(size: Int) { * @constructor Creates a new array of the specified [size], with all elements initialized to null char (`\u0000'). */ public class CharArray(size: Int) { - /** - * Creates a new array of the specified [size], where each element is calculated by calling the specified - * [init] function. The [init] function returns an array element given its index. - */ - public inline constructor(size: Int, init: (Int) -> Char) - /** Returns the array element at the given [index]. This method can be called using the index operator. */ public operator fun get(index: Int): Char /** Sets the element at the given [index] to the given [value]. This method can be called using the index operator. */ @@ -59,6 +53,17 @@ public class CharArray(size: Int) { public operator fun iterator(): CharIterator } +@kotlin.internal.InlineOnly +public inline fun CharArray(size: Int, init: (Int) -> Char): CharArray { + val result = CharArray(size) + var i = 0 + while (i != result.size) { + result[i] = init(i) + ++i + } + return result +} + /** * An array of shorts. When targeting the JVM, instances of this class are represented as `short[]`. * @constructor Creates a new array of the specified [size], with all elements initialized to zero.