[Wasm] Make Arrays' constructors with size and lambda inline

Fixed #KT-58746
This commit is contained in:
Igor Yakovlev
2023-05-18 19:19:12 +02:00
committed by Space Team
parent 1d5c080dd8
commit 78b72efd32
7 changed files with 218 additions and 50 deletions
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorI
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.toMultiModuleAction
import org.jetbrains.kotlin.backend.wasm.lower.*
import org.jetbrains.kotlin.backend.wasm.lower.WasmArrayConstructorLowering
import org.jetbrains.kotlin.backend.wasm.lower.WasmArrayConstructorReferenceLowering
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToFunctionCallsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
@@ -113,6 +115,19 @@ private val lateinitUsageLoweringPhase = makeWasmModulePhase(
description = "Insert checks for lateinit field references"
)
private val arrayConstructorReferencePhase = makeWasmModulePhase(
::WasmArrayConstructorReferenceLowering,
name = "ArrayConstructorReference",
description = "Transform `::Array` into a lambda"
)
private val arrayConstructorPhase = makeWasmModulePhase(
::WasmArrayConstructorLowering,
name = "ArrayConstructor",
description = "Transform `Array(size) { index -> value }` into a loop",
prerequisite = setOf(arrayConstructorReferencePhase)
)
private val sharedVariablesLoweringPhase = makeWasmModulePhase(
::SharedVariablesLowering,
name = "SharedVariablesLowering",
@@ -607,6 +622,8 @@ val wasmPhases = SameTypeNamedCompilerPhase(
lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then
arrayConstructorReferencePhase then
arrayConstructorPhase then
sharedVariablesLoweringPhase then
localClassesInInlineLambdasPhase then
localClassesInInlineFunctionsPhase then
@@ -194,6 +194,18 @@ class WasmSymbols(
val wasmArrayCopy = getInternalFunction("wasm_array_copy")
val wasmArrayNewData0 = getInternalFunction("array_new_data0")
val primitiveTypeToCreateTypedArray = mapOf(
context.irBuiltIns.arrayClass to getFunction("createAnyArray", kotlinTopLevelPackage),
context.irBuiltIns.booleanArray to getFunction("createBooleanArray", kotlinTopLevelPackage),
context.irBuiltIns.byteArray to getFunction("createByteArray", kotlinTopLevelPackage),
context.irBuiltIns.shortArray to getFunction("createShortArray", kotlinTopLevelPackage),
context.irBuiltIns.charArray to getFunction("createCharArray", kotlinTopLevelPackage),
context.irBuiltIns.intArray to getFunction("createIntArray", kotlinTopLevelPackage),
context.irBuiltIns.longArray to getFunction("createLongArray", kotlinTopLevelPackage),
context.irBuiltIns.floatArray to getFunction("createFloatArray", kotlinTopLevelPackage),
context.irBuiltIns.doubleArray to getFunction("createDoubleArray", kotlinTopLevelPackage),
)
val intToLong = getInternalFunction("wasm_i64_extend_i32_s")
val rangeCheck = getInternalFunction("rangeCheck")
@@ -141,6 +141,12 @@ internal class WasmUsefulDeclarationProcessor(
irClass.getWasmArrayAnnotation()?.type
?.enqueueType(irClass, "array type for wasm array annotated")
if (irClass.symbol in context.wasmSymbols.primitiveTypeToCreateTypedArray.keys) {
irClass.declarations.forEach {
(it as? IrField)?.enqueue(irClass, "preserve all fields for primitive arrays")
}
}
if (context.inlineClassesUtils.isClassInlineLike(irClass)) {
irClass.declarations
.firstIsInstanceOrNull<IrConstructor>()
@@ -166,7 +172,11 @@ internal class WasmUsefulDeclarationProcessor(
irFunction.getEffectiveValueParameters().forEach { it.enqueueValueParameterType(irFunction) }
irFunction.returnType.enqueueType(irFunction, "function return type")
kotlinClosureToJsClosureConvertFunToKotlinClosureCallFun[irFunction]?.enqueue(irFunction, "kotlin closure to JS closure conversion", false)
kotlinClosureToJsClosureConvertFunToKotlinClosureCallFun[irFunction]?.enqueue(
irFunction,
"kotlin closure to JS closure conversion",
false
)
}
override fun processSimpleFunction(irFunction: IrSimpleFunction) {
@@ -0,0 +1,92 @@
/*
* 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.wasm.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.irBlock
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class WasmArrayConstructorLowering(val context: WasmBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(ArrayConstructorTransformer(context, container as IrSymbolOwner))
}
}
private class ArrayConstructorTransformer(
val context: WasmBackendContext,
val container: IrSymbolOwner
) : IrElementTransformerVoidWithContext() {
// Array(size, init) -> create###Array(size, init)
companion object {
internal fun arrayInlineToSizeCreator(context: WasmBackendContext, irConstructor: IrConstructor): IrFunctionSymbol? =
when (irConstructor.valueParameters.size) {
2 -> context.wasmSymbols.primitiveTypeToCreateTypedArray[irConstructor.constructedClass.symbol]
else -> null
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val creator = arrayInlineToSizeCreator(context, expression.symbol.owner)
?: return super.visitConstructorCall(expression)
expression.transformChildrenVoid()
val scope = (currentScope ?: createScope(container)).scope
return context.createIrBuilder(scope.scopeOwnerSymbol).irBlock(expression.startOffset, expression.endOffset) {
+irCall(creator, expression.type).also { call ->
repeat(expression.typeArgumentsCount) { call.putTypeArgument(it, expression.getTypeArgument(it)) }
repeat(expression.valueArgumentsCount) { call.putValueArgument(it, expression.getValueArgument(it)) }
}
}
}
}
class WasmArrayConstructorReferenceLowering(val context: WasmBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(ArrayConstructorReferenceTransformer(context))
}
private class ArrayConstructorReferenceTransformer(val context: WasmBackendContext) : IrElementTransformerVoid() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid()
val target = expression.symbol.owner
if (target !is IrConstructor) return expression
val creator = ArrayConstructorTransformer.arrayInlineToSizeCreator(context, target)
?: return super.visitFunctionReference(expression)
return IrFunctionReferenceImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
type = expression.type,
symbol = creator,
typeArgumentsCount = expression.typeArgumentsCount,
valueArgumentsCount = expression.valueArgumentsCount,
reflectionTarget = creator,
origin = expression.origin
).also { reference ->
repeat(expression.typeArgumentsCount) { reference.putTypeArgument(it, expression.getTypeArgument(it)) }
repeat(expression.valueArgumentsCount) { reference.putValueArgument(it, expression.getValueArgument(it)) }
}
}
}
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: INLINE_ARRAY_CONSTRUCTOR
typealias ArrayS = Array<String>
fun testArray() {
+15 -7
View File
@@ -3,6 +3,14 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress(
"TYPE_PARAMETER_AS_REIFIED",
"WRONG_MODIFIER_TARGET",
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"UNUSED_PARAMETER"
)
package kotlin
import kotlin.wasm.internal.*
@@ -17,9 +25,8 @@ import kotlin.wasm.internal.*
public class Array<T> @PublishedApi internal constructor(size: Int) {
internal val storage: WasmAnyArray = WasmAnyArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmAnyArray) : this(check(false) as Int)
internal constructor(storage: WasmAnyArray)
/**
* Creates a new array with the specified [size], where each element is calculated by calling the specified
@@ -28,11 +35,7 @@ public class Array<T> @PublishedApi internal constructor(size: Int) {
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
// TODO: it have to be inline
@Suppress("TYPE_PARAMETER_AS_REIFIED")
public constructor(size: Int, init: (Int) -> T) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> T)
/**
* Returns the array element at the specified [index]. This method can be called using the
@@ -83,3 +86,8 @@ internal fun <T> arrayIterator(array: Array<T>) = object : Iterator<T> {
override fun next() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun <reified T> createAnyArray(size: Int, invokable: (Int) -> T): Array<T> {
val result = WasmAnyArray(size)
result.fill(size, invokable)
return Array(result)
}
+71 -40
View File
@@ -3,6 +3,14 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress(
"TYPE_PARAMETER_AS_REIFIED",
"WRONG_MODIFIER_TARGET",
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"UNUSED_PARAMETER"
)
package kotlin
import kotlin.wasm.internal.*
@@ -10,13 +18,10 @@ import kotlin.wasm.internal.*
public class ByteArray(size: Int) {
internal val storage = WasmByteArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmByteArray) : this(check(false) as Int)
internal constructor(storage: WasmByteArray)
public constructor(size: Int, init: (Int) -> Byte) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Byte)
public operator fun get(index: Int): Byte {
rangeCheck(index, storage.len())
@@ -40,17 +45,19 @@ internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() {
override fun nextByte() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createByteArray(size: Int, invokable: (Int) -> Byte): ByteArray {
val result = WasmByteArray(size)
result.fill(size, invokable)
return ByteArray(result)
}
public class CharArray(size: Int) {
internal val storage = WasmCharArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmCharArray) : this(check(false) as Int)
internal constructor(storage: WasmCharArray)
public constructor(size: Int, init: (Int) -> Char) : this(size) {
storage.fill(size) { init(it) }
}
public inline constructor(size: Int, init: (Int) -> Char)
public operator fun get(index: Int): Char {
rangeCheck(index, storage.len())
@@ -75,17 +82,19 @@ internal fun charArrayIterator(array: CharArray) = object : CharIterator() {
override fun nextChar() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createCharArray(size: Int, invokable: (Int) -> Char): CharArray {
val result = WasmCharArray(size)
result.fill(size, invokable)
return CharArray(result)
}
public class ShortArray(size: Int) {
internal val storage = WasmShortArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmShortArray) : this(check(false) as Int)
internal constructor(storage: WasmShortArray)
public constructor(size: Int, init: (Int) -> Short) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Short)
public operator fun get(index: Int): Short {
rangeCheck(index, storage.len())
@@ -110,17 +119,19 @@ internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() {
override fun nextShort() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createShortArray(size: Int, invokable: (Int) -> Short): ShortArray {
val result = WasmShortArray(size)
result.fill(size, invokable)
return ShortArray(result)
}
public class IntArray(size: Int) {
internal val storage = WasmIntArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmIntArray) : this(check(false) as Int)
internal constructor(storage: WasmIntArray)
public constructor(size: Int, init: (Int) -> Int) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Int)
public operator fun get(index: Int): Int {
rangeCheck(index, storage.len())
@@ -145,17 +156,19 @@ internal fun intArrayIterator(array: IntArray) = object : IntIterator() {
override fun nextInt() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createIntArray(size: Int, invokable: (Int) -> Int): IntArray {
val result = WasmIntArray(size)
result.fill(size, invokable)
return IntArray(result)
}
public class LongArray(size: Int) {
internal val storage = WasmLongArray (size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmLongArray) : this(check(false) as Int)
internal constructor(storage: WasmLongArray)
public constructor(size: Int, init: (Int) -> Long) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Long)
public operator fun get(index: Int): Long {
rangeCheck(index, storage.len())
@@ -179,17 +192,19 @@ internal fun longArrayIterator(array: LongArray) = object : LongIterator() {
override fun nextLong() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createLongArray(size: Int, invokable: (Int) -> Long): LongArray {
val result = WasmLongArray(size)
result.fill(size, invokable)
return LongArray(result)
}
public class FloatArray(size: Int) {
internal val storage = WasmFloatArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmFloatArray) : this(check(false) as Int)
internal constructor(storage: WasmFloatArray)
public constructor(size: Int, init: (Int) -> Float) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Float)
public operator fun get(index: Int): Float {
rangeCheck(index, storage.len())
@@ -213,17 +228,19 @@ internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() {
override fun nextFloat() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createFloatArray(size: Int, invokable: (Int) -> Float): FloatArray {
val result = WasmFloatArray(size)
result.fill(size, invokable)
return FloatArray(result)
}
public class DoubleArray(size: Int) {
internal val storage = WasmDoubleArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "UNUSED_PARAMETER", "CAST_NEVER_SUCCEEDS")
@WasmPrimitiveConstructor
internal constructor(storage: WasmDoubleArray) : this(check(false) as Int)
internal constructor(storage: WasmDoubleArray)
public constructor(size: Int, init: (Int) -> Double) : this(size) {
storage.fill(size, init)
}
public inline constructor(size: Int, init: (Int) -> Double)
public operator fun get(index: Int): Double {
rangeCheck(index, storage.len())
@@ -247,17 +264,19 @@ internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator()
override fun nextDouble() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
internal inline fun createDoubleArray(size: Int, invokable: (Int) -> Double): DoubleArray {
val result = WasmDoubleArray(size)
result.fill(size, invokable)
return DoubleArray(result)
}
public class BooleanArray(size: Int) {
internal val storage = WasmByteArray(size)
@Suppress("TYPE_PARAMETER_AS_REIFIED", "CAST_NEVER_SUCCEEDS", "UNUSED_PARAMETER")
@WasmPrimitiveConstructor
internal constructor(storage: WasmByteArray) : this(check(false) as Int)
internal constructor(storage: WasmByteArray)
public constructor(size: Int, init: (Int) -> Boolean) : this(size) {
storage.fill(size) { init(it).toInt().reinterpretAsByte() }
}
public inline constructor(size: Int, init: (Int) -> Boolean)
public operator fun get(index: Int): Boolean {
rangeCheck(index, storage.len())
@@ -280,3 +299,15 @@ internal fun booleanArrayIterator(array: BooleanArray) = object : BooleanIterato
override fun hasNext() = index != array.size
override fun nextBoolean() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
}
@WasmNoOpCast
private fun Boolean.reinterpretAsByte(): Byte =
implementedAsIntrinsic
internal inline fun createBooleanArray(size: Int, invokable: (Int) -> Boolean): BooleanArray {
val result = WasmByteArray(size)
result.fill(size) {
invokable(it).reinterpretAsByte()
}
return BooleanArray(result)
}