[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>>)
|
||||
|
||||
// The Kosoraju-Sharir algorithm.
|
||||
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(private val graph: DirectedGraph<K, N>) {
|
||||
private val visited = mutableSetOf<K>()
|
||||
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) {
|
||||
with(config.configuration) {
|
||||
disable(compileTimeEvaluatePhase)
|
||||
disable(escapeAnalysisPhase)
|
||||
disable(localEscapeAnalysisPhase)
|
||||
|
||||
// Don't serialize anything to a final executable.
|
||||
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)
|
||||
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(localEscapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(escapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
||||
|
||||
+19
-13
@@ -129,10 +129,15 @@ internal val dcePhase = makeKonanModuleOpPhase(
|
||||
context, context.moduleDFG!!,
|
||||
externalModulesDFG,
|
||||
context.devirtualizationAnalysisResult!!,
|
||||
true
|
||||
// For DCE we don't wanna miss any potentially reachable function.
|
||||
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
|
||||
).build()
|
||||
|
||||
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) {
|
||||
if (!node.symbol.isGlobalInitializer)
|
||||
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
||||
@@ -200,22 +205,23 @@ internal val dcePhase = makeKonanModuleOpPhase(
|
||||
)
|
||||
|
||||
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||
// Disabled by default !!!!
|
||||
name = "EscapeAnalysis",
|
||||
description = "Escape analysis",
|
||||
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
|
||||
op = { context, _ ->
|
||||
context.externalModulesDFG?.let { externalModulesDFG ->
|
||||
val callGraph = CallGraphBuilder(
|
||||
context, context.moduleDFG!!,
|
||||
externalModulesDFG,
|
||||
context.devirtualizationAnalysisResult!!,
|
||||
false
|
||||
).build()
|
||||
EscapeAnalysis.computeLifetimes(
|
||||
context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes
|
||||
)
|
||||
}
|
||||
val entryPoint = context.ir.symbols.entryPoint?.owner
|
||||
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
|
||||
val callGraph = CallGraphBuilder(
|
||||
context, context.moduleDFG!!,
|
||||
externalModulesDFG,
|
||||
context.devirtualizationAnalysisResult!!,
|
||||
// Can't tolerate any non-devirtualized call site for a library.
|
||||
// TODO: What about private virtual functions?
|
||||
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.ir.declarations.*
|
||||
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.descriptors.konan.CompiledKlibModuleOrigin
|
||||
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 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,
|
||||
val codegen: CodeGenerator,
|
||||
startLocation: LocationInfo?,
|
||||
@@ -192,10 +328,13 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
|
||||
private val prologueBb = basicBlockInFunction("prologue", startLocation)
|
||||
private val localsInitBb = basicBlockInFunction("locals_init", startLocation)
|
||||
private val stackLocalsInitBb = basicBlockInFunction("stack_locals_init", startLocation)
|
||||
private val entryBb = basicBlockInFunction("entry", startLocation)
|
||||
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
||||
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
||||
|
||||
val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb)
|
||||
|
||||
/**
|
||||
* 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,
|
||||
// along with ability to allocate in appropriate arena.
|
||||
val resultSlot = when (resultLifetime.slotType) {
|
||||
SlotType.ARENA -> {
|
||||
SlotType.STACK -> {
|
||||
localAllocs++
|
||||
// Case of local call. Use memory allocated on stack.
|
||||
val type = LLVMGetReturnType(LLVMGetElementType(llvmFunction.type))!!
|
||||
@@ -426,84 +565,29 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
fun allocInstance(typeInfo: LLVMValueRef, lifetime: Lifetime): LLVMValueRef =
|
||||
call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
||||
|
||||
fun allocInstance(irClass: IrClass, lifetime: Lifetime): LLVMValueRef {
|
||||
val typeInfo = codegen.typeInfoForAllocation(irClass)
|
||||
return if (lifetime == Lifetime.LOCAL) {
|
||||
val stackSlot = alloca(context.llvmDeclarations.forClass(irClass).bodyType)
|
||||
val objectHeader = structGep(stackSlot, 0, "objHeader")
|
||||
setTypeInfoForLocalObject(objectHeader, typeInfo)
|
||||
objectHeader
|
||||
} else {
|
||||
allocInstance(typeInfo, lifetime)
|
||||
}
|
||||
}
|
||||
fun allocInstance(irClass: IrClass, lifetime: Lifetime, stackLocalsManager: StackLocalsManager) =
|
||||
if (lifetime == Lifetime.STACK)
|
||||
stackLocalsManager.alloc(irClass,
|
||||
// In case the allocation is not from the root scope, fields must be cleaned up explicitly,
|
||||
// as the object might be being reused.
|
||||
cleanFieldsExplicitly = stackLocalsManager != this.stackLocalsManager)
|
||||
else
|
||||
allocInstance(codegen.typeInfoForAllocation(irClass), lifetime)
|
||||
|
||||
// TODO: find better place?
|
||||
val arrayToElementType = mapOf(
|
||||
"kotlin.ByteArray" to int8Type,
|
||||
"kotlin.CharArray" to int16Type,
|
||||
"kotlin.ShortArray" to int16Type,
|
||||
"kotlin.IntArray" to int32Type,
|
||||
"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 {
|
||||
fun allocArray(
|
||||
irClass: IrClass,
|
||||
count: LLVMValueRef,
|
||||
lifetime: Lifetime,
|
||||
exceptionHandler: ExceptionHandler
|
||||
): LLVMValueRef {
|
||||
val typeInfo = codegen.typeInfoValue(irClass)
|
||||
return if (lifetime == Lifetime.LOCAL) {
|
||||
val className = irClass.fqNameForIrSerialization.asString()
|
||||
assert(className in arrayToElementType)
|
||||
allocArrayOnStack(className, count, typeInfo)
|
||||
return if (lifetime == Lifetime.STACK) {
|
||||
stackLocalsManager.allocArray(irClass, count)
|
||||
} else {
|
||||
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? {
|
||||
if (context.config.debug) {
|
||||
call(context.llvm.llvmTrap, emptyList())
|
||||
@@ -1112,6 +1196,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
}
|
||||
|
||||
appendingTo(localsInitBb) {
|
||||
br(stackLocalsInitBb)
|
||||
}
|
||||
|
||||
appendingTo(stackLocalsInitBb) {
|
||||
br(entryBb)
|
||||
}
|
||||
|
||||
@@ -1293,6 +1381,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
call(context.llvm.leaveFrameFunction,
|
||||
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
|
||||
|
||||
internal sealed class SlotType {
|
||||
// An object is statically allocated on stack.
|
||||
object STACK : SlotType()
|
||||
|
||||
// Frame local arena slot can be used.
|
||||
object ARENA : SlotType()
|
||||
|
||||
@@ -58,6 +61,12 @@ internal sealed class SlotType {
|
||||
|
||||
// Lifetimes class of reference, computed by escape analysis.
|
||||
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).
|
||||
object LOCAL : Lifetime(SlotType.ARENA) {
|
||||
override fun toString(): String {
|
||||
@@ -66,7 +75,7 @@ internal sealed class Lifetime(val slotType: SlotType) {
|
||||
}
|
||||
|
||||
// If reference is only returned.
|
||||
object RETURN_VALUE : Lifetime(SlotType.RETURN) {
|
||||
object RETURN_VALUE : Lifetime(SlotType.ANONYMOUS) {
|
||||
override fun toString(): String {
|
||||
return "RETURN_VALUE"
|
||||
}
|
||||
@@ -492,8 +501,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val initInstanceFunction = importModelSpecificRtFunction("InitInstance")
|
||||
val initSharedInstanceFunction = importModelSpecificRtFunction("InitSharedInstance")
|
||||
val updateHeapRefFunction = importModelSpecificRtFunction("UpdateHeapRef")
|
||||
val releaseHeapRefFunction = importModelSpecificRtFunction("ReleaseHeapRef")
|
||||
val updateStackRefFunction = importModelSpecificRtFunction("UpdateStackRef")
|
||||
val updateReturnRefFunction = importModelSpecificRtFunction("UpdateReturnRef")
|
||||
val zeroHeapRefFunction = importRtFunction("ZeroHeapRef")
|
||||
val zeroArrayRefsFunction = importRtFunction("ZeroArrayRefs")
|
||||
val enterFrameFunction = importModelSpecificRtFunction("EnterFrame")
|
||||
val leaveFrameFunction = importModelSpecificRtFunction("LeaveFrame")
|
||||
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
|
||||
|
||||
+3
-1
@@ -103,6 +103,8 @@ internal interface IntrinsicGeneratorEnvironment {
|
||||
|
||||
val exceptionHandler: ExceptionHandler
|
||||
|
||||
val stackLocalsManager: StackLocalsManager
|
||||
|
||||
fun calculateLifetime(element: IrElement): Lifetime
|
||||
|
||||
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 enumClass = callSite.getTypeArgument(typeParameterT)!!
|
||||
val enumIrClass = enumClass.getClass()!!
|
||||
return allocInstance(enumIrClass, environment.calculateLifetime(callSite))
|
||||
return allocInstance(enumIrClass, environment.calculateLifetime(callSite), environment.stackLocalsManager)
|
||||
}
|
||||
|
||||
private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef =
|
||||
|
||||
+14
-1
@@ -134,6 +134,8 @@ internal interface CodeContext {
|
||||
|
||||
fun genThrow(exception: LLVMValueRef)
|
||||
|
||||
val stackLocalsManager: StackLocalsManager
|
||||
|
||||
/**
|
||||
* Declares the variable.
|
||||
* @return index of declared variable.
|
||||
@@ -218,6 +220,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
override val exceptionHandler: ExceptionHandler
|
||||
get() = currentCodeContext.exceptionHandler
|
||||
|
||||
override val stackLocalsManager: StackLocalsManager
|
||||
get() = currentCodeContext.stackLocalsManager
|
||||
|
||||
override fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass?) =
|
||||
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 val stackLocalsManager: StackLocalsManager get() = unsupported()
|
||||
|
||||
override fun genDeclareVariable(variable: IrVariable, value: LLVMValueRef?, variableLocation: VariableDebugLocation?) = unsupported(variable)
|
||||
|
||||
override fun getDeclaredVariable(variable: IrVariable) = -1
|
||||
@@ -529,6 +536,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
else
|
||||
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()
|
||||
}
|
||||
|
||||
override val stackLocalsManager get() = functionGenerationContext.stackLocalsManager
|
||||
|
||||
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()}")
|
||||
|
||||
else -> functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee))
|
||||
else -> functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee),
|
||||
currentCodeContext.stackLocalsManager)
|
||||
}
|
||||
evaluateSimpleFunctionCall(callee.symbol.owner,
|
||||
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
||||
|
||||
+1
-1
@@ -857,7 +857,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
||||
val coroutineSuspended = callFromBridge(
|
||||
codegen.llvmFunction(context.ir.symbols.objCExportGetCoroutineSuspended.owner),
|
||||
emptyList(),
|
||||
Lifetime.LOCAL
|
||||
Lifetime.STACK
|
||||
)
|
||||
ifThen(icmpNe(targetResult!!, coroutineSuspended)) {
|
||||
// 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.Context
|
||||
|
||||
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol)
|
||||
: DirectedGraphNode<DataFlowIR.FunctionSymbol> {
|
||||
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol.Declared)
|
||||
: DirectedGraphNode<DataFlowIR.FunctionSymbol.Declared> {
|
||||
|
||||
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
|
||||
.filter { !it.isVirtual }
|
||||
.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]!!
|
||||
}
|
||||
|
||||
@@ -31,26 +33,31 @@ internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.Functi
|
||||
val callSites = mutableListOf<CallSite>()
|
||||
}
|
||||
|
||||
internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGraphNode>,
|
||||
val reversedEdges: Map<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>)
|
||||
: DirectedGraph<DataFlowIR.FunctionSymbol, CallGraphNode> {
|
||||
internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol.Declared, CallGraphNode>,
|
||||
val reversedEdges: Map<DataFlowIR.FunctionSymbol.Declared, MutableList<DataFlowIR.FunctionSymbol.Declared>>,
|
||||
val rootExternalFunctions: List<DataFlowIR.FunctionSymbol>)
|
||||
: DirectedGraph<DataFlowIR.FunctionSymbol.Declared, CallGraphNode> {
|
||||
|
||||
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
|
||||
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,
|
||||
val moduleDFG: ModuleDFG,
|
||||
val externalModulesDFG: ExternalModulesDFG,
|
||||
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
||||
val gotoExternal: Boolean) {
|
||||
internal class CallGraphBuilder(
|
||||
val context: Context,
|
||||
val moduleDFG: ModuleDFG,
|
||||
val externalModulesDFG: ExternalModulesDFG,
|
||||
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
||||
val nonDevirtualizedCallSitesUnfoldFactor: Int
|
||||
) {
|
||||
|
||||
private val DEBUG = 0
|
||||
|
||||
@@ -66,47 +73,45 @@ internal class CallGraphBuilder(val context: Context,
|
||||
return this
|
||||
}
|
||||
|
||||
private val visitedFunctions = mutableSetOf<DataFlowIR.FunctionSymbol>()
|
||||
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol, CallGraphNode>()
|
||||
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol, MutableList<DataFlowIR.FunctionSymbol>>()
|
||||
private val callGraph = CallGraph(directEdges, reversedEdges)
|
||||
private val functionStack = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
private val directEdges = mutableMapOf<DataFlowIR.FunctionSymbol.Declared, CallGraphNode>()
|
||||
private val reversedEdges = mutableMapOf<DataFlowIR.FunctionSymbol.Declared, MutableList<DataFlowIR.FunctionSymbol.Declared>>()
|
||||
private val externalRootFunctions = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
private val callGraph = CallGraph(directEdges, reversedEdges, externalRootFunctions)
|
||||
|
||||
private data class HandleFunctionParams(val caller: DataFlowIR.FunctionSymbol.Declared?,
|
||||
val calleeFunction: DataFlowIR.Function)
|
||||
private val functionStack = mutableListOf<HandleFunctionParams>()
|
||||
|
||||
fun build(): CallGraph {
|
||||
val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG)
|
||||
@Suppress("LoopToCallChain")
|
||||
for (symbol in rootSet)
|
||||
functionStack.push(symbol)
|
||||
for (symbol in rootSet) {
|
||||
val function = moduleDFG.functions[symbol]
|
||||
if (function == null)
|
||||
externalRootFunctions.add(symbol)
|
||||
else
|
||||
functionStack.push(HandleFunctionParams(null, function))
|
||||
}
|
||||
|
||||
while (functionStack.isNotEmpty()) {
|
||||
val symbol = functionStack.pop()
|
||||
if (symbol !in visitedFunctions)
|
||||
handleFunction(symbol)
|
||||
val (caller, calleeFunction) = functionStack.pop()
|
||||
val callee = calleeFunction.symbol as DataFlowIR.FunctionSymbol.Declared
|
||||
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
|
||||
}
|
||||
|
||||
private fun addNode(symbol: DataFlowIR.FunctionSymbol) {
|
||||
if (directEdges.containsKey(symbol))
|
||||
return
|
||||
val node = CallGraphNode(callGraph, symbol)
|
||||
directEdges.put(symbol, node)
|
||||
val list = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
reversedEdges.put(symbol, list)
|
||||
private fun addNode(symbol: DataFlowIR.FunctionSymbol.Declared) {
|
||||
directEdges[symbol] = CallGraphNode(callGraph, symbol)
|
||||
reversedEdges[symbol] = mutableListOf()
|
||||
}
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
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) =
|
||||
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit): Unit =
|
||||
forEachNonScopeNode { node ->
|
||||
when (node) {
|
||||
is DataFlowIR.Node.Call -> block(node)
|
||||
@@ -137,92 +142,63 @@ internal class CallGraphBuilder(val context: Context,
|
||||
returnType = node.symbol.returnParameter.type,
|
||||
irCallSite = null
|
||||
))
|
||||
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
private fun staticCall(caller: DataFlowIR.FunctionSymbol, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
||||
callGraph.addEdge(caller, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !directEdges.containsKey(callee))
|
||||
functionStack.push(callee)
|
||||
private fun staticCall(caller: DataFlowIR.FunctionSymbol.Declared, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
||||
val resolvedCallee = callee.resolved()
|
||||
val callSite = CallGraphNode.CallSite(call, false, resolvedCallee)
|
||||
val function = moduleDFG.functions[resolvedCallee]
|
||||
callGraph.addEdge(caller, callSite)
|
||||
if (function != null)
|
||||
functionStack.push(HandleFunctionParams(caller, function))
|
||||
}
|
||||
|
||||
private fun handleFunction(symbol: DataFlowIR.FunctionSymbol) {
|
||||
visitedFunctions += symbol
|
||||
if (gotoExternal) {
|
||||
addNode(symbol)
|
||||
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] ?: return
|
||||
val body = function.body
|
||||
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())
|
||||
private fun handleFunction(symbol: DataFlowIR.FunctionSymbol.Declared, function: DataFlowIR.Function) {
|
||||
val body = function.body
|
||||
body.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] }
|
||||
when {
|
||||
call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee)
|
||||
|
||||
devirtualizedCallSite != null -> {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
staticCall(symbol, call, it.callee.resolved())
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
devirtualizedCallSite != null -> {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
staticCall(symbol, call, it.callee)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var function = moduleDFG.functions[symbol]
|
||||
var local = true
|
||||
if (function != null)
|
||||
addNode(symbol)
|
||||
else {
|
||||
function = externalModulesDFG.functionDFGs[symbol]!!
|
||||
local = false
|
||||
}
|
||||
val body = function.body
|
||||
body.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites.get(it) }
|
||||
if (devirtualizedCallSite == null) {
|
||||
val callee = call.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& call !is DataFlowIR.Node.VirtualCall
|
||||
&& !visitedFunctions.contains(callee))
|
||||
functionStack.push(callee)
|
||||
} else {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
val callee = it.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !visitedFunctions.contains(callee))
|
||||
functionStack.push(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
|
||||
val allPossibleCallees = mutableListOf<DataFlowIR.FunctionSymbol>()
|
||||
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")
|
||||
}
|
||||
allPossibleCallees.add(actualCallee)
|
||||
}
|
||||
if (allPossibleCallees.size <= nonDevirtualizedCallSitesUnfoldFactor)
|
||||
allPossibleCallees.forEach { staticCall(symbol, call, it) }
|
||||
else {
|
||||
val callSite = CallGraphNode.CallSite(call, true, call.callee)
|
||||
callGraph.addEdge(symbol, callSite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ private class VariableValues {
|
||||
}
|
||||
|
||||
fun add(variable: IrVariable, element: IrExpression) =
|
||||
elementData[variable]!!.values.add(element)
|
||||
elementData[variable]?.values?.add(element)
|
||||
|
||||
private fun add(variable: IrVariable, elements: Set<IrExpression>) =
|
||||
elementData[variable]?.values?.addAll(elements)
|
||||
@@ -736,7 +736,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
createUninitializedInstanceSymbol ->
|
||||
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
|
||||
value.getTypeArgument(0)!!.getClass()!!
|
||||
))
|
||||
), 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 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()
|
||||
|
||||
@@ -647,10 +647,15 @@ internal object DataFlowIR {
|
||||
&& !irClass.isNonGeneratedAnnotation()
|
||||
&& (it.isOverridableOrOverrides || bridgeTarget != null || function.isSpecial || !irClass.isFinal())
|
||||
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 })
|
||||
else
|
||||
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
|
||||
|
||||
+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.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isBuiltInOperator
|
||||
|
||||
internal object LocalEscapeAnalysis {
|
||||
private val DEBUG = 0
|
||||
@@ -187,7 +186,7 @@ internal object LocalEscapeAnalysis {
|
||||
else -> null
|
||||
}
|
||||
ir?.let {
|
||||
lifetimes.put(it, Lifetime.LOCAL)
|
||||
lifetimes.put(it, Lifetime.STACK)
|
||||
DEBUG_OUTPUT(3) { println("${ir} does not escape") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3252,6 +3252,13 @@ void UpdateReturnRefRelaxed(ObjHeader** returnSlot, const ObjHeader* 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) {
|
||||
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.
|
||||
// If object is frozen or permanent, an exception is thrown.
|
||||
void MutationCheck(ObjHeader* obj) {
|
||||
if (obj->local()) return;
|
||||
auto* container = obj->container();
|
||||
if (container == nullptr || container->frozen())
|
||||
ThrowInvalidMutabilityException(obj);
|
||||
|
||||
@@ -501,7 +501,9 @@ MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object)
|
||||
// Sets heap location.
|
||||
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
||||
// 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.
|
||||
MODEL_VARIANTS(void, ZeroStackRef, ObjHeader** 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].
|
||||
*/
|
||||
@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
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ public final class Array<T> {
|
||||
* If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException].
|
||||
*/
|
||||
@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
|
||||
|
||||
/**
|
||||
|
||||
@@ -528,7 +528,7 @@ public inline fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
|
||||
* Returns an array containing the specified elements.
|
||||
*/
|
||||
@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>
|
||||
|
||||
@SymbolName("Kotlin_emptyArray")
|
||||
|
||||
@@ -14,7 +14,7 @@ import kotlin.native.internal.ExportForCppRuntime
|
||||
* either throwing exception or returning some kind of implementation-specific default value.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
internal inline fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
// TODO: special case for size == 0?
|
||||
require(size >= 0) { "capacity must be non-negative." }
|
||||
@Suppress("TYPE_PARAMETER_AS_REIFIED")
|
||||
@@ -76,7 +76,7 @@ internal fun <E> Array<E>.resetAt(index: Int) {
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
@SymbolName("Kotlin_ByteArray_fillImpl")
|
||||
@@ -125,7 +125,7 @@ internal fun <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
@SymbolName("Kotlin_ByteArray_copyImpl")
|
||||
|
||||
@@ -6,6 +6,7 @@ package kotlin.native
|
||||
|
||||
import kotlin.native.concurrent.isFrozen
|
||||
import kotlin.native.concurrent.InvalidMutabilityException
|
||||
import kotlin.native.internal.Escapes
|
||||
|
||||
/**
|
||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||
@@ -51,6 +52,7 @@ public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): Report
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
||||
@Escapes(0b01) // <hook> escapes
|
||||
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,6 +90,7 @@ internal annotation class FixmeConcurrency
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
internal annotation class Escapes(val who: Int)
|
||||
|
||||
// Decyphering of binary values can be found in EscapeAnalysis.kt
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
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.Frozen
|
||||
import kotlin.native.internal.NoReorderFields
|
||||
import kotlin.native.internal.Escapes
|
||||
|
||||
/**
|
||||
* Theory of operations:
|
||||
@@ -53,6 +54,7 @@ internal abstract class WeakReferenceImpl {
|
||||
|
||||
// Get a counter from non-null object.
|
||||
@SymbolName("Konan_getWeakReferenceImpl")
|
||||
@Escapes(0b01) // referent escapes.
|
||||
external internal fun getWeakReferenceImpl(referent: Any): WeakReferenceImpl
|
||||
|
||||
// Create a counter object.
|
||||
|
||||
Reference in New Issue
Block a user