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:
pyos
2019-04-12 17:03:09 +02:00
committed by max-kammerer
parent f4bb1354c9
commit 4a29e3cfcf
14 changed files with 227 additions and 286 deletions
@@ -84,6 +84,12 @@ 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)
@@ -351,6 +357,7 @@ val jsPhases = namedIrModulePhase(
description = "IR module lowering",
lower = testGenerationPhase then
expectDeclarationsRemovingPhase then
arrayConstructorPhase then
functionInliningPhase then
lateinitLoweringPhase then
tailrecLoweringPhase then
@@ -1,65 +0,0 @@
/*
* 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<IrConstructor>().first { it.valueParameters.size == 2 }.symbol
private val IrClassSymbol.sizeConstructor
get() = owner.declarations.filterIsInstance<IrConstructor>().first { it.valueParameters.size == 1 }.symbol
@@ -19,7 +19,6 @@ 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.*
@@ -43,38 +42,31 @@ 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)
val callSite = when (expression) {
is IrCall -> expression
is IrConstructorCall -> arrayConstructorTransformer.transformConstructorCall(expression)
else -> return expression
}
if (!callSite.symbol.owner.needsInlining)
return callSite
if (!(expression is IrCall || expression is IrConstructorCall) || !expression.symbol.owner.needsInlining)
return expression
val languageVersionSettings = context.configuration.languageVersionSettings
when {
Symbols.isLateinitIsInitializedPropertyGetter(callSite.symbol) ->
return callSite
Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol) ->
return expression
// Handle coroutine intrinsics
// TODO These should actually be inlined.
callSite.descriptor.isBuiltInIntercepted(languageVersionSettings) ->
expression.descriptor.isBuiltInIntercepted(languageVersionSettings) ->
error("Continuation.intercepted is not available with release coroutines")
callSite.symbol.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
return irCall(callSite, context.coroutineSuspendOrReturn)
callSite.symbol == context.intrinsics.jsCoroutineContext ->
return irCall(callSite, context.coroutineGetContextJs)
expression.symbol.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
return irCall(expression, context.coroutineSuspendOrReturn)
expression.symbol == context.intrinsics.jsCoroutineContext ->
return irCall(expression, context.coroutineGetContextJs)
}
val callee = getFunctionDeclaration(callSite.symbol) // Get declaration of the function to be inlined.
val callee = getFunctionDeclaration(expression.symbol) // Get declaration of the function to be inlined.
callee.transformChildrenVoid(this) // Process recursive inline.
val parent = allScopes.map { it.irElement }.filterIsInstance<IrDeclarationParent>().lastOrNull()
val inliner = Inliner(callSite, callee, currentScope!!, parent, context)
val inliner = Inliner(expression, callee, currentScope!!, parent, context)
return inliner.inline()
}