temp
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 50f55ee..59289ba 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt
@@ -478,6 +478,7 @@ var devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult? = null var referencedFunctions: Set<IrFunction>? = null + var smallFunctions: Set<IrFunction>? = null val isNativeLibrary: Boolean by lazy { val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 82c9886..3f4beee 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt
@@ -307,6 +307,7 @@ EscapeAnalysis.computeLifetimes( context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes ) + SmallFunctionAnalysis.findSmallFunction(context, callGraph) } )
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index f11737c..ee88d1e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt
@@ -481,6 +481,16 @@ (!stackLocalsManager.isEmpty() && context.memoryModel != MemoryModel.EXPERIMENTAL) || switchToRunnable + protected val canOmmitFrame: Boolean + get() = context.memoryModel == MemoryModel.EXPERIMENTAL && + !context.shouldContainDebugInfo() && + irFunction != null && + context.smallFunctions?.contains(irFunction) == true + + + protected val needFrame: Boolean + get() = (needCleanupLandingpadAndLeaveFrame || needSlots) && !canOmmitFrame + private var setCurrentFrameIsCalled: Boolean = false private val switchToRunnable: Boolean = @@ -1433,11 +1443,11 @@ val needCleanupLandingpadAndLeaveFrame = this.needCleanupLandingpadAndLeaveFrame appendingTo(prologueBb) { - val slots = if (needSlotsPhi || needCleanupLandingpadAndLeaveFrame) + val slots = if (needSlotsPhi || needFrame) LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!! else kNullObjHeaderPtrPtr - if (needSlots || needCleanupLandingpadAndLeaveFrame) { + if (needFrame) { check(!forbidRuntime) { "Attempt to start a frame where runtime usage is forbidden" } // Zero-init slots. val slotsMem = bitcast(kInt8Ptr, slots) @@ -1518,12 +1528,12 @@ if (switchToRunnable) { switchThreadState(Runnable) } - if (needSlots || needCleanupLandingpadAndLeaveFrame) { + if (needFrame) { call(context.llvm.enterFrameFunction, listOf(slotsPhi!!, Int32(vars.skipSlots).llvm, Int32(slotCount).llvm)) } else { check(!setCurrentFrameIsCalled) } - if (context.memoryModel == MemoryModel.EXPERIMENTAL && !forbidRuntime) { + if (!canOmmitFrame && context.memoryModel == MemoryModel.EXPERIMENTAL && !forbidRuntime) { call(context.llvm.Kotlin_mm_safePointFunctionPrologue, emptyList()) } resetDebugLocation() @@ -1713,7 +1723,7 @@ } private fun releaseVars() { - if (needCleanupLandingpadAndLeaveFrame || needSlots) { + if (needFrame) { check(!forbidRuntime) { "Attempt to leave a frame where runtime usage is forbidden" } call(context.llvm.leaveFrameFunction, listOf(slotsPhi!!, Int32(vars.skipSlots).llvm, Int32(slotCount).llvm))
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/SmallFunctionAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/SmallFunctionAnalysis.kt new file mode 100644 index 0000000..7c85cdc --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/SmallFunctionAnalysis.kt
@@ -0,0 +1,102 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.optimizations + +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.visitors.* + +internal object SmallFunctionAnalysis { + fun findSmallFunction(context: Context, callGraph: CallGraph) { + context.inVerbosePhase = true + val condensation = DirectedGraphCondensationBuilder(callGraph).build() + val smallFunctions = mutableSetOf<DataFlowIR.FunctionSymbol.Declared>() + val predefined = listOf( + context.irBuiltIns.anyClass.owner.constructors.single(), + context.irBuiltIns.unitClass.owner.constructors.single() + ) + smallFunctions.addAll(callGraph.directEdges.keys.filter { it.irFunction in predefined }) + var externalFunctions = 0 + var functionsWithLoops = 0 + var functionsWithAllocations = 0 + var functionsWithRecursiveCall = 0 + var functionsWithOtherNonSmallCall = 0 + var functionsWithVirtualCall = 0 + for (multiNode in condensation.topologicalOrder.reversed()) { + val function = multiNode.nodes.singleOrNull() + if (function == null || callGraph.directEdges[function]!!.callSites.any { it.actualCallee == function }) { + functionsWithRecursiveCall += multiNode.nodes.size + context.log { "Functions ${multiNode.nodes.joinToString { it.name!! }} are not small, as they are recursive" } + continue + } + val irFunction = function.irFunction + if (irFunction == null || irFunction.isExternal || irFunction.annotations.hasAnnotation(RuntimeNames.exportForCppRuntime)) { + externalFunctions++ + context.log { "${function.name} is not small, as it is external" } + continue + } + val loops = mutableListOf<IrLoop>() + val constructorCalls = mutableListOf<IrConstructorCall>() + irFunction.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + return element.acceptChildrenVoid(this) + } + + override fun visitLoop(loop: IrLoop) { + loops.add(loop) + } + + override fun visitConstructorCall(expression: IrConstructorCall) { + constructorCalls.add(expression) + } + }) + if (loops.isNotEmpty()) { + functionsWithLoops++ + context.log { "${function.name} is not small, as it contain ${loops.size} loops" } + continue + } + if (constructorCalls.isNotEmpty()) { + functionsWithAllocations++ + context.log { "${function.name} is not small, as it allocates" } + continue + } + val callSites = callGraph.directEdges[function]!!.callSites + val virtualCall = callSites.firstOrNull { it.isVirtual } + if (virtualCall != null) { + functionsWithVirtualCall++ + context.log { "${function.name} is not small, as it has virtual call ${virtualCall.actualCallee.name}" } + continue + } + val badCall = callSites.map { it.actualCallee }.firstOrNull { it !in smallFunctions || it.irFunction?.isExternal != false } + if (badCall != null) { + functionsWithOtherNonSmallCall++ + context.log { "${function.name} is not small, as it calls ${badCall.name}, which is not small" } + continue + } + context.logMultiple { + +"${function.name} is small!" + +function.irFunction!!.dump() + } + smallFunctions.add(function) + } + context.smallFunctions = smallFunctions.map { it.irFunction!! }.toSet() + context.logMultiple { + +"Small function analysis stats:" + +" ${smallFunctions.size} functions are small" + +" $externalFunctions functions are not small as external" + +" $functionsWithLoops functions are not small as they have loops inside" + +" $functionsWithAllocations functions are not small as they have allocations inside" + +" $functionsWithVirtualCall functions are not small as they have virtual call inside" + +" $functionsWithOtherNonSmallCall functions are not small as they have call of other non-small functions inside" + } + context.inVerbosePhase = true + } +} \ No newline at end of file