diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractValueUsageTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractValueUsageTransformer.kt index 57e8a02b682..0233d3abeb2 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractValueUsageTransformer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractValueUsageTransformer.kt @@ -77,7 +77,7 @@ abstract class AbstractValueUsageTransformer( protected open fun IrExpression.useAsResult(enclosing: IrExpression): IrExpression = this.useAs(enclosing.type) - protected open fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression = this + protected open fun useAsVarargElement(element: IrExpression, expression: IrVararg): IrExpression = element override fun visitPropertyReference(expression: IrPropertyReference): IrExpression { TODO() @@ -233,7 +233,7 @@ abstract class AbstractValueUsageTransformer( is IrSpreadElement -> element.expression = element.expression.useAs(expression.type) is IrExpression -> { - expression.putElement(i, element.useAsVarargElement(expression)) + expression.putElement(i, useAsVarargElement(element, expression)) } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt index 4da51d08893..e6df7e9286b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt @@ -108,16 +108,14 @@ abstract class AbstractValueUsageLowering(val context: JsCommonBackendContext) : return this.useAsArgument(expression.target.valueParameters[parameter.index]) } - - override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression { - return this.useAs( + override fun useAsVarargElement(element: IrExpression, expression: IrVararg): IrExpression = + element.useAs( // Do not box primitive inline classes - if (icUtils.isTypeInlined(type) && !icUtils.isTypeInlined(expression.type) && !expression.type.isPrimitiveArray()) + if (icUtils.isTypeInlined(element.type) && !icUtils.isTypeInlined(expression.type) && !expression.type.isPrimitiveArray()) irBuiltIns.anyNType else expression.varargElementType ) - } } class AutoboxingTransformer(context: JsCommonBackendContext) : AbstractValueUsageLowering(context) { diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt index 13fc8914106..763ae7359f3 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.SymbolTable diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt index 213f5145b92..09fda8dc046 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt @@ -37,6 +37,14 @@ internal class WasmUsefulDeclarationProcessor( super.visitVariable(declaration, data) } + override fun visitVararg(expression: IrVararg, data: IrDeclaration) { + expression.type.getClass()!! + .constructors + .firstOrNull { it.hasWasmPrimitiveConstructorAnnotation() } + ?.enqueue(data, "implicit vararg constructor") + super.visitVararg(expression, data) + } + private fun tryToProcessIntrinsicCall(from: IrDeclaration, call: IrCall): Boolean = when (call.symbol) { context.wasmSymbols.unboxIntrinsic -> { val fromType = call.getTypeArgument(0) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt index f1b65f27e82..5402f6f117d 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt @@ -73,6 +73,59 @@ class BodyGenerator( error("Unexpected element of type ${element::class}") } + private fun tryGenerateConstVarargArray(irVararg: IrVararg, wasmArrayType: WasmImmediate.GcType): Boolean { + if (irVararg.elements.isEmpty()) return false + + val kind = (irVararg.elements[0] as? IrConst<*>)?.kind ?: return false + if (kind == IrConstKind.String || kind == IrConstKind.Null) return false + if (irVararg.elements.any { it !is IrConst<*> || it.kind != kind }) return false + + val elementConstValues = irVararg.elements.map { (it as IrConst<*>).value!! } + + val resource = when (irVararg.varargElementType) { + irBuiltIns.byteType -> elementConstValues.map { (it as Byte).toLong() } to WasmI8 + irBuiltIns.booleanType -> elementConstValues.map { if (it as Boolean) 1L else 0L } to WasmI8 + irBuiltIns.intType -> elementConstValues.map { (it as Int).toLong() } to WasmI32 + irBuiltIns.shortType -> elementConstValues.map { (it as Short).toLong() } to WasmI16 + irBuiltIns.longType -> elementConstValues.map { it as Long } to WasmI64 + else -> return false + } + + val constantArrayId = context.referenceConstantArray(resource) + + body.buildConstI32(0) + body.buildConstI32(irVararg.elements.size) + body.buildInstr(WasmOp.ARRAY_NEW_DATA, wasmArrayType, WasmImmediate.DataIdx(constantArrayId)) + return true + } + + private fun tryGenerateVarargArray(irVararg: IrVararg, wasmArrayType: WasmImmediate.GcType) { + irVararg.elements.forEach { + check(it is IrExpression) + generateExpression(it) + } + + val length = WasmImmediate.ConstI32(irVararg.elements.size) + body.buildInstr(WasmOp.ARRAY_NEW_FIXED, wasmArrayType, length) + } + + override fun visitVararg(expression: IrVararg) { + val arrayClass = expression.type.getClass()!! + + val wasmArrayType = arrayClass.constructors + .mapNotNull { it.valueParameters.singleOrNull()?.type } + .firstOrNull { it.getClass()?.getWasmArrayAnnotation() != null } + ?.getRuntimeClass(irBuiltIns)?.symbol + ?.let(context::referenceGcType) + ?.let(WasmImmediate::GcType) + + check(wasmArrayType != null) + + generateAnyParameters(arrayClass.symbol) + if (!tryGenerateConstVarargArray(expression, wasmArrayType)) tryGenerateVarargArray(expression, wasmArrayType) + body.buildStructNew(context.referenceGcType(expression.type.getRuntimeClass(irBuiltIns).symbol)) + } + override fun visitThrow(expression: IrThrow) { generateExpression(expression.value) body.buildThrow(context.tagIdx) @@ -256,14 +309,18 @@ class BodyGenerator( generateCall(expression) } + private fun generateBox(expression: IrExpression, type: IrType) { + val klassSymbol = type.getRuntimeClass(irBuiltIns).symbol + generateAnyParameters(klassSymbol) + generateExpression(expression) + body.buildStructNew(context.referenceGcType(klassSymbol)) + } + private fun generateCall(call: IrFunctionAccessExpression) { // Box intrinsic has an additional klass ID argument. // Processing it separately if (call.symbol == wasmSymbols.boxIntrinsic) { - val klassSymbol = call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol - generateAnyParameters(klassSymbol) - generateExpression(call.getValueArgument(0)!!) - body.buildStructNew(context.referenceGcType(klassSymbol)) + generateBox(call.getValueArgument(0)!!, call.getTypeArgument(0)!!) return } @@ -491,17 +548,17 @@ class BodyGenerator( body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate) } - wasmSymbols.wasmArrayNewData0 -> { - val immediate = WasmImmediate.GcType( - context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol) - ) - body.buildInstr(WasmOp.ARRAY_NEW_DATA, immediate, WasmImmediate.DataIdx(0)) - } - wasmSymbols.stringGetPoolSize -> { body.buildConstI32Symbol(context.stringPoolSize) } + wasmSymbols.wasmArrayNewData0 -> { + val arrayGcType = WasmImmediate.GcType( + context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol) + ) + body.buildInstr(WasmOp.ARRAY_NEW_DATA, arrayGcType, WasmImmediate.DataIdx(0)) + } + else -> { return false } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/ConstantData.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/ConstantData.kt index 2c6060dd5ac..aa3f4ff99bc 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/ConstantData.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/ConstantData.kt @@ -10,7 +10,10 @@ import org.jetbrains.kotlin.wasm.ir.WasmSymbol // Representation of constant data in Wasm memory internal const val CHAR_SIZE_BYTES = 2 +internal const val BYTE_SIZE_BYTES = 1 +internal const val SHORT_SIZE_BYTES = 2 internal const val INT_SIZE_BYTES = 4 +internal const val LONG_SIZE_BYTES = 8 sealed class ConstantDataElement { abstract val sizeInBytes: Int @@ -45,6 +48,23 @@ class ConstantDataIntField(val name: String, val value: WasmSymbol) : Const override val sizeInBytes: Int = INT_SIZE_BYTES } +class ConstantDataIntegerArray(val name: String, val value: List, private val integerSize: Int) : ConstantDataElement() { + override fun toBytes(): ByteArray { + val array = ByteArray(value.size * integerSize) + repeat(value.size) { i -> + value[i].toLittleEndianBytesTo(array, i * integerSize, integerSize) + } + return array + } + + override fun dump(indent: String, startAddress: Int): String { + if (value.isEmpty()) return "" + return "${addressToString(startAddress)}: $indent i${integerSize * 8}[] : ${toBytes().contentToString()} ;; $name\n" + } + + override val sizeInBytes: Int = value.size * integerSize +} + class ConstantDataIntArray(val name: String, val value: List>) : ConstantDataElement() { override fun toBytes(): ByteArray { return value.fold(byteArrayOf()) { acc, el -> acc + el.owner.toLittleEndianBytes() } @@ -95,6 +115,13 @@ class ConstantDataStruct(val name: String, val elements: List, WasmSymbol> + fun referenceConstantArray(resource: Pair, WasmType>): WasmSymbol + fun transformType(irType: IrType): WasmType fun transformFieldType(irType: IrType): WasmType diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt index 6fbae86f294..b25a0d52e39 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt @@ -40,6 +40,8 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { ReferencableElements() val stringLiteralPoolId = ReferencableElements() + val constantArrayDataSegmentId = + ReferencableElements, WasmType>, Int>() private val tagFuncType = WasmFunctionType( listOf( @@ -148,6 +150,18 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { val data = mutableListOf() data.add(WasmData(WasmDataMode.Passive, stringDataSectionBytes.toByteArray())) + constantArrayDataSegmentId.unbound.forEach { (constantArraySegment, symbol) -> + symbol.bind(data.size) + val integerSize = when (constantArraySegment.second) { + WasmI8 -> BYTE_SIZE_BYTES + WasmI16 -> SHORT_SIZE_BYTES + WasmI32 -> INT_SIZE_BYTES + WasmI64 -> LONG_SIZE_BYTES + else -> TODO("type ${constantArraySegment.second} is not implemented") + } + val constData = ConstantDataIntegerArray("constant_array", constantArraySegment.first, integerSize) + data.add(WasmData(WasmDataMode.Passive, constData.toBytes())) + } typeInfo.buildData(data, address = { klassIds.getValue(it) }) val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList()) @@ -203,6 +217,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { startFunction = null, // Module is initialized via export call elements = emptyList(), data = data, + dataCount = true, tags = listOf(tag) ) module.calculateIds() diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt index 5454099166f..e4a920a7514 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt @@ -67,6 +67,9 @@ class WasmModuleCodegenContextImpl( return address to id } + override fun referenceConstantArray(resource: Pair, WasmType>): WasmSymbol = + wasmFragment.constantArrayDataSegmentId.reference(resource) + override fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement) { wasmFragment.typeInfo.define(irClass, typeInfo) } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt index 190a86c24e6..2afc2955996 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExternalType import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.lower.AbstractValueUsageLowering import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrVararg import org.jetbrains.kotlin.ir.types.IrType /** @@ -26,4 +27,10 @@ class ExplicitlyCastExternalTypesLowering(wasmContext: WasmBackendContext) : Abs return this } + + override fun useAsVarargElement(element: IrExpression, expression: IrVararg): IrExpression = + if (isExternalType(element.type)) + element.useAs(irBuiltIns.anyNType) + else + super.useAsVarargElement(element, expression) } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmVarargExpressionLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmVarargExpressionLowering.kt index 01350d81452..e30c9cd85df 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmVarargExpressionLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmVarargExpressionLowering.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.wasm.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irComposite import org.jetbrains.kotlin.backend.wasm.WasmBackendContext @@ -20,7 +21,6 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.util.OperatorNameConventions -import java.lang.IllegalArgumentException internal class WasmVarargExpressionLowering( private val context: WasmBackendContext @@ -44,11 +44,11 @@ internal class WasmVarargExpressionLowering( get() = arrayClass.symbol in context.wasmSymbols.unsignedTypesToUnsignedArrays.values val primaryConstructor: IrConstructor - get() { + get() = if (isUnsigned) - return arrayClass.constructors.find { it.valueParameters.singleOrNull()?.type == context.irBuiltIns.intType }!! - return arrayClass.primaryConstructor!! - } + arrayClass.constructors.find { it.valueParameters.singleOrNull()?.type == context.irBuiltIns.intType }!! + else arrayClass.primaryConstructor!! + val constructors get() = arrayClass.constructors @@ -74,7 +74,6 @@ internal class WasmVarargExpressionLowering( return func?.owner ?: throw IllegalArgumentException("copyInto is not found for ${arrayType.render()}") } - } private fun IrBlockBuilder.irCreateArray(size: IrExpression, arrDescr: ArrayDescr) = @@ -164,16 +163,37 @@ internal class WasmVarargExpressionLowering( this@irSize.irSize() } + private fun tryVisitWithNoSpread(irVararg: IrVararg, builder: DeclarationIrBuilder): IrExpression { + val irVarargType = irVararg.type + if (!irVarargType.isUnsignedArray()) return irVararg + + val unsignedConstructor = irVarargType.getClass()!!.primaryConstructor!! + val constructorParameterType = unsignedConstructor.valueParameters[0].type + val signedElementType = constructorParameterType.getArrayElementType(context.irBuiltIns) + + irVararg.type = constructorParameterType + irVararg.varargElementType = signedElementType + return builder.irCall(unsignedConstructor.symbol, irVarargType).also { + it.putValueArgument(0, irVararg) + } + } + override fun visitVararg(expression: IrVararg): IrExpression { // Optimization in case if we have a single spread element - if (expression.elements.size == 1 && expression.elements.first() is IrSpreadElement) { - val spreadExpr = (expression.elements.first() as IrSpreadElement).expression + val singleSpreadElement = expression.elements.singleOrNull() as? IrSpreadElement + if (singleSpreadElement != null) { + val spreadExpr = singleSpreadElement.expression if (isImmediatelyCreatedArray(spreadExpr)) return spreadExpr.transform(this, null) } // Lower nested varargs val irVararg = super.visitVararg(expression) as IrVararg + val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol) + + if (irVararg.elements.none { it is IrSpreadElement }) { + return tryVisitWithNoSpread(irVararg, builder) + } // Create temporary variable for each element and emit them all at once to preserve // argument evaluation order as per kotlin language spec. @@ -202,9 +222,7 @@ internal class WasmVarargExpressionLowering( yield(VarargSegmentBuilder.Plain(currentElements.toList(), context)) }.toList() - val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol) val destArrayDescr = ArrayDescr(irVararg.type, context) - return builder.irComposite(irVararg) { // Emit all of the variables first so that all vararg expressions // are evaluated only once and in order of their appearance. diff --git a/libraries/stdlib/wasm/builtins/kotlin/Array.kt b/libraries/stdlib/wasm/builtins/kotlin/Array.kt index 00bdbf45547..56b903c1691 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/Array.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/Array.kt @@ -17,6 +17,10 @@ import kotlin.wasm.internal.* public class Array constructor(size: Int) { internal val storage: WasmAnyArray = WasmAnyArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmAnyArray) : this(check(false) as Int) + /** * Creates a new array with the specified [size], where each element is calculated by calling the specified * [init] function. diff --git a/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt b/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt index c662ac9374c..367775881db 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt @@ -10,6 +10,10 @@ import kotlin.wasm.internal.* public class ByteArray(size: Int) { internal val storage = WasmByteArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmByteArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Byte) : this(size) { storage.fill(size, init) } @@ -40,6 +44,10 @@ internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() { public class CharArray(size: Int) { internal val storage = WasmCharArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmCharArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Char) : this(size) { storage.fill(size) { init(it) } } @@ -71,6 +79,10 @@ internal fun charArrayIterator(array: CharArray) = object : CharIterator() { public class ShortArray(size: Int) { internal val storage = WasmShortArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmShortArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Short) : this(size) { storage.fill(size, init) } @@ -102,6 +114,10 @@ internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() { public class IntArray(size: Int) { internal val storage = WasmIntArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmIntArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Int) : this(size) { storage.fill(size, init) } @@ -133,6 +149,10 @@ internal fun intArrayIterator(array: IntArray) = object : IntIterator() { public class LongArray(size: Int) { internal val storage = WasmLongArray (size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmLongArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Long) : this(size) { storage.fill(size, init) } @@ -163,6 +183,10 @@ internal fun longArrayIterator(array: LongArray) = object : LongIterator() { public class FloatArray(size: Int) { internal val storage = WasmFloatArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmFloatArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Float) : this(size) { storage.fill(size, init) } @@ -193,6 +217,10 @@ internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() { public class DoubleArray(size: Int) { internal val storage = WasmDoubleArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmDoubleArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Double) : this(size) { storage.fill(size, init) } @@ -223,6 +251,10 @@ internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() public class BooleanArray(size: Int) { internal val storage = WasmByteArray(size) + @Suppress("TYPE_PARAMETER_AS_REIFIED") + @WasmPrimitiveConstructor + internal constructor(storage: WasmByteArray) : this(check(false) as Int) + public constructor(size: Int, init: (Int) -> Boolean) : this(size) { storage.fill(size) { init(it).toInt().reinterpretAsByte() } } diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt index f865ba7493a..d325a136df0 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt @@ -28,7 +28,7 @@ internal fun wasm_ref_test(a: Any?): Boolean = internal fun wasm_array_copy(destination: T, destinationIndex: Int, source: T, sourceIndex: Int, length: Int): Unit = implementedAsIntrinsic -internal fun array_new_data0(address: Int, length: Int): WasmCharArray = +internal fun array_new_data0(address: Int, length: Int): T = implementedAsIntrinsic @WasmOp(WasmOp.I32_EQ) diff --git a/libraries/stdlib/wasm/src/generated/_WasmOp.kt b/libraries/stdlib/wasm/src/generated/_WasmOp.kt index 346ff78f124..0267aaa8199 100644 --- a/libraries/stdlib/wasm/src/generated/_WasmOp.kt +++ b/libraries/stdlib/wasm/src/generated/_WasmOp.kt @@ -253,6 +253,8 @@ internal annotation class WasmOp(val name: String) { const val ARRAY_SET = "ARRAY_SET" const val ARRAY_LEN = "ARRAY_LEN" const val ARRAY_COPY = "ARRAY_COPY" + const val ARRAY_NEW_DATA = "ARRAY_NEW_DATA" + const val ARRAY_NEW_FIXED = "ARRAY_NEW_FIXED" const val I31_NEW = "I31_NEW" const val I31_GET_S = "I31_GET_S" const val I31_GET_U = "I31_GET_U" diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt index 706b5326ce8..44276a068a8 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt @@ -29,7 +29,7 @@ class WasmModule( val startFunction: WasmFunction? = null, val data: List = emptyList(), - val dataCount: Boolean = false, + val dataCount: Boolean = true, ) sealed class WasmNamedModuleField { diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt index 5b5aedfe95e..70a7f083203 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt @@ -73,7 +73,10 @@ sealed class WasmImmediate { constructor(value: WasmMemory) : this(WasmSymbol(value)) } - class DataIdx(val value: Int) : WasmImmediate() + class DataIdx(val value: WasmSymbol) : WasmImmediate() { + constructor(value: Int) : this(WasmSymbol(value)) + } + class TableIdx(val value: WasmSymbolReadOnly) : WasmImmediate() { constructor(value: Int) : this(WasmSymbol(value)) } @@ -356,6 +359,7 @@ enum class WasmOp( ARRAY_LEN("array.len", 0xFB_17, listOf(STRUCT_TYPE_IDX)), ARRAY_COPY("array.copy", 0xFB_18, listOf(STRUCT_TYPE_IDX, STRUCT_TYPE_IDX)), ARRAY_NEW_DATA("array.new_data", 0xFB_1D, listOf(STRUCT_TYPE_IDX, DATA_IDX)), + ARRAY_NEW_FIXED("array.new_fixed", 0xFB_1A, listOf(STRUCT_TYPE_IDX, CONST_I32)), I31_NEW("i31.new", 0xFB_20), I31_GET_S("i31.get_s", 0xFB_21), diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt index 44ef6cab879..41f6fdc00e1 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt @@ -32,7 +32,7 @@ class WasmBinaryToIR(val b: MyByteReader) { var startFunction: WasmFunction? = null val elements: MutableList = mutableListOf() val data: MutableList = mutableListOf() - var dataCount: Boolean = false + var dataCount: Boolean = true val tags: MutableList = mutableListOf() private fun byIdx(l1: List, l2: List, index: Int): T { diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt index 663fc58fd57..3cf77fc2bd0 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt @@ -114,10 +114,6 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod data.forEach { appendData(it) } } - appendSection(12u) { - b.writeVarUInt32(data.size) - } - //text section (should be placed after data) if (emitNameSection) { appendTextSection(definedFunctions) @@ -217,7 +213,7 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod is WasmImmediate.GlobalIdx -> appendModuleFieldReference(x.value.owner) is WasmImmediate.TypeIdx -> appendModuleFieldReference(x.value.owner) is WasmImmediate.MemoryIdx -> appendModuleFieldReference(x.value.owner) - is WasmImmediate.DataIdx -> b.writeVarUInt32(x.value) + is WasmImmediate.DataIdx -> b.writeVarUInt32(x.value.owner) is WasmImmediate.TableIdx -> b.writeVarUInt32(x.value.owner) is WasmImmediate.LabelIdx -> b.writeVarUInt32(x.value) is WasmImmediate.TagIdx -> b.writeVarUInt32(x.value)