[K/N] Force static constants and boxing caches be same values
^KT-49403
This commit is contained in:
+54
-21
@@ -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 =
|
||||
|
||||
+3
@@ -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<LLVMValueRef>()
|
||||
val initializersGenerationState = InitializersGenerationState()
|
||||
val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>()
|
||||
|
||||
|
||||
private object lazyRtFunction {
|
||||
operator fun provideDelegate(
|
||||
|
||||
+1
-1
@@ -1845,7 +1845,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
require(codegen.getLLVMType(value.type) == codegen.kObjHeaderPtr) {
|
||||
"Can't wrap ${value.value.kind.asString} constant to type ${value.type.render()}"
|
||||
}
|
||||
context.llvm.staticData.createConstKotlinObject(
|
||||
value.toBoxCacheValue(context) ?: context.llvm.staticData.createConstKotlinObject(
|
||||
constructedType.getClass()!!,
|
||||
evaluateConst(value.value)
|
||||
)
|
||||
|
||||
@@ -172,4 +172,36 @@ inline fun invokeAndReturnKClass(block: ()->Boolean) : 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)
|
||||
}
|
||||
Reference in New Issue
Block a user