diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt index 5d3d0efddf0..2795fa9e6e8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt @@ -5,8 +5,7 @@ package org.jetbrains.kotlin.backend.konan -import llvm.LLVMConstInt -import llvm.LLVMTypeRef +import llvm.* import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -15,6 +14,8 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.ir.expressions.IrConstantPrimitive import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl @@ -22,7 +23,6 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.defaultOrNullableType import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization -import org.jetbrains.kotlin.ir.util.getContainingFile import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.name.Name @@ -168,13 +168,13 @@ internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context. * If output target is native binary then the cache is created. */ internal fun initializeCachedBoxes(context: Context) { - if (context.producedLlvmModuleContainsStdlib) { - BoxCache.values().forEach { cache -> - val cacheName = "${cache.name}_CACHE" - val rangeStart = "${cache.name}_RANGE_FROM" - val rangeEnd = "${cache.name}_RANGE_TO" - initCache(cache, context, cacheName, rangeStart, rangeEnd) - } + BoxCache.values().forEach { cache -> + val cacheName = "${cache.name}_CACHE" + val rangeStart = "${cache.name}_RANGE_FROM" + val rangeEnd = "${cache.name}_RANGE_TO" + initCache(cache, context, cacheName, rangeStart, rangeEnd, + declareOnly = !context.producedLlvmModuleContainsStdlib + ).also { context.llvm.boxCacheGlobals[cache] = it } } } @@ -182,22 +182,55 @@ internal fun initializeCachedBoxes(context: Context) { * Adds global that refers to the cache. */ private fun initCache(cache: BoxCache, context: Context, cacheName: String, - rangeStartName: String, rangeEndName: String) { + rangeStartName: String, rangeEndName: String, declareOnly: Boolean) : StaticData.Global { val kotlinType = context.irBuiltIns.getKotlinClass(cache) val staticData = context.llvm.staticData val llvmType = staticData.getLLVMType(kotlinType.defaultType) - - val (start, end) = context.config.target.getBoxCacheRange(cache) - // Constancy of these globals allows LLVM's constant propagation and DCE - // to remove fast path of boxing function in case of empty range. - staticData.placeGlobal(rangeStartName, createConstant(llvmType, start), true) - .setConstant(true) - staticData.placeGlobal(rangeEndName, createConstant(llvmType, end), true) - .setConstant(true) - val values = (start..end).map { staticData.createInitializer(kotlinType, createConstant(llvmType, it)) } val llvmBoxType = structType(context.llvm.runtime.objHeaderType, llvmType) - staticData.placeGlobalConstArray(cacheName, llvmBoxType, values, true).llvm + val (start, end) = context.config.target.getBoxCacheRange(cache) + + return if (declareOnly) { + staticData.createGlobal(LLVMArrayType(llvmBoxType, end - start + 1)!!, cacheName, true) + } else { + // Constancy of these globals allows LLVM's constant propagation and DCE + // to remove fast path of boxing function in case of empty range. + staticData.placeGlobal(rangeStartName, createConstant(llvmType, start), true) + .setConstant(true) + staticData.placeGlobal(rangeEndName, createConstant(llvmType, end), true) + .setConstant(true) + val values = (start..end).map { staticData.createInitializer(kotlinType, createConstant(llvmType, it)) } + staticData.placeGlobalArray(cacheName, llvmBoxType, values, true).also { + it.setConstant(true) + } + } +} + +internal fun IrConstantPrimitive.toBoxCacheValue(context: Context): ConstValue? { + val cacheType = when (value.type) { + context.irBuiltIns.booleanType -> BoxCache.BOOLEAN + context.irBuiltIns.byteType -> BoxCache.BYTE + context.irBuiltIns.shortType -> BoxCache.SHORT + context.irBuiltIns.charType -> BoxCache.CHAR + context.irBuiltIns.intType -> BoxCache.INT + context.irBuiltIns.longType -> BoxCache.LONG + else -> return null + } + val value = when (value.kind) { + IrConstKind.Boolean -> if (value.value as Boolean) 1L else 0L + IrConstKind.Byte -> (value.value as Byte).toLong() + IrConstKind.Short -> (value.value as Short).toLong() + IrConstKind.Char -> (value.value as Char).code.toLong() + IrConstKind.Int -> (value.value as Int).toLong() + IrConstKind.Long -> value.value as Long + else -> throw IllegalArgumentException("IrConst of kind ${value.kind} can't be converted to box cache") + } + val (start, end) = context.config.target.getBoxCacheRange(cacheType) + return if (value in start..end) { + context.llvm.boxCacheGlobals[cacheType]?.pointer?.getElementPtr(value.toInt() - start)?.getElementPtr(0) + } else { + null + } } private fun createConstant(llvmType: LLVMTypeRef, value: Int): ConstValue = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index c2f1e7bd215..61f60399868 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.toKString import llvm.* +import org.jetbrains.kotlin.backend.konan.BoxCache import org.jetbrains.kotlin.backend.konan.CachedLibraries import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.backend.konan.Context @@ -586,6 +587,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti var fileUsesThreadLocalObjects = false val globalSharedObjects = mutableSetOf() val initializersGenerationState = InitializersGenerationState() + val boxCacheGlobals = mutableMapOf() + private object lazyRtFunction { operator fun provideDelegate( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 7931779dff4..fa3769dc864 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1845,7 +1845,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: MapBoolean) : KClass<*> { assertTrue(clazz.isPermanent()) assertEquals("kotlin.Int", clazz.qualifiedName) } +} + +@Test fun testSmallIntIdentity() { + val xBool = true + val xBoolStatic : Any = false + val xBoolDyanmic : Any = !xBool + assertSame(xBoolStatic, xBoolDyanmic) + + val xByte = 1.toByte() + val xByteStatic : Any = 2.toByte() + val xByteDyanmic : Any = (xByte + xByte).toByte() + assertSame(xByteStatic, xByteDyanmic) + + val xShort = 1.toShort() + val xShortStatic : Any = 2.toShort() + val xShortDyanmic : Any = (xShort + xShort).toShort() + assertSame(xShortStatic, xShortDyanmic) + + val xInt = 1.toInt() + val xIntStatic : Any = 2.toInt() + val xIntDyanmic : Any = xInt + xInt + assertSame(xIntStatic, xIntDyanmic) + + val xChar = 1.toChar() + val xCharStatic : Any = 2.toChar() + val xCharDyanmic : Any = (xChar.code + xChar.code).toChar() + assertSame(xCharStatic, xCharDyanmic) + + val xLong = 1.toLong() + val xLongStatic = 2.toLong() + val xLongDyanmic = xLong + xLong + assertSame(xLongStatic, xLongDyanmic) } \ No newline at end of file