[optmz][codegen] Rewrote escape analysis
This commit is contained in:
+1
@@ -20,6 +20,7 @@ internal class DirectedGraphMultiNode<out K>(val nodes: Set<K>)
|
|||||||
|
|
||||||
internal class DirectedGraphCondensation<out K>(val topologicalOrder: List<DirectedGraphMultiNode<K>>)
|
internal class DirectedGraphCondensation<out K>(val topologicalOrder: List<DirectedGraphMultiNode<K>>)
|
||||||
|
|
||||||
|
// The Kosoraju-Sharir algorithm.
|
||||||
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(private val graph: DirectedGraph<K, N>) {
|
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(private val graph: DirectedGraph<K, N>) {
|
||||||
private val visited = mutableSetOf<K>()
|
private val visited = mutableSetOf<K>()
|
||||||
private val order = mutableListOf<N>()
|
private val order = mutableListOf<N>()
|
||||||
|
|||||||
+2
-2
@@ -522,7 +522,7 @@ internal fun PhaseConfig.disableUnless(phase: AnyNamedPhase, condition: Boolean)
|
|||||||
internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
||||||
with(config.configuration) {
|
with(config.configuration) {
|
||||||
disable(compileTimeEvaluatePhase)
|
disable(compileTimeEvaluatePhase)
|
||||||
disable(escapeAnalysisPhase)
|
disable(localEscapeAnalysisPhase)
|
||||||
|
|
||||||
// Don't serialize anything to a final executable.
|
// Don't serialize anything to a final executable.
|
||||||
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
|
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
|
||||||
@@ -536,7 +536,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
|
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
|
||||||
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(localEscapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(escapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
||||||
|
|||||||
+19
-13
@@ -129,10 +129,15 @@ internal val dcePhase = makeKonanModuleOpPhase(
|
|||||||
context, context.moduleDFG!!,
|
context, context.moduleDFG!!,
|
||||||
externalModulesDFG,
|
externalModulesDFG,
|
||||||
context.devirtualizationAnalysisResult!!,
|
context.devirtualizationAnalysisResult!!,
|
||||||
true
|
// For DCE we don't wanna miss any potentially reachable function.
|
||||||
|
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
val referencedFunctions = mutableSetOf<IrFunction>()
|
val referencedFunctions = mutableSetOf<IrFunction>()
|
||||||
|
callGraph.rootExternalFunctions.forEach {
|
||||||
|
if (!it.isGlobalInitializer)
|
||||||
|
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
|
||||||
|
}
|
||||||
for (node in callGraph.directEdges.values) {
|
for (node in callGraph.directEdges.values) {
|
||||||
if (!node.symbol.isGlobalInitializer)
|
if (!node.symbol.isGlobalInitializer)
|
||||||
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
||||||
@@ -200,22 +205,23 @@ internal val dcePhase = makeKonanModuleOpPhase(
|
|||||||
)
|
)
|
||||||
|
|
||||||
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||||
// Disabled by default !!!!
|
|
||||||
name = "EscapeAnalysis",
|
name = "EscapeAnalysis",
|
||||||
description = "Escape analysis",
|
description = "Escape analysis",
|
||||||
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
|
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
|
||||||
op = { context, _ ->
|
op = { context, _ ->
|
||||||
context.externalModulesDFG?.let { externalModulesDFG ->
|
val entryPoint = context.ir.symbols.entryPoint?.owner
|
||||||
val callGraph = CallGraphBuilder(
|
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
|
||||||
context, context.moduleDFG!!,
|
val callGraph = CallGraphBuilder(
|
||||||
externalModulesDFG,
|
context, context.moduleDFG!!,
|
||||||
context.devirtualizationAnalysisResult!!,
|
externalModulesDFG,
|
||||||
false
|
context.devirtualizationAnalysisResult!!,
|
||||||
).build()
|
// Can't tolerate any non-devirtualized call site for a library.
|
||||||
EscapeAnalysis.computeLifetimes(
|
// TODO: What about private virtual functions?
|
||||||
context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes
|
nonDevirtualizedCallSitesUnfoldFactor = if (entryPoint == null) 0 else 5
|
||||||
)
|
).build()
|
||||||
}
|
EscapeAnalysis.computeLifetimes(
|
||||||
|
context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+162
-73
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
||||||
@@ -161,6 +160,143 @@ private inline fun <R> generateFunctionBody(
|
|||||||
*/
|
*/
|
||||||
internal data class LocationInfoRange(var start: LocationInfo, var end: LocationInfo?)
|
internal data class LocationInfoRange(var start: LocationInfo, var end: LocationInfo?)
|
||||||
|
|
||||||
|
internal interface StackLocalsManager {
|
||||||
|
fun alloc(irClass: IrClass, cleanFieldsExplicitly: Boolean): LLVMValueRef
|
||||||
|
|
||||||
|
fun allocArray(irClass: IrClass, count: LLVMValueRef): LLVMValueRef
|
||||||
|
|
||||||
|
fun clean(refsOnly: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class StackLocalsManagerImpl(
|
||||||
|
val functionGenerationContext: FunctionGenerationContext,
|
||||||
|
val bbInitStackLocals: LLVMBasicBlockRef
|
||||||
|
) : StackLocalsManager {
|
||||||
|
private class StackLocal(val isArray: Boolean, val irClass: IrClass,
|
||||||
|
val bodyPtr: LLVMValueRef, val objHeaderPtr: LLVMValueRef)
|
||||||
|
|
||||||
|
private val stackLocals = mutableListOf<StackLocal>()
|
||||||
|
|
||||||
|
override fun alloc(irClass: IrClass, cleanFieldsExplicitly: Boolean): LLVMValueRef = with(functionGenerationContext) {
|
||||||
|
val type = context.llvmDeclarations.forClass(irClass).bodyType
|
||||||
|
val stackLocal = appendingTo(bbInitStackLocals) {
|
||||||
|
val stackSlot = LLVMBuildAlloca(builder, type, "")!!
|
||||||
|
|
||||||
|
call(context.llvm.memsetFunction,
|
||||||
|
listOf(bitcast(kInt8Ptr, stackSlot),
|
||||||
|
Int8(0).llvm,
|
||||||
|
Int32(LLVMSizeOfTypeInBits(codegen.llvmTargetData, type).toInt() / 8).llvm,
|
||||||
|
Int1(0).llvm))
|
||||||
|
|
||||||
|
val objectHeader = structGep(stackSlot, 0, "objHeader")
|
||||||
|
val typeInfo = codegen.typeInfoForAllocation(irClass)
|
||||||
|
setTypeInfoForLocalObject(objectHeader, typeInfo)
|
||||||
|
StackLocal(false, irClass, stackSlot, objectHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
stackLocals += stackLocal
|
||||||
|
if (cleanFieldsExplicitly)
|
||||||
|
clean(stackLocal, false)
|
||||||
|
stackLocal.objHeaderPtr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns generated special type for local array.
|
||||||
|
// It's needed to prevent changing variables order on stack.
|
||||||
|
private fun localArrayType(irClass: IrClass, count: Int) = with(functionGenerationContext) {
|
||||||
|
val name = "local#${irClass.name}${count}#internal"
|
||||||
|
// Create new type or get already created.
|
||||||
|
context.declaredLocalArrays.getOrPut(name) {
|
||||||
|
val fieldTypes = listOf(kArrayHeader, LLVMArrayType(arrayToElementType[irClass.symbol]!!, count))
|
||||||
|
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
|
||||||
|
LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, 1)
|
||||||
|
classType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val symbols = functionGenerationContext.context.ir.symbols
|
||||||
|
// TODO: find better place?
|
||||||
|
private val arrayToElementType = mapOf(
|
||||||
|
symbols.array to functionGenerationContext.kObjHeaderPtr,
|
||||||
|
symbols.byteArray to int8Type,
|
||||||
|
symbols.charArray to int16Type,
|
||||||
|
symbols.shortArray to int16Type,
|
||||||
|
symbols.intArray to int32Type,
|
||||||
|
symbols.longArray to int64Type,
|
||||||
|
symbols.floatArray to floatType,
|
||||||
|
symbols.doubleArray to doubleType,
|
||||||
|
symbols.booleanArray to int8Type
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun allocArray(irClass: IrClass, count: LLVMValueRef) = with(functionGenerationContext) {
|
||||||
|
val stackLocal = appendingTo(bbInitStackLocals) {
|
||||||
|
val constCount = extractConstUnsignedInt(count).toInt()
|
||||||
|
val arrayType = localArrayType(irClass, constCount)
|
||||||
|
val typeInfo = codegen.typeInfoValue(irClass)
|
||||||
|
val arraySlot = LLVMBuildAlloca(builder, arrayType, "")!!
|
||||||
|
// Set array size in ArrayHeader.
|
||||||
|
val arrayHeaderSlot = structGep(arraySlot, 0, "arrayHeader")
|
||||||
|
setTypeInfoForLocalObject(arrayHeaderSlot, typeInfo)
|
||||||
|
val sizeField = structGep(arrayHeaderSlot, 1, "count_")
|
||||||
|
store(count, sizeField)
|
||||||
|
|
||||||
|
call(context.llvm.memsetFunction,
|
||||||
|
listOf(bitcast(kInt8Ptr, structGep(arraySlot, 1, "arrayBody")),
|
||||||
|
Int8(0).llvm,
|
||||||
|
Int32(constCount * LLVMSizeOfTypeInBits(codegen.llvmTargetData,
|
||||||
|
arrayToElementType[irClass.symbol]).toInt() / 8).llvm,
|
||||||
|
Int1(0).llvm))
|
||||||
|
StackLocal(true, irClass, arraySlot, arrayHeaderSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
stackLocals += stackLocal
|
||||||
|
bitcast(kObjHeaderPtr, stackLocal.objHeaderPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clean(refsOnly: Boolean) = stackLocals.forEach { clean(it, refsOnly) }
|
||||||
|
|
||||||
|
private fun clean(stackLocal: StackLocal, refsOnly: Boolean) = with(functionGenerationContext) {
|
||||||
|
if (stackLocal.isArray) {
|
||||||
|
if (stackLocal.irClass.symbol == context.ir.symbols.array)
|
||||||
|
call(context.llvm.zeroArrayRefsFunction, listOf(stackLocal.objHeaderPtr))
|
||||||
|
} else {
|
||||||
|
val type = context.llvmDeclarations.forClass(stackLocal.irClass).bodyType
|
||||||
|
for (field in context.getLayoutBuilder(stackLocal.irClass).fields) {
|
||||||
|
val fieldIndex = context.llvmDeclarations.forField(field).index
|
||||||
|
val fieldType = LLVMStructGetTypeAtIndex(type, fieldIndex)!!
|
||||||
|
|
||||||
|
if (isObjectType(fieldType)) {
|
||||||
|
val fieldPtr = LLVMBuildStructGEP(builder, stackLocal.bodyPtr, fieldIndex, "")!!
|
||||||
|
if (refsOnly)
|
||||||
|
storeHeapRef(kNullObjHeaderPtr, fieldPtr)
|
||||||
|
else
|
||||||
|
call(context.llvm.zeroHeapRefFunction, listOf(fieldPtr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!refsOnly) {
|
||||||
|
val bodyPtr = ptrToInt(stackLocal.bodyPtr, codegen.intPtrType)
|
||||||
|
val bodySize = LLVMSizeOfTypeInBits(codegen.llvmTargetData, type).toInt() / 8
|
||||||
|
val serviceInfoSize = runtime.pointerSize
|
||||||
|
val serviceInfoSizeLlvm = LLVMConstInt(codegen.intPtrType, serviceInfoSize.toLong(), 1)!!
|
||||||
|
val bodyWithSkippedServiceInfoPtr = intToPtr(add(bodyPtr, serviceInfoSizeLlvm), kInt8Ptr)
|
||||||
|
call(context.llvm.memsetFunction,
|
||||||
|
listOf(bodyWithSkippedServiceInfoPtr,
|
||||||
|
Int8(0).llvm,
|
||||||
|
Int32(bodySize - serviceInfoSize).llvm,
|
||||||
|
Int1(0).llvm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTypeInfoForLocalObject(objectHeader: LLVMValueRef, typeInfoPointer: LLVMValueRef) = with(functionGenerationContext) {
|
||||||
|
val typeInfo = structGep(objectHeader, 0, "typeInfoOrMeta_")
|
||||||
|
// Set tag OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER.
|
||||||
|
val typeInfoValue = intToPtr(or(ptrToInt(typeInfoPointer, codegen.intPtrType),
|
||||||
|
codegen.immThreeIntPtrType), kTypeInfoPtr)
|
||||||
|
store(typeInfoValue, typeInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class FunctionGenerationContext(val function: LLVMValueRef,
|
internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||||
val codegen: CodeGenerator,
|
val codegen: CodeGenerator,
|
||||||
startLocation: LocationInfo?,
|
startLocation: LocationInfo?,
|
||||||
@@ -192,10 +328,13 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
|
|
||||||
private val prologueBb = basicBlockInFunction("prologue", startLocation)
|
private val prologueBb = basicBlockInFunction("prologue", startLocation)
|
||||||
private val localsInitBb = basicBlockInFunction("locals_init", startLocation)
|
private val localsInitBb = basicBlockInFunction("locals_init", startLocation)
|
||||||
|
private val stackLocalsInitBb = basicBlockInFunction("stack_locals_init", startLocation)
|
||||||
private val entryBb = basicBlockInFunction("entry", startLocation)
|
private val entryBb = basicBlockInFunction("entry", startLocation)
|
||||||
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
||||||
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
||||||
|
|
||||||
|
val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: consider merging this with [ExceptionHandler].
|
* TODO: consider merging this with [ExceptionHandler].
|
||||||
*/
|
*/
|
||||||
@@ -348,7 +487,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
// This allows appropriate rootset accounting by just looking at the stack slots,
|
// This allows appropriate rootset accounting by just looking at the stack slots,
|
||||||
// along with ability to allocate in appropriate arena.
|
// along with ability to allocate in appropriate arena.
|
||||||
val resultSlot = when (resultLifetime.slotType) {
|
val resultSlot = when (resultLifetime.slotType) {
|
||||||
SlotType.ARENA -> {
|
SlotType.STACK -> {
|
||||||
localAllocs++
|
localAllocs++
|
||||||
// Case of local call. Use memory allocated on stack.
|
// Case of local call. Use memory allocated on stack.
|
||||||
val type = LLVMGetReturnType(LLVMGetElementType(llvmFunction.type))!!
|
val type = LLVMGetReturnType(LLVMGetElementType(llvmFunction.type))!!
|
||||||
@@ -426,84 +565,29 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
fun allocInstance(typeInfo: LLVMValueRef, lifetime: Lifetime): LLVMValueRef =
|
fun allocInstance(typeInfo: LLVMValueRef, lifetime: Lifetime): LLVMValueRef =
|
||||||
call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
||||||
|
|
||||||
fun allocInstance(irClass: IrClass, lifetime: Lifetime): LLVMValueRef {
|
fun allocInstance(irClass: IrClass, lifetime: Lifetime, stackLocalsManager: StackLocalsManager) =
|
||||||
val typeInfo = codegen.typeInfoForAllocation(irClass)
|
if (lifetime == Lifetime.STACK)
|
||||||
return if (lifetime == Lifetime.LOCAL) {
|
stackLocalsManager.alloc(irClass,
|
||||||
val stackSlot = alloca(context.llvmDeclarations.forClass(irClass).bodyType)
|
// In case the allocation is not from the root scope, fields must be cleaned up explicitly,
|
||||||
val objectHeader = structGep(stackSlot, 0, "objHeader")
|
// as the object might be being reused.
|
||||||
setTypeInfoForLocalObject(objectHeader, typeInfo)
|
cleanFieldsExplicitly = stackLocalsManager != this.stackLocalsManager)
|
||||||
objectHeader
|
else
|
||||||
} else {
|
allocInstance(codegen.typeInfoForAllocation(irClass), lifetime)
|
||||||
allocInstance(typeInfo, lifetime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: find better place?
|
fun allocArray(
|
||||||
val arrayToElementType = mapOf(
|
irClass: IrClass,
|
||||||
"kotlin.ByteArray" to int8Type,
|
count: LLVMValueRef,
|
||||||
"kotlin.CharArray" to int16Type,
|
lifetime: Lifetime,
|
||||||
"kotlin.ShortArray" to int16Type,
|
exceptionHandler: ExceptionHandler
|
||||||
"kotlin.IntArray" to int32Type,
|
): LLVMValueRef {
|
||||||
"kotlin.LongArray" to int64Type,
|
|
||||||
"kotlin.FloatArray" to floatType,
|
|
||||||
"kotlin.DoubleArray" to doubleType,
|
|
||||||
"kotlin.BooleanArray" to int8Type
|
|
||||||
)
|
|
||||||
|
|
||||||
// Returns generated special type for local array.
|
|
||||||
// It's needed to prevent changing variables order on stack.
|
|
||||||
// TODO: add alignment calculation. Now it isn't needed, because of working only with primitive types.
|
|
||||||
private fun localArrayType(className: String, count: Int): LLVMTypeRef {
|
|
||||||
val name = "local#${className}${count}#internal"
|
|
||||||
// Create new type or get already created.
|
|
||||||
return context.declaredLocalArrays.getOrPut(name) {
|
|
||||||
val fieldTypes = listOf(kArrayHeader, LLVMArrayType(arrayToElementType[className]!!, count))
|
|
||||||
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
|
|
||||||
LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, 1)
|
|
||||||
classType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun allocArray(irClass: IrClass,
|
|
||||||
count: LLVMValueRef,
|
|
||||||
lifetime: Lifetime,
|
|
||||||
exceptionHandler: ExceptionHandler): LLVMValueRef {
|
|
||||||
val typeInfo = codegen.typeInfoValue(irClass)
|
val typeInfo = codegen.typeInfoValue(irClass)
|
||||||
return if (lifetime == Lifetime.LOCAL) {
|
return if (lifetime == Lifetime.STACK) {
|
||||||
val className = irClass.fqNameForIrSerialization.asString()
|
stackLocalsManager.allocArray(irClass, count)
|
||||||
assert(className in arrayToElementType)
|
|
||||||
allocArrayOnStack(className, count, typeInfo)
|
|
||||||
} else {
|
} else {
|
||||||
call(context.llvm.allocArrayFunction, listOf(typeInfo, count), lifetime, exceptionHandler)
|
call(context.llvm.allocArrayFunction, listOf(typeInfo, count), lifetime, exceptionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTypeInfoForLocalObject(objectHeader: LLVMValueRef, typeInfoPointer: LLVMValueRef) {
|
|
||||||
val typeInfo = structGep(objectHeader, 0, "typeInfoOrMeta_")
|
|
||||||
// Set tag OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER.
|
|
||||||
val typeInfoValue = intToPtr(or(ptrToInt(typeInfoPointer, codegen.intPtrType),
|
|
||||||
codegen.immThreeIntPtrType), kTypeInfoPtr)
|
|
||||||
store(typeInfoValue, typeInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun allocArrayOnStack(arrayTypeName: String, count: LLVMValueRef, typeInfo: LLVMValueRef): LLVMValueRef {
|
|
||||||
val constCount = extractConstUnsignedInt(count).toInt()
|
|
||||||
val arrayType = localArrayType(arrayTypeName, constCount)
|
|
||||||
val arraySlot = alloca(arrayType)
|
|
||||||
// Set array size in ArrayHeader.
|
|
||||||
val arrayHeaderSlot = structGep(arraySlot, 0, "arrayHeader")
|
|
||||||
setTypeInfoForLocalObject(arrayHeaderSlot, typeInfo)
|
|
||||||
val sizeField = structGep(arrayHeaderSlot, 1, "count_")
|
|
||||||
store(count, sizeField)
|
|
||||||
call(context.llvm.memsetFunction,
|
|
||||||
listOf(bitcast(kInt8Ptr, structGep(arraySlot, 1, "arrayBody")),
|
|
||||||
Int8(0).llvm,
|
|
||||||
Int32(constCount * LLVMSizeOfTypeInBits(codegen.llvmTargetData,
|
|
||||||
arrayToElementType[arrayTypeName]).toInt() / 8).llvm,
|
|
||||||
Int1(0).llvm))
|
|
||||||
return bitcast(kObjHeaderPtr, arrayHeaderSlot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unreachable(): LLVMValueRef? {
|
fun unreachable(): LLVMValueRef? {
|
||||||
if (context.config.debug) {
|
if (context.config.debug) {
|
||||||
call(context.llvm.llvmTrap, emptyList())
|
call(context.llvm.llvmTrap, emptyList())
|
||||||
@@ -1112,6 +1196,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
}
|
}
|
||||||
|
|
||||||
appendingTo(localsInitBb) {
|
appendingTo(localsInitBb) {
|
||||||
|
br(stackLocalsInitBb)
|
||||||
|
}
|
||||||
|
|
||||||
|
appendingTo(stackLocalsInitBb) {
|
||||||
br(entryBb)
|
br(entryBb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,6 +1381,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
call(context.llvm.leaveFrameFunction,
|
call(context.llvm.leaveFrameFunction,
|
||||||
listOf(slotsPhi!!, Int32(vars.skipSlots).llvm, Int32(slotCount).llvm))
|
listOf(slotsPhi!!, Int32(vars.skipSlots).llvm, Int32(slotCount).llvm))
|
||||||
}
|
}
|
||||||
|
stackLocalsManager.clean(refsOnly = true) // Only bother about not leaving any dangling references.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -34,6 +34,9 @@ import kotlin.properties.ReadOnlyProperty
|
|||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
internal sealed class SlotType {
|
internal sealed class SlotType {
|
||||||
|
// An object is statically allocated on stack.
|
||||||
|
object STACK : SlotType()
|
||||||
|
|
||||||
// Frame local arena slot can be used.
|
// Frame local arena slot can be used.
|
||||||
object ARENA : SlotType()
|
object ARENA : SlotType()
|
||||||
|
|
||||||
@@ -58,6 +61,12 @@ internal sealed class SlotType {
|
|||||||
|
|
||||||
// Lifetimes class of reference, computed by escape analysis.
|
// Lifetimes class of reference, computed by escape analysis.
|
||||||
internal sealed class Lifetime(val slotType: SlotType) {
|
internal sealed class Lifetime(val slotType: SlotType) {
|
||||||
|
object STACK : Lifetime(SlotType.STACK) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "STACK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If reference is frame-local (only obtained from some call and never leaves).
|
// If reference is frame-local (only obtained from some call and never leaves).
|
||||||
object LOCAL : Lifetime(SlotType.ARENA) {
|
object LOCAL : Lifetime(SlotType.ARENA) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@@ -66,7 +75,7 @@ internal sealed class Lifetime(val slotType: SlotType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If reference is only returned.
|
// If reference is only returned.
|
||||||
object RETURN_VALUE : Lifetime(SlotType.RETURN) {
|
object RETURN_VALUE : Lifetime(SlotType.ANONYMOUS) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "RETURN_VALUE"
|
return "RETURN_VALUE"
|
||||||
}
|
}
|
||||||
@@ -492,8 +501,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
|||||||
val initInstanceFunction = importModelSpecificRtFunction("InitInstance")
|
val initInstanceFunction = importModelSpecificRtFunction("InitInstance")
|
||||||
val initSharedInstanceFunction = importModelSpecificRtFunction("InitSharedInstance")
|
val initSharedInstanceFunction = importModelSpecificRtFunction("InitSharedInstance")
|
||||||
val updateHeapRefFunction = importModelSpecificRtFunction("UpdateHeapRef")
|
val updateHeapRefFunction = importModelSpecificRtFunction("UpdateHeapRef")
|
||||||
|
val releaseHeapRefFunction = importModelSpecificRtFunction("ReleaseHeapRef")
|
||||||
val updateStackRefFunction = importModelSpecificRtFunction("UpdateStackRef")
|
val updateStackRefFunction = importModelSpecificRtFunction("UpdateStackRef")
|
||||||
val updateReturnRefFunction = importModelSpecificRtFunction("UpdateReturnRef")
|
val updateReturnRefFunction = importModelSpecificRtFunction("UpdateReturnRef")
|
||||||
|
val zeroHeapRefFunction = importRtFunction("ZeroHeapRef")
|
||||||
|
val zeroArrayRefsFunction = importRtFunction("ZeroArrayRefs")
|
||||||
val enterFrameFunction = importModelSpecificRtFunction("EnterFrame")
|
val enterFrameFunction = importModelSpecificRtFunction("EnterFrame")
|
||||||
val leaveFrameFunction = importModelSpecificRtFunction("LeaveFrame")
|
val leaveFrameFunction = importModelSpecificRtFunction("LeaveFrame")
|
||||||
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
|
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
|
||||||
|
|||||||
+3
-1
@@ -103,6 +103,8 @@ internal interface IntrinsicGeneratorEnvironment {
|
|||||||
|
|
||||||
val exceptionHandler: ExceptionHandler
|
val exceptionHandler: ExceptionHandler
|
||||||
|
|
||||||
|
val stackLocalsManager: StackLocalsManager
|
||||||
|
|
||||||
fun calculateLifetime(element: IrElement): Lifetime
|
fun calculateLifetime(element: IrElement): Lifetime
|
||||||
|
|
||||||
fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass? = null): LLVMValueRef
|
fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass? = null): LLVMValueRef
|
||||||
@@ -303,7 +305,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
|||||||
val typeParameterT = context.ir.symbols.createUninitializedInstance.descriptor.typeParameters[0]
|
val typeParameterT = context.ir.symbols.createUninitializedInstance.descriptor.typeParameters[0]
|
||||||
val enumClass = callSite.getTypeArgument(typeParameterT)!!
|
val enumClass = callSite.getTypeArgument(typeParameterT)!!
|
||||||
val enumIrClass = enumClass.getClass()!!
|
val enumIrClass = enumClass.getClass()!!
|
||||||
return allocInstance(enumIrClass, environment.calculateLifetime(callSite))
|
return allocInstance(enumIrClass, environment.calculateLifetime(callSite), environment.stackLocalsManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef =
|
private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef =
|
||||||
|
|||||||
+14
-1
@@ -134,6 +134,8 @@ internal interface CodeContext {
|
|||||||
|
|
||||||
fun genThrow(exception: LLVMValueRef)
|
fun genThrow(exception: LLVMValueRef)
|
||||||
|
|
||||||
|
val stackLocalsManager: StackLocalsManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares the variable.
|
* Declares the variable.
|
||||||
* @return index of declared variable.
|
* @return index of declared variable.
|
||||||
@@ -218,6 +220,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
override val exceptionHandler: ExceptionHandler
|
override val exceptionHandler: ExceptionHandler
|
||||||
get() = currentCodeContext.exceptionHandler
|
get() = currentCodeContext.exceptionHandler
|
||||||
|
|
||||||
|
override val stackLocalsManager: StackLocalsManager
|
||||||
|
get() = currentCodeContext.stackLocalsManager
|
||||||
|
|
||||||
override fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass?) =
|
override fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass?) =
|
||||||
evaluateSimpleFunctionCall(function, args, resultLifetime, superClass)
|
evaluateSimpleFunctionCall(function, args, resultLifetime, superClass)
|
||||||
|
|
||||||
@@ -248,6 +253,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
override fun genThrow(exception: LLVMValueRef) = unsupported()
|
override fun genThrow(exception: LLVMValueRef) = unsupported()
|
||||||
|
|
||||||
|
override val stackLocalsManager: StackLocalsManager get() = unsupported()
|
||||||
|
|
||||||
override fun genDeclareVariable(variable: IrVariable, value: LLVMValueRef?, variableLocation: VariableDebugLocation?) = unsupported(variable)
|
override fun genDeclareVariable(variable: IrVariable, value: LLVMValueRef?, variableLocation: VariableDebugLocation?) = unsupported(variable)
|
||||||
|
|
||||||
override fun getDeclaredVariable(variable: IrVariable) = -1
|
override fun getDeclaredVariable(variable: IrVariable) = -1
|
||||||
@@ -529,6 +536,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
else
|
else
|
||||||
super.genContinue(destination)
|
super.genContinue(destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val stackLocalsManager: StackLocalsManager =
|
||||||
|
object : StackLocalsManager by functionGenerationContext.stackLocalsManager { }
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
@@ -668,6 +678,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
functionGenerationContext.unreachable()
|
functionGenerationContext.unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val stackLocalsManager get() = functionGenerationContext.stackLocalsManager
|
||||||
|
|
||||||
override fun functionScope(): CodeContext = this
|
override fun functionScope(): CodeContext = this
|
||||||
|
|
||||||
|
|
||||||
@@ -2178,7 +2190,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
constructedClass.isObjCClass() -> error("Call should've been lowered: ${callee.dump()}")
|
constructedClass.isObjCClass() -> error("Call should've been lowered: ${callee.dump()}")
|
||||||
|
|
||||||
else -> functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee))
|
else -> functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee),
|
||||||
|
currentCodeContext.stackLocalsManager)
|
||||||
}
|
}
|
||||||
evaluateSimpleFunctionCall(callee.symbol.owner,
|
evaluateSimpleFunctionCall(callee.symbol.owner,
|
||||||
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
||||||
|
|||||||
+1
-1
@@ -857,7 +857,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
|||||||
val coroutineSuspended = callFromBridge(
|
val coroutineSuspended = callFromBridge(
|
||||||
codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner),
|
codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
Lifetime.LOCAL
|
Lifetime.STACK
|
||||||
)
|
)
|
||||||
ifThen(icmpNe(targetResult!!, coroutineSuspended)) {
|
ifThen(icmpNe(targetResult!!, coroutineSuspended)) {
|
||||||
// Callee haven't suspended, so it isn't going to call the completion. Call it here:
|
// Callee haven't suspended, so it isn't going to call the completion. Call it here:
|
||||||
|
|||||||
+100
-124
@@ -11,18 +11,20 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraph
|
|||||||
import org.jetbrains.kotlin.backend.konan.DirectedGraphNode
|
import org.jetbrains.kotlin.backend.konan.DirectedGraphNode
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
|
||||||
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol)
|
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol.Declared)
|
||||||
: DirectedGraphNode<DataFlowIR.FunctionSymbol> {
|
: DirectedGraphNode<DataFlowIR.FunctionSymbol.Declared> {
|
||||||
|
|
||||||
override val key get() = symbol
|
override val key get() = symbol
|
||||||
|
|
||||||
override val directEdges: List<DataFlowIR.FunctionSymbol> by lazy {
|
override val directEdges: List<DataFlowIR.FunctionSymbol.Declared> by lazy {
|
||||||
graph.directEdges[symbol]!!.callSites
|
graph.directEdges[symbol]!!.callSites
|
||||||
|
.filter { !it.isVirtual }
|
||||||
.map { it.actualCallee }
|
.map { it.actualCallee }
|
||||||
.filter { graph.reversedEdges.containsKey(it) }
|
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
||||||
|
.filter { graph.directEdges.containsKey(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val reversedEdges: List<DataFlowIR.FunctionSymbol> by lazy {
|
override val reversedEdges: List<DataFlowIR.FunctionSymbol.Declared> by lazy {
|
||||||
graph.reversedEdges[symbol]!!
|
graph.reversedEdges[symbol]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,26 +33,31 @@ internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.Functi
|
|||||||
val callSites = mutableListOf<CallSite>()
|
val callSites = mutableListOf<CallSite>()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGraphNode>,
|
internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol.Declared, CallGraphNode>,
|
||||||
val reversedEdges: Map<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>)
|
val reversedEdges: Map<DataFlowIR.FunctionSymbol.Declared, MutableList<DataFlowIR.FunctionSymbol.Declared>>,
|
||||||
: DirectedGraph<DataFlowIR.FunctionSymbol, CallGraphNode> {
|
val rootExternalFunctions: List<DataFlowIR.FunctionSymbol>)
|
||||||
|
: DirectedGraph<DataFlowIR.FunctionSymbol.Declared, CallGraphNode> {
|
||||||
|
|
||||||
override val nodes get() = directEdges.values
|
override val nodes get() = directEdges.values
|
||||||
|
|
||||||
override fun get(key: DataFlowIR.FunctionSymbol) = directEdges[key]!!
|
override fun get(key: DataFlowIR.FunctionSymbol.Declared) = directEdges[key]!!
|
||||||
|
|
||||||
fun addEdge(caller: DataFlowIR.FunctionSymbol, callSite: CallGraphNode.CallSite) {
|
fun addEdge(caller: DataFlowIR.FunctionSymbol.Declared, callSite: CallGraphNode.CallSite) {
|
||||||
directEdges[caller]!!.callSites += callSite
|
directEdges[caller]!!.callSites += callSite
|
||||||
reversedEdges[callSite.actualCallee]?.add(caller)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addReversedEdge(caller: DataFlowIR.FunctionSymbol.Declared, callee: DataFlowIR.FunctionSymbol.Declared) {
|
||||||
|
reversedEdges[callee]!!.add(caller)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CallGraphBuilder(val context: Context,
|
internal class CallGraphBuilder(
|
||||||
val moduleDFG: ModuleDFG,
|
val context: Context,
|
||||||
val externalModulesDFG: ExternalModulesDFG,
|
val moduleDFG: ModuleDFG,
|
||||||
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
val externalModulesDFG: ExternalModulesDFG,
|
||||||
val gotoExternal: Boolean) {
|
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
||||||
|
val nonDevirtualizedCallSitesUnfoldFactor: Int
|
||||||
|
) {
|
||||||
|
|
||||||
private val DEBUG = 0
|
private val DEBUG = 0
|
||||||
|
|
||||||
@@ -66,47 +73,45 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
private val visitedFunctions = mutableSetOf<DataFlowIR.FunctionSymbol>()
|
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol.Declared, CallGraphNode>()
|
||||||
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol, CallGraphNode>()
|
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol.Declared, MutableList<DataFlowIR.FunctionSymbol.Declared>>()
|
||||||
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>()
|
private val externalRootFunctions = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||||
private val callGraph = CallGraph(directEdges, reversedEdges)
|
private val callGraph = CallGraph(directEdges, reversedEdges, externalRootFunctions)
|
||||||
private val functionStack = mutableListOf<DataFlowIR.FunctionSymbol>()
|
|
||||||
|
private data class HandleFunctionParams(val caller: DataFlowIR.FunctionSymbol.Declared?,
|
||||||
|
val calleeFunction: DataFlowIR.Function)
|
||||||
|
private val functionStack = mutableListOf<HandleFunctionParams>()
|
||||||
|
|
||||||
fun build(): CallGraph {
|
fun build(): CallGraph {
|
||||||
val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG)
|
val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG)
|
||||||
@Suppress("LoopToCallChain")
|
for (symbol in rootSet) {
|
||||||
for (symbol in rootSet)
|
val function = moduleDFG.functions[symbol]
|
||||||
functionStack.push(symbol)
|
if (function == null)
|
||||||
|
externalRootFunctions.add(symbol)
|
||||||
|
else
|
||||||
|
functionStack.push(HandleFunctionParams(null, function))
|
||||||
|
}
|
||||||
|
|
||||||
while (functionStack.isNotEmpty()) {
|
while (functionStack.isNotEmpty()) {
|
||||||
val symbol = functionStack.pop()
|
val (caller, calleeFunction) = functionStack.pop()
|
||||||
if (symbol !in visitedFunctions)
|
val callee = calleeFunction.symbol as DataFlowIR.FunctionSymbol.Declared
|
||||||
handleFunction(symbol)
|
val gotoCallee = !directEdges.containsKey(callee)
|
||||||
|
if (gotoCallee)
|
||||||
|
addNode(callee)
|
||||||
|
if (caller != null)
|
||||||
|
callGraph.addReversedEdge(caller, callee)
|
||||||
|
if (gotoCallee)
|
||||||
|
handleFunction(callee, calleeFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_OUTPUT(0) {
|
|
||||||
println("DirectEdges: ${directEdges.size}")
|
|
||||||
println("ReversedEdges: ${reversedEdges.size}")
|
|
||||||
println("Sum: ${directEdges.values.sumBy { it.callSites.size }}")
|
|
||||||
}
|
|
||||||
|
|
||||||
return callGraph
|
return callGraph
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNode(symbol: DataFlowIR.FunctionSymbol) {
|
private fun addNode(symbol: DataFlowIR.FunctionSymbol.Declared) {
|
||||||
if (directEdges.containsKey(symbol))
|
directEdges[symbol] = CallGraphNode(callGraph, symbol)
|
||||||
return
|
reversedEdges[symbol] = mutableListOf()
|
||||||
val node = CallGraphNode(callGraph, symbol)
|
|
||||||
directEdges.put(symbol, node)
|
|
||||||
val list = mutableListOf<DataFlowIR.FunctionSymbol>()
|
|
||||||
reversedEdges.put(symbol, list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val symbols = context.ir.symbols
|
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit): Unit =
|
||||||
private val arrayGet = symbols.arrayGet[symbols.array]!!.owner
|
|
||||||
private val arraySet = symbols.arraySet[symbols.array]!!.owner
|
|
||||||
|
|
||||||
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit) =
|
|
||||||
forEachNonScopeNode { node ->
|
forEachNonScopeNode { node ->
|
||||||
when (node) {
|
when (node) {
|
||||||
is DataFlowIR.Node.Call -> block(node)
|
is DataFlowIR.Node.Call -> block(node)
|
||||||
@@ -137,92 +142,63 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
returnType = node.symbol.returnParameter.type,
|
returnType = node.symbol.returnParameter.type,
|
||||||
irCallSite = null
|
irCallSite = null
|
||||||
))
|
))
|
||||||
|
|
||||||
|
else -> { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun staticCall(caller: DataFlowIR.FunctionSymbol, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
private fun staticCall(caller: DataFlowIR.FunctionSymbol.Declared, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
||||||
callGraph.addEdge(caller, CallGraphNode.CallSite(call, false, callee))
|
val resolvedCallee = callee.resolved()
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
val callSite = CallGraphNode.CallSite(call, false, resolvedCallee)
|
||||||
&& !directEdges.containsKey(callee))
|
val function = moduleDFG.functions[resolvedCallee]
|
||||||
functionStack.push(callee)
|
callGraph.addEdge(caller, callSite)
|
||||||
|
if (function != null)
|
||||||
|
functionStack.push(HandleFunctionParams(caller, function))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFunction(symbol: DataFlowIR.FunctionSymbol) {
|
private fun handleFunction(symbol: DataFlowIR.FunctionSymbol.Declared, function: DataFlowIR.Function) {
|
||||||
visitedFunctions += symbol
|
val body = function.body
|
||||||
if (gotoExternal) {
|
body.forEachCallSite { call ->
|
||||||
addNode(symbol)
|
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] }
|
||||||
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] ?: return
|
when {
|
||||||
val body = function.body
|
call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee)
|
||||||
body.forEachCallSite { call ->
|
|
||||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] }
|
|
||||||
when {
|
|
||||||
call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee.resolved())
|
|
||||||
|
|
||||||
devirtualizedCallSite != null -> {
|
devirtualizedCallSite != null -> {
|
||||||
devirtualizedCallSite.possibleCallees.forEach {
|
devirtualizedCallSite.possibleCallees.forEach {
|
||||||
staticCall(symbol, call, it.callee.resolved())
|
staticCall(symbol, call, it.callee)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call.receiverType == DataFlowIR.Type.Virtual -> {
|
|
||||||
// Skip callsite. This can only be for invocations Any's methods on instances of ObjC classes.
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
// Callsite has not been devirtualized - conservatively assume the worst:
|
|
||||||
// any inheritor of the receiver type is possible here.
|
|
||||||
val typeHierarchy = devirtualizationAnalysisResult.typeHierarchy
|
|
||||||
typeHierarchy.inheritorsOf(call.receiverType as DataFlowIR.Type.Declared).forEachBit {
|
|
||||||
val receiverType = typeHierarchy.allTypes[it]
|
|
||||||
if (receiverType.isAbstract) return@forEachBit
|
|
||||||
// TODO: Unconservative way - when we can use it?
|
|
||||||
//.filter { devirtualizationAnalysisResult.instantiatingClasses.contains(it) }
|
|
||||||
val actualCallee = when (call) {
|
|
||||||
is DataFlowIR.Node.VtableCall ->
|
|
||||||
receiverType.vtable[call.calleeVtableIndex]
|
|
||||||
|
|
||||||
is DataFlowIR.Node.ItableCall ->
|
|
||||||
receiverType.itable[call.calleeHash]!!
|
|
||||||
|
|
||||||
else -> error("Unreachable")
|
|
||||||
}
|
|
||||||
staticCall(symbol, call, actualCallee.resolved())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
call.receiverType == DataFlowIR.Type.Virtual -> {
|
||||||
var function = moduleDFG.functions[symbol]
|
// Skip callsite. This can only be for invocations Any's methods on instances of ObjC classes.
|
||||||
var local = true
|
}
|
||||||
if (function != null)
|
|
||||||
addNode(symbol)
|
else -> {
|
||||||
else {
|
// Callsite has not been devirtualized - conservatively assume the worst:
|
||||||
function = externalModulesDFG.functionDFGs[symbol]!!
|
// any inheritor of the receiver type is possible here.
|
||||||
local = false
|
val typeHierarchy = devirtualizationAnalysisResult.typeHierarchy
|
||||||
}
|
val allPossibleCallees = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||||
val body = function.body
|
typeHierarchy.inheritorsOf(call.receiverType as DataFlowIR.Type.Declared).forEachBit {
|
||||||
body.forEachCallSite { call ->
|
val receiverType = typeHierarchy.allTypes[it]
|
||||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites.get(it) }
|
if (receiverType.isAbstract) return@forEachBit
|
||||||
if (devirtualizedCallSite == null) {
|
// TODO: Unconservative way - when we can use it?
|
||||||
val callee = call.callee.resolved()
|
//.filter { devirtualizationAnalysisResult.instantiatingClasses.contains(it) }
|
||||||
if (moduleDFG.functions.containsKey(callee))
|
val actualCallee = when (call) {
|
||||||
addNode(callee)
|
is DataFlowIR.Node.VtableCall ->
|
||||||
if (local)
|
receiverType.vtable[call.calleeVtableIndex]
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
is DataFlowIR.Node.ItableCall ->
|
||||||
&& call !is DataFlowIR.Node.VirtualCall
|
receiverType.itable[call.calleeHash]!!
|
||||||
&& !visitedFunctions.contains(callee))
|
|
||||||
functionStack.push(callee)
|
else -> error("Unreachable")
|
||||||
} else {
|
}
|
||||||
devirtualizedCallSite.possibleCallees.forEach {
|
allPossibleCallees.add(actualCallee)
|
||||||
val callee = it.callee.resolved()
|
}
|
||||||
if (moduleDFG.functions.containsKey(callee))
|
if (allPossibleCallees.size <= nonDevirtualizedCallSitesUnfoldFactor)
|
||||||
addNode(callee)
|
allPossibleCallees.forEach { staticCall(symbol, call, it) }
|
||||||
if (local)
|
else {
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
val callSite = CallGraphNode.CallSite(call, true, call.callee)
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
callGraph.addEdge(symbol, callSite)
|
||||||
&& !visitedFunctions.contains(callee))
|
|
||||||
functionStack.push(callee)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -63,7 +63,7 @@ private class VariableValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add(variable: IrVariable, element: IrExpression) =
|
fun add(variable: IrVariable, element: IrExpression) =
|
||||||
elementData[variable]!!.values.add(element)
|
elementData[variable]?.values?.add(element)
|
||||||
|
|
||||||
private fun add(variable: IrVariable, elements: Set<IrExpression>) =
|
private fun add(variable: IrVariable, elements: Set<IrExpression>) =
|
||||||
elementData[variable]?.values?.addAll(elements)
|
elementData[variable]?.values?.addAll(elements)
|
||||||
@@ -736,7 +736,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
createUninitializedInstanceSymbol ->
|
createUninitializedInstanceSymbol ->
|
||||||
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
|
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
|
||||||
value.getTypeArgument(0)!!.getClass()!!
|
value.getTypeArgument(0)!!.getClass()!!
|
||||||
))
|
), value)
|
||||||
|
|
||||||
reinterpret -> getNode(value.extensionReceiver!!).value
|
reinterpret -> getNode(value.extensionReceiver!!).value
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -251,7 +251,7 @@ internal object DataFlowIR {
|
|||||||
|
|
||||||
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
|
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
|
||||||
|
|
||||||
class AllocInstance(val type: Type) : Node()
|
class AllocInstance(val type: Type, val irCallSite: IrCall?) : Node()
|
||||||
|
|
||||||
class FunctionReference(val symbol: FunctionSymbol, val type: Type, val returnType: Type) : Node()
|
class FunctionReference(val symbol: FunctionSymbol, val type: Type, val returnType: Type) : Node()
|
||||||
|
|
||||||
@@ -647,10 +647,15 @@ internal object DataFlowIR {
|
|||||||
&& !irClass.isNonGeneratedAnnotation()
|
&& !irClass.isNonGeneratedAnnotation()
|
||||||
&& (it.isOverridableOrOverrides || bridgeTarget != null || function.isSpecial || !irClass.isFinal())
|
&& (it.isOverridableOrOverrides || bridgeTarget != null || function.isSpecial || !irClass.isFinal())
|
||||||
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
|
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
|
||||||
if (it.isExported())
|
val frozen = it is IrConstructor && irClass!!.annotations.findAnnotation(KonanFqNames.frozen) != null
|
||||||
|
val functionSymbol = if (it.isExported())
|
||||||
FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
|
FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
|
||||||
else
|
else
|
||||||
FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
|
FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
|
||||||
|
if (frozen) {
|
||||||
|
functionSymbol.escapes = 0b1 // Assume instances of frozen classes escape.
|
||||||
|
}
|
||||||
|
functionSymbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
functionMap[it] = symbol
|
functionMap[it] = symbol
|
||||||
|
|||||||
+1337
-341
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isBuiltInOperator
|
|
||||||
|
|
||||||
internal object LocalEscapeAnalysis {
|
internal object LocalEscapeAnalysis {
|
||||||
private val DEBUG = 0
|
private val DEBUG = 0
|
||||||
@@ -187,7 +186,7 @@ internal object LocalEscapeAnalysis {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
ir?.let {
|
ir?.let {
|
||||||
lifetimes.put(it, Lifetime.LOCAL)
|
lifetimes.put(it, Lifetime.STACK)
|
||||||
DEBUG_OUTPUT(3) { println("${ir} does not escape") }
|
DEBUG_OUTPUT(3) { println("${ir} does not escape") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3252,6 +3252,13 @@ void UpdateReturnRefRelaxed(ObjHeader** returnSlot, const ObjHeader* value) {
|
|||||||
updateReturnRef<false>(returnSlot, value);
|
updateReturnRef<false>(returnSlot, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZeroArrayRefs(ArrayHeader* array) {
|
||||||
|
for (uint32_t index = 0; index < array->count_; ++index) {
|
||||||
|
ObjHeader** location = ArrayAddressOfElementAt(array, index);
|
||||||
|
zeroHeapRef(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateHeapRefIfNull(ObjHeader** location, const ObjHeader* object) {
|
void UpdateHeapRefIfNull(ObjHeader** location, const ObjHeader* object) {
|
||||||
updateHeapRefIfNull(location, object);
|
updateHeapRefIfNull(location, object);
|
||||||
}
|
}
|
||||||
@@ -3426,6 +3433,7 @@ void FreezeSubgraph(ObjHeader* root) {
|
|||||||
// This function is called from field mutators to check if object's header is frozen.
|
// This function is called from field mutators to check if object's header is frozen.
|
||||||
// If object is frozen or permanent, an exception is thrown.
|
// If object is frozen or permanent, an exception is thrown.
|
||||||
void MutationCheck(ObjHeader* obj) {
|
void MutationCheck(ObjHeader* obj) {
|
||||||
|
if (obj->local()) return;
|
||||||
auto* container = obj->container();
|
auto* container = obj->container();
|
||||||
if (container == nullptr || container->frozen())
|
if (container == nullptr || container->frozen())
|
||||||
ThrowInvalidMutabilityException(obj);
|
ThrowInvalidMutabilityException(obj);
|
||||||
|
|||||||
@@ -501,7 +501,9 @@ MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object)
|
|||||||
// Sets heap location.
|
// Sets heap location.
|
||||||
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
||||||
// Zeroes heap location.
|
// Zeroes heap location.
|
||||||
void ZeroHeapRef(ObjHeader** location);
|
void ZeroHeapRef(ObjHeader** location) RUNTIME_NOTHROW;
|
||||||
|
// Zeroes an array.
|
||||||
|
void ZeroArrayRefs(ArrayHeader* array) RUNTIME_NOTHROW;
|
||||||
// Zeroes stack location.
|
// Zeroes stack location.
|
||||||
MODEL_VARIANTS(void, ZeroStackRef, ObjHeader** location);
|
MODEL_VARIANTS(void, ZeroStackRef, ObjHeader** location);
|
||||||
// Updates stack location.
|
// Updates stack location.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public final class Array<T> {
|
|||||||
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException].
|
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException].
|
||||||
*/
|
*/
|
||||||
@SymbolName("Kotlin_Array_get")
|
@SymbolName("Kotlin_Array_get")
|
||||||
@PointsTo(0b0100, 0, 0b0001) // <this> points to <return>, <return> points to <this>.
|
@PointsTo(0x000, 0x000, 0x002) // ret -> this.intestines
|
||||||
external public operator fun get(index: Int): T
|
external public operator fun get(index: Int): T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +67,7 @@ public final class Array<T> {
|
|||||||
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException].
|
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException].
|
||||||
*/
|
*/
|
||||||
@SymbolName("Kotlin_Array_set")
|
@SymbolName("Kotlin_Array_set")
|
||||||
@PointsTo(0b0100, 0, 0b0001) // <this> points to <value>, <value> points to <this>.
|
@PointsTo(0x300, 0x000, 0x000) // this.intestines -> value
|
||||||
external public operator fun set(index: Int, value: T): Unit
|
external public operator fun set(index: Int, value: T): Unit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ public inline fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
|
|||||||
* Returns an array containing the specified elements.
|
* Returns an array containing the specified elements.
|
||||||
*/
|
*/
|
||||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||||
@PointsTo(0, 1) // <return> points to <array> argument.
|
@PointsTo(0x00, 0x01) // ret -> elements
|
||||||
public external inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>
|
public external inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>
|
||||||
|
|
||||||
@SymbolName("Kotlin_emptyArray")
|
@SymbolName("Kotlin_emptyArray")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import kotlin.native.internal.ExportForCppRuntime
|
|||||||
* either throwing exception or returning some kind of implementation-specific default value.
|
* either throwing exception or returning some kind of implementation-specific default value.
|
||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
internal inline fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||||
// TODO: special case for size == 0?
|
// TODO: special case for size == 0?
|
||||||
require(size >= 0) { "capacity must be non-negative." }
|
require(size >= 0) { "capacity must be non-negative." }
|
||||||
@Suppress("TYPE_PARAMETER_AS_REIFIED")
|
@Suppress("TYPE_PARAMETER_AS_REIFIED")
|
||||||
@@ -76,7 +76,7 @@ internal fun <E> Array<E>.resetAt(index: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SymbolName("Kotlin_Array_fillImpl")
|
@SymbolName("Kotlin_Array_fillImpl")
|
||||||
@PointsTo(0b01000, 0, 0, 0b00001) // <array> points to <value>, <value> points to <array>.
|
@PointsTo(0x3000, 0x0000, 0x0000, 0x0000) // array.intestines -> value
|
||||||
internal external fun <T> arrayFill(array: Array<T>, fromIndex: Int, toIndex: Int, value: T)
|
internal external fun <T> arrayFill(array: Array<T>, fromIndex: Int, toIndex: Int, value: T)
|
||||||
|
|
||||||
@SymbolName("Kotlin_ByteArray_fillImpl")
|
@SymbolName("Kotlin_ByteArray_fillImpl")
|
||||||
@@ -125,7 +125,7 @@ internal fun <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SymbolName("Kotlin_Array_copyImpl")
|
@SymbolName("Kotlin_Array_copyImpl")
|
||||||
@PointsTo(0b000100, 0, 0b000001) // <array> points to <destination>, <destination> points to <array>.
|
@PointsTo(0x00000, 0x00000, 0x00004, 0x00000, 0x00000) // destination.intestines -> array.intestines
|
||||||
internal external fun arrayCopy(array: Array<Any?>, fromIndex: Int, destination: Array<Any?>, toIndex: Int, count: Int)
|
internal external fun arrayCopy(array: Array<Any?>, fromIndex: Int, destination: Array<Any?>, toIndex: Int, count: Int)
|
||||||
|
|
||||||
@SymbolName("Kotlin_ByteArray_copyImpl")
|
@SymbolName("Kotlin_ByteArray_copyImpl")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package kotlin.native
|
|||||||
|
|
||||||
import kotlin.native.concurrent.isFrozen
|
import kotlin.native.concurrent.isFrozen
|
||||||
import kotlin.native.concurrent.InvalidMutabilityException
|
import kotlin.native.concurrent.InvalidMutabilityException
|
||||||
|
import kotlin.native.internal.Escapes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||||
@@ -51,6 +52,7 @@ public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): Report
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
||||||
|
@Escapes(0b01) // <hook> escapes
|
||||||
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ internal annotation class FixmeConcurrency
|
|||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
internal annotation class Escapes(val who: Int)
|
internal annotation class Escapes(val who: Int)
|
||||||
|
|
||||||
|
// Decyphering of binary values can be found in EscapeAnalysis.kt
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
internal annotation class PointsTo(vararg val onWhom: Int)
|
internal annotation class PointsTo(vararg val onWhom: Int)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import kotlinx.cinterop.COpaquePointer
|
|||||||
import kotlin.native.internal.ExportForCppRuntime
|
import kotlin.native.internal.ExportForCppRuntime
|
||||||
import kotlin.native.internal.Frozen
|
import kotlin.native.internal.Frozen
|
||||||
import kotlin.native.internal.NoReorderFields
|
import kotlin.native.internal.NoReorderFields
|
||||||
|
import kotlin.native.internal.Escapes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Theory of operations:
|
* Theory of operations:
|
||||||
@@ -53,6 +54,7 @@ internal abstract class WeakReferenceImpl {
|
|||||||
|
|
||||||
// Get a counter from non-null object.
|
// Get a counter from non-null object.
|
||||||
@SymbolName("Konan_getWeakReferenceImpl")
|
@SymbolName("Konan_getWeakReferenceImpl")
|
||||||
|
@Escapes(0b01) // referent escapes.
|
||||||
external internal fun getWeakReferenceImpl(referent: Any): WeakReferenceImpl
|
external internal fun getWeakReferenceImpl(referent: Any): WeakReferenceImpl
|
||||||
|
|
||||||
// Create a counter object.
|
// Create a counter object.
|
||||||
|
|||||||
Reference in New Issue
Block a user