From 2818d3767eaeaf2355b73a291cebbba9d5aa32e8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Thu, 24 Jan 2019 18:45:50 +0300 Subject: [PATCH] [JS IR BE] Covert Char into inline class --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 5 +- .../lower/ArrayInlineConstructorLowering.kt | 11 ++- .../backend/js/lower/AutoboxingTransformer.kt | 3 +- .../ir/backend/js/lower/VarargLowering.kt | 82 +++++++++++++++---- .../calls/NumberConversionCallsTransformer.kt | 4 +- .../iterateOverArrayOfUnsignedValues.kt | 1 + libraries/stdlib/js/irRuntime/arrays.kt | 2 +- .../stdlib/js/irRuntime/builtins/Char.kt | 19 ++--- .../stdlib/js/irRuntime/numberConversion.kt | 2 +- 9 files changed, 90 insertions(+), 39 deletions(-) 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 c244a370c15..2866ce0a52b 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 @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.util.patchDeclarationParents -import org.jetbrains.kotlin.ir.visitors.acceptVoid private fun FileLoweringPass.lower(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { lower(it) } @@ -371,13 +370,13 @@ val jsPhases = listOf( TypeOperatorLoweringPhase, SecondaryConstructorLoweringPhase, SecondaryFactoryInjectorLoweringPhase, + ClassReferenceLoweringPhase, InlineClassLoweringPhase, AutoboxingTransformerPhase, BlockDecomposerLoweringPhase, - ClassReferenceLoweringPhase, PrimitiveCompanionLoweringPhase, ConstLoweringPhase, CallsLoweringPhase, IrModuleEndPhase, IrToJsPhase -) \ No newline at end of file +) 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 index e61d8081839..043b4488576 100644 --- 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 @@ -6,14 +6,15 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext -import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -29,9 +30,11 @@ private class ArrayConstructorTransformer( val context: JsIrBackendContext ) : IrElementTransformerVoid() { - private val primitiveArrayInlineToSizeConstructorMap = context.intrinsics.primitiveArrays.keys.associate { - it.inlineConstructor to it.sizeConstructor - } + // 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 + } override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) 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 cc0c3120ce5..a027abe4f2d 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 @@ -151,7 +151,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression { return this.useAs( - if (this.type.isInlined()) + // Do not box primitive inline classes + if (this.type.isInlined() && !expression.type.isInlined() && !expression.type.isPrimitiveArray()) irBuiltIns.anyNType else expression.varargElementType diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt index 68d313f7e5b..9675fddde10 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt @@ -8,13 +8,22 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrSpreadElement +import org.jetbrains.kotlin.ir.expressions.IrVararg import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.getInlineClassBackingField +import org.jetbrains.kotlin.ir.util.getInlinedClass import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -44,32 +53,64 @@ private class VarargTransformer( } } + fun IrExpression.unboxInlineClassIfNeeded(): IrExpression { + val inlinedClass = type.getInlinedClass() ?: return this + val field = getInlineClassBackingField(inlinedClass) + return IrGetFieldImpl(startOffset, endOffset, field.symbol, inlinedClass.defaultType, this) + } + + fun IrExpression.boxInlineClassIfNeeded(inlineClass: IrClass?) = + if (inlineClass == null) + this + else + IrCallImpl(startOffset, endOffset, inlineClass.defaultType, inlineClass.constructors.single { it.isPrimary }.symbol).also { + it.putValueArgument(0, this) + } + override fun visitVararg(expression: IrVararg): IrExpression { expression.transformChildrenVoid(this) val currentList = mutableListOf() val segments = mutableListOf() + val elementType = expression.varargElementType + val primitiveElementType: IrType + val primitiveExpressionType: IrType + val needUnboxing: Boolean + val arrayInlineClass = expression.type.getInlinedClass() + if (arrayInlineClass != null) { + primitiveElementType = getInlineClassBackingField(elementType.getInlinedClass()!!).type + primitiveExpressionType = getInlineClassBackingField(arrayInlineClass).type + needUnboxing = true + } else { + primitiveElementType = elementType + primitiveExpressionType = expression.type + needUnboxing = false + } + for (e in expression.elements) { - if (e is IrSpreadElement) { - if (!currentList.isEmpty()) { - segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType)) - currentList.clear() + when (e) { + is IrSpreadElement -> { + if (!currentList.isEmpty()) { + segments.add(currentList.toArrayLiteral(primitiveExpressionType, primitiveElementType)) + currentList.clear() + } + segments.add(if (needUnboxing) e.expression.unboxInlineClassIfNeeded() else e.expression) + } + + is IrExpression -> { + currentList.add(if (needUnboxing) e.unboxInlineClassIfNeeded() else e) } - segments.add(e.expression) - } else { - // IrVarargElement is either IrSpreadElement or IrExpression - currentList.add(e as IrExpression) } } if (!currentList.isEmpty()) { - segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType)) + segments.add(currentList.toArrayLiteral(primitiveExpressionType, primitiveElementType)) currentList.clear() } // empty vararg => empty array literal if (segments.isEmpty()) { - return emptyList().toArrayLiteral(expression.type, expression.varargElementType) + return emptyList().toArrayLiteral(primitiveExpressionType, primitiveElementType) } // vararg with a single segment => no need to concatenate @@ -85,11 +126,19 @@ private class VarargTransformer( putValueArgument(0, segments.first()) } } else { - segments.first() + val res = segments.first() + return if (needUnboxing) + res.boxInlineClassIfNeeded(arrayInlineClass!!) + else + res } } - val arrayLiteral = segments.toArrayLiteral(IrSimpleTypeImpl(context.intrinsics.array, false, emptyList(), emptyList()), context.irBuiltIns.anyType) + val arrayLiteral = + segments.toArrayLiteral( + IrSimpleTypeImpl(context.intrinsics.array, false, emptyList(), emptyList()), + context.irBuiltIns.anyType + ) val concatFun = if (expression.type.classifierOrNull in context.intrinsics.primitiveArrays.keys) { context.intrinsics.primitiveArrayConcat @@ -97,7 +146,7 @@ private class VarargTransformer( context.intrinsics.arrayConcat } - return IrCallImpl( + val res = IrCallImpl( expression.startOffset, expression.endOffset, expression.type, @@ -105,6 +154,11 @@ private class VarargTransformer( ).apply { putValueArgument(0, arrayLiteral) } + + return if (needUnboxing) + res.boxInlineClassIfNeeded(arrayInlineClass!!) + else + res } override fun visitCall(expression: IrCall): IrExpression { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt index 605e3a619a5..137f391a856 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt @@ -66,14 +66,12 @@ class NumberConversionCallsTransformer(context: JsIrBackendContext) : CallsTrans } for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.shortType, irBuiltIns.intType)) { - add(type, ConversionNames.TO_CHAR, intrinsics.charConstructor) + add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar) } for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType, irBuiltIns.numberType)) { add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar) } - - add(irBuiltIns.charType, ConversionNames.TO_CHAR, ::useDispatchReceiver) } override fun transformCall(call: IrCall): IrExpression { diff --git a/compiler/testData/codegen/box/unsignedTypes/iterateOverArrayOfUnsignedValues.kt b/compiler/testData/codegen/box/unsignedTypes/iterateOverArrayOfUnsignedValues.kt index 575420fa3ca..fa6fbdabd23 100644 --- a/compiler/testData/codegen/box/unsignedTypes/iterateOverArrayOfUnsignedValues.kt +++ b/compiler/testData/codegen/box/unsignedTypes/iterateOverArrayOfUnsignedValues.kt @@ -1,4 +1,5 @@ // WITH_RUNTIME +// KJS_WITH_FULL_RUNTIME // IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR diff --git a/libraries/stdlib/js/irRuntime/arrays.kt b/libraries/stdlib/js/irRuntime/arrays.kt index 0bda393bd63..47723465f3f 100644 --- a/libraries/stdlib/js/irRuntime/arrays.kt +++ b/libraries/stdlib/js/irRuntime/arrays.kt @@ -32,7 +32,7 @@ internal fun booleanArray(size: Int): BooleanArray = withType("BooleanArray", fi internal fun booleanArrayOf(arr: Array): BooleanArray = withType("BooleanArray", arr.asDynamic().slice()).unsafeCast() -internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array(size), '\u0000')).unsafeCast() +internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array(size), 0)).unsafeCast() internal fun charArrayOf(arr: Array): CharArray = withType("CharArray", arr.asDynamic().slice()).unsafeCast() diff --git a/libraries/stdlib/js/irRuntime/builtins/Char.kt b/libraries/stdlib/js/irRuntime/builtins/Char.kt index 63cbe7b5e13..888ef6312a7 100644 --- a/libraries/stdlib/js/irRuntime/builtins/Char.kt +++ b/libraries/stdlib/js/irRuntime/builtins/Char.kt @@ -9,10 +9,9 @@ package kotlin * Represents a 16-bit Unicode character. * On the JVM, non-nullable values of this type are represented as values of the primitive type `char`. */ -public class Char(value: Int) : Comparable { - - private val value = value and 0xFFFF - +@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS") +public inline class Char internal constructor (val value: Int) : Comparable { + /** * Compares this value with the specified value for order. * Returns zero if this value is equal to the specified other value, a negative number if it's less than other, @@ -21,17 +20,17 @@ public class Char(value: Int) : Comparable { public override fun compareTo(other: Char): Int = value - other.value /** Adds the other Int value to this value resulting a Char. */ - public operator fun plus(other: Int): Char = Char(value + other) + public operator fun plus(other: Int): Char = (value + other).toChar() /** Subtracts the other Char value from this value resulting an Int. */ public operator fun minus(other: Char): Int = value - other.value /** Subtracts the other Int value from this value resulting a Char. */ - public operator fun minus(other: Int): Char = Char(value - other) + public operator fun minus(other: Int): Char = (value - other).toChar() /** Increments this value. */ - public operator fun inc(): Char = Char(value + 1) + public operator fun inc(): Char = (value + 1).toChar() /** Decrements this value. */ - public operator fun dec(): Char = Char(value - 1) + public operator fun dec(): Char = (value - 1).toChar() /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: Char): CharRange = CharRange(this, other) @@ -51,10 +50,6 @@ public class Char(value: Int) : Comparable { /** Returns the value of this character as a `Double`. */ public fun toDouble(): Double = value.toDouble() - override fun equals(other: Any?): Boolean = other is Char && value == other.value - - override fun hashCode(): Int = value - override fun toString(): String { val value = value return js("String.fromCharCode(value)").unsafeCast() diff --git a/libraries/stdlib/js/irRuntime/numberConversion.kt b/libraries/stdlib/js/irRuntime/numberConversion.kt index acfdbff8e6e..bfd726ce57a 100644 --- a/libraries/stdlib/js/irRuntime/numberConversion.kt +++ b/libraries/stdlib/js/irRuntime/numberConversion.kt @@ -27,4 +27,4 @@ fun doubleToInt(a: dynamic) = js(""" return a | 0; """).unsafeCast() -fun numberToChar(a: dynamic) = Char(numberToInt(a)) \ No newline at end of file +fun numberToChar(a: dynamic) = Char(numberToInt(a) and 0xFFFF) \ No newline at end of file