From 5bc03a45a77a20d50bd4a825fe426fc40e6af93e Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Sat, 12 Sep 2020 09:35:15 +0500 Subject: [PATCH] [IR] Rewrote debug output using Context.log --- .../jetbrains/kotlin/backend/konan/Context.kt | 12 +- .../konan/descriptors/ClassLayoutBuilder.kt | 55 ++-- .../konan/optimizations/CallGraphBuilder.kt | 6 - .../backend/konan/optimizations/DFGBuilder.kt | 84 +++--- .../backend/konan/optimizations/DataFlowIR.kt | 236 +++++++-------- .../konan/optimizations/Devirtualization.kt | 183 ++++++------ .../konan/optimizations/EscapeAnalysis.kt | 268 +++++++----------- .../optimizations/LocalEscapeAnalysis.kt | 21 +- 8 files changed, 358 insertions(+), 507 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 4f9b8eb8690..04f68af0734 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -43,9 +43,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.defaultType -import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.konan.library.KonanLibraryLayout import org.jetbrains.kotlin.library.SerializedIrModule @@ -429,3 +426,12 @@ private fun MemberScope.getContributedClassifier(name: String) = private fun MemberScope.getContributedFunctions(name: String) = this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) + +internal class ContextLogger(val context: Context) { + operator fun String.unaryPlus() = context.log { this } +} + +internal fun Context.logMultiple(messageBuilder: ContextLogger.() -> Unit) { + if (!inVerbosePhase) return + with(ContextLogger(this)) { messageBuilder() } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index f3db758996a..7431e56aae7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -262,19 +262,12 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod } internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, val isLowered: Boolean) { - private val DEBUG = 0 - - private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { - if (DEBUG > severity) block() - } - val vtableEntries: List by lazy { - assert(!irClass.isInterface) - DEBUG_OUTPUT(0) { - println() - println("BUILDING vTable for ${irClass.render()}") + context.logMultiple { + +"" + +"BUILDING vTable for ${irClass.render()}" } val superVtableEntries = if (irClass.isSpecialClassWithNoSupertypes()) { @@ -288,17 +281,17 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va val newVtableSlots = mutableListOf() val overridenVtableSlots = mutableMapOf() - DEBUG_OUTPUT(0) { - println() - println("SUPER vTable:") - superVtableEntries.forEach { println(" ${it.overriddenFunction.render()} -> ${it.function.render()}") } + context.logMultiple { + +"" + +"SUPER vTable:" + superVtableEntries.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()}" } - println() - println("METHODS:") - methods.forEach { println(" ${it.render()}") } + +"" + +"METHODS:" + methods.forEach { +" ${it.render()}" } - println() - println("BUILDING INHERITED vTable") + +"" + +"BUILDING INHERITED vTable" } val superVtableMap = superVtableEntries.groupBy { it.function } @@ -316,13 +309,9 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va } val inheritedVtableSlots = superVtableEntries.map { superMethod -> overridenVtableSlots[superMethod.overriddenFunction]?.also { - DEBUG_OUTPUT(0) { - println("Taking overridden ${superMethod.overriddenFunction.render()} -> ${it.function.render()}") - } + context.log { "Taking overridden ${superMethod.overriddenFunction.render()} -> ${it.function.render()}" } } ?: superMethod.also { - DEBUG_OUTPUT(0) { - println("Taking super ${superMethod.overriddenFunction.render()} -> ${superMethod.function.render()}") - } + context.log { "Taking super ${superMethod.overriddenFunction.render()} -> ${superMethod.function.render()}" } } } @@ -336,15 +325,15 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va .distinctBy { it.function to it.bridgeDirections } .filter { it.function.isOverridable } - DEBUG_OUTPUT(0) { - println() - println("INHERITED vTable slots:") - inheritedVtableSlots.forEach { println(" ${it.overriddenFunction.render()} -> ${it.function.render()}") } + context.logMultiple { + +"" + +"INHERITED vTable slots:" + inheritedVtableSlots.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()}" } - println() - println("MY OWN vTable slots:") - filteredNewVtableSlots.forEach { println(" ${it.overriddenFunction.render()} -> ${it.function.render()} ${it.function}") } - println("DONE vTable for ${irClass.render()}") + +"" + +"MY OWN vTable slots:" + filteredNewVtableSlots.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()} ${it.function}" } + +"DONE vTable for ${irClass.render()}" } inheritedVtableSlots + filteredNewVtableSlots.sortedBy { it.overriddenFunction.uniqueId } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt index 1d22117b390..2c8485ff1d8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt @@ -59,12 +59,6 @@ internal class CallGraphBuilder( val nonDevirtualizedCallSitesUnfoldFactor: Int ) { - private val DEBUG = 0 - - private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { - if (DEBUG > severity) block() - } - private val devirtualizedCallSites = devirtualizationAnalysisResult.devirtualizedCallSites private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index bdf267c3c75..04f70e38fa0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions -import org.jetbrains.kotlin.backend.konan.descriptors.target import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.localHash @@ -177,12 +176,6 @@ internal class ModuleDFG(val functions: Map Unit) { - if (DEBUG > severity) block() - } - private val TAKE_NAMES = true // Take fqNames for all functions and types (for debug purposes). private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null @@ -211,9 +204,9 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag assert (body != null || declaration.constructedClass.isNonGeneratedAnnotation()) { "Non-annotation class constructor has empty body" } - DEBUG_OUTPUT(0) { - println("Analysing function ${declaration.descriptor}") - println("IR: ${ir2stringWhole(declaration)}") + context.logMultiple { + +"Analysing function ${declaration.descriptor}" + +"IR: ${ir2stringWhole(declaration)}" } analyze(declaration, body) } @@ -224,9 +217,9 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag // External function or intrinsic. symbolTable.mapFunction(declaration) } else { - DEBUG_OUTPUT(0) { - println("Analysing function ${declaration.descriptor}") - println("IR: ${ir2stringWhole(declaration)}") + context.logMultiple { + +"Analysing function ${declaration.descriptor}" + +"IR: ${ir2stringWhole(declaration)}" } analyze(declaration, body) } @@ -235,9 +228,9 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag override fun visitField(declaration: IrField) { if (declaration.parent is IrFile) declaration.initializer?.let { - DEBUG_OUTPUT(0) { - println("Analysing global field ${declaration.descriptor}") - println("IR: ${ir2stringWhole(declaration)}") + context.logMultiple { + +"Analysing global field ${declaration.descriptor}" + +"IR: ${ir2stringWhole(declaration)}" } analyze(declaration, IrSetFieldImpl(it.startOffset, it.endOffset, declaration.symbol, null, it.expression, context.irBuiltIns.unitType)) @@ -249,29 +242,25 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag val visitor = ElementFinderVisitor() body?.acceptVoid(visitor) - DEBUG_OUTPUT(0) { - println("FIRST PHASE") - visitor.variableValues.elementData.forEach { t, u -> - println("VAR $t [LOOP ${u.loop}]:") - u.values.forEach { - println(" ${ir2stringWhole(it)}") - } + context.logMultiple { + +"FIRST PHASE" + visitor.variableValues.elementData.forEach { (t, u) -> + +"VAR $t [LOOP ${u.loop}]:" + u.values.forEach { +" ${ir2stringWhole(it)}" } } visitor.expressions.forEach { t -> - println("EXP [LOOP ${t.value}] ${ir2stringWhole(t.key)}") + +"EXP [LOOP ${t.value}] ${ir2stringWhole(t.key)}" } } // Compute transitive closure of possible values for variables. visitor.variableValues.computeClosure() - DEBUG_OUTPUT(0) { - println("SECOND PHASE") - visitor.variableValues.elementData.forEach { t, u -> - println("VAR $t [LOOP ${u.loop}]:") - u.values.forEach { - println(" ${ir2stringWhole(it)}") - } + context.logMultiple { + +"SECOND PHASE" + visitor.variableValues.elementData.forEach { (t, u) -> + +"VAR $t [LOOP ${u.loop}]:" + u.values.forEach { +" ${ir2stringWhole(it)}" } } } @@ -279,27 +268,28 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag declaration, visitor.expressions, visitor.parentLoops, visitor.returnValues, visitor.thrownValues, visitor.catchParameters).build() - DEBUG_OUTPUT(0) { - function.debugOutput() + context.logMultiple { + +function.debugString() + +"" } - functions.put(function.symbol, function) + functions[function.symbol] = function } }, data = null) - DEBUG_OUTPUT(1) { - println("SYMBOL TABLE:") + context.logMultiple { + +"SYMBOL TABLE:" symbolTable.classMap.forEach { irClass, type -> - println(" DESCRIPTOR: ${irClass.descriptor}") - println(" TYPE: $type") + +" DESCRIPTOR: ${irClass.descriptor}" + +" TYPE: $type" if (type !is DataFlowIR.Type.Declared) return@forEach - println(" SUPER TYPES:") - type.superTypes.forEach { println(" $it") } - println(" VTABLE:") - type.vtable.forEach { println(" $it") } - println(" ITABLE:") - type.itable.forEach { println(" ${it.key} -> ${it.value}") } + +" SUPER TYPES:" + type.superTypes.forEach { +" $it" } + +" VTABLE:" + type.vtable.forEach { +" $it" } + +" ITABLE:" + type.itable.forEach { +" ${it.key} -> ${it.value}" } } } @@ -618,9 +608,9 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag return variables[valueDeclaration]!! } return nodes.getOrPut(expression) { - DEBUG_OUTPUT(0) { - println("Converting expression") - println(ir2stringWhole(expression)) + context.logMultiple { + +"Converting expression" + +ir2stringWhole(expression) } val values = mutableListOf() val edges = mutableListOf() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index ec29cee2f3f..aaaecc99a81 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.konan.optimizations import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isBuiltInOperator -import org.jetbrains.kotlin.backend.konan.descriptors.target import org.jetbrains.kotlin.backend.konan.ir.allParameters import org.jetbrains.kotlin.backend.konan.ir.isOverridableOrOverrides import org.jetbrains.kotlin.backend.konan.llvm.functionName @@ -17,6 +16,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.backend.konan.llvm.symbolName import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget +import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR.Function.Companion.appendCastTo import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.IrElement @@ -285,17 +285,19 @@ internal object DataFlowIR { class Function(val symbol: FunctionSymbol, val body: FunctionBody) { - fun debugOutput() { - println("FUNCTION $symbol") - println("Params: ${symbol.parameters.contentToString()}") + fun debugOutput() = println(debugString()) + + fun debugString() = buildString { + appendLine("FUNCTION $symbol") + appendLine("Params: ${symbol.parameters.contentToString()}") val nodes = listOf(body.rootScope) + body.allScopes.flatMap { it.nodes } val ids = nodes.withIndex().associateBy({ it.value }, { it.index }) nodes.forEach { - println(" NODE #${ids[it]!!}") - printNode(it, ids) + appendLine(" NODE #${ids[it]!!}") + appendLine(nodeToString(it, ids)) } - println(" RETURNS") - printNode(body.returns, ids) + appendLine(" RETURNS") + append(nodeToString(body.returns, ids)) } companion object { @@ -303,169 +305,125 @@ internal object DataFlowIR { fun nodeToString(node: Node, ids: Map) = when (node) { is Node.Const -> - " CONST ${node.type}\n" + " CONST ${node.type}" Node.Null -> - " NULL\n" + " NULL" is Node.Parameter -> - " PARAM ${node.index}\n" + " PARAM ${node.index}" is Node.Singleton -> - " SINGLETON ${node.type}\n" + " SINGLETON ${node.type}" is Node.AllocInstance -> - " ALLOC INSTANCE ${node.type}\n" + " ALLOC INSTANCE ${node.type}" is Node.FunctionReference -> - " FUNCTION REFERENCE ${node.symbol}\n" + " FUNCTION REFERENCE ${node.symbol}" - is Node.StaticCall -> { - buildString { - appendLine(" STATIC CALL ${node.callee}. Return type = ${node.returnType}") - node.arguments.forEach { - append(" ARG #${ids[it.node]!!}") - if (it.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${it.castToType}") - } + is Node.StaticCall -> buildString { + append(" STATIC CALL ${node.callee}. Return type = ${node.returnType}") + appendList(node.arguments) { + append(" ARG #${ids[it.node]!!}") + appendCastTo(it.castToType) } } - is Node.VtableCall -> { - buildString { - appendLine(" VIRTUAL CALL ${node.callee}. Return type = ${node.returnType}") - appendLine(" RECEIVER: ${node.receiverType}") - appendLine(" VTABLE INDEX: ${node.calleeVtableIndex}") - node.arguments.forEach { - append(" ARG #${ids[it.node]!!}") - if (it.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${it.castToType}") - } + is Node.VtableCall -> buildString { + appendLine(" VIRTUAL CALL ${node.callee}. Return type = ${node.returnType}") + appendLine(" RECEIVER: ${node.receiverType}") + append(" VTABLE INDEX: ${node.calleeVtableIndex}") + appendList(node.arguments) { + append(" ARG #${ids[it.node]!!}") + appendCastTo(it.castToType) } } - is Node.ItableCall -> { - buildString { - appendLine(" INTERFACE CALL ${node.callee}. Return type = ${node.returnType}") - appendLine(" RECEIVER: ${node.receiverType}") - appendLine(" METHOD HASH: ${node.calleeHash}") - node.arguments.forEach { - append(" ARG #${ids[it.node]!!}") - if (it.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${it.castToType}") - } + is Node.ItableCall -> buildString { + appendLine(" INTERFACE CALL ${node.callee}. Return type = ${node.returnType}") + appendLine(" RECEIVER: ${node.receiverType}") + append(" METHOD HASH: ${node.calleeHash}") + appendList(node.arguments) { + append(" ARG #${ids[it.node]!!}") + appendCastTo(it.castToType) } } - is Node.NewObject -> { - buildString { - appendLine(" NEW OBJECT ${node.callee}") - appendLine(" CONSTRUCTED TYPE ${node.constructedType}") - node.arguments.forEach { - append(" ARG #${ids[it.node]!!}") - if (it.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${it.castToType}") - } + is Node.NewObject -> buildString { + appendLine(" NEW OBJECT ${node.callee}") + append(" CONSTRUCTED TYPE ${node.constructedType}") + appendList(node.arguments) { + append(" ARG #${ids[it.node]!!}") + appendCastTo(it.castToType) } } - is Node.FieldRead -> { - buildString { - appendLine(" FIELD READ ${node.field}") - append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}") - if (node.receiver?.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.receiver.castToType}") + is Node.FieldRead -> buildString { + appendLine(" FIELD READ ${node.field}") + append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}") + appendCastTo(node.receiver?.castToType) + } + + is Node.FieldWrite -> buildString { + appendLine(" FIELD WRITE ${node.field}") + append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}") + appendCastTo(node.receiver?.castToType) + appendLine() + append(" VALUE #${ids[node.value.node]!!}") + appendCastTo(node.value.castToType) + } + + is Node.ArrayRead -> buildString { + appendLine(" ARRAY READ") + append(" ARRAY #${ids[node.array.node]}") + appendCastTo(node.array.castToType) + appendLine() + append(" INDEX #${ids[node.index.node]!!}") + appendCastTo(node.index.castToType) + } + + is Node.ArrayWrite -> buildString { + appendLine(" ARRAY WRITE") + append(" ARRAY #${ids[node.array.node]}") + appendCastTo(node.array.castToType) + appendLine() + append(" INDEX #${ids[node.index.node]!!}") + appendCastTo(node.index.castToType) + appendLine() + append(" VALUE #${ids[node.value.node]!!}") + appendCastTo(node.value.castToType) + } + + is Node.Variable -> buildString { + append(" ${node.kind}") + appendList(node.values) { + append(" VAL #${ids[it.node]!!}") + appendCastTo(it.castToType) } } - is Node.FieldWrite -> { - buildString { - appendLine(" FIELD WRITE ${node.field}") - append(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}") - if (node.receiver?.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.receiver.castToType}") - print(" VALUE #${ids[node.value.node]!!}") - if (node.value.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.value.castToType}") + is Node.Scope -> buildString { + append(" SCOPE ${node.depth}") + appendList(node.nodes.toList()) { + append(" SUBNODE #${ids[it]!!}") } } - is Node.ArrayRead -> { - buildString { - appendLine(" ARRAY READ") - append(" ARRAY #${ids[node.array.node]}") - if (node.array.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.array.castToType}") - append(" INDEX #${ids[node.index.node]!!}") - if (node.index.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.index.castToType}") - } - } + else -> " UNKNOWN: ${node::class.java}" + } - is Node.ArrayWrite -> { - buildString { - appendLine(" ARRAY WRITE") - append(" ARRAY #${ids[node.array.node]}") - if (node.array.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.array.castToType}") - append(" INDEX #${ids[node.index.node]!!}") - if (node.index.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.index.castToType}") - print(" VALUE #${ids[node.value.node]!!}") - if (node.value.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${node.value.castToType}") - } + private fun StringBuilder.appendList(list: List, itemPrinter: StringBuilder.(T) -> Unit) { + if (list.isEmpty()) return + for (i in list.indices) { + appendLine() + itemPrinter(list[i]) } + } - is Node.Variable -> { - buildString { - appendLine(" ${node.kind}") - node.values.forEach { - append(" VAL #${ids[it.node]!!}") - if (it.castToType == null) - appendLine() - else - appendLine(" CASTED TO ${it.castToType}") - } - } - } - - is Node.Scope -> { - val result = StringBuilder() - result.appendLine(" SCOPE ${node.depth}") - node.nodes.forEach { - result.appendLine(" SUBNODE #${ids[it]!!}") - } - result.toString() - } - - else -> { - " UNKNOWN: ${node::class.java}\n" - } + private fun StringBuilder.appendCastTo(type: Type?) { + if (type != null) + append(" CASTED TO ${type}") } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt index 3b66e662627..9274ffc0917 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt @@ -54,12 +54,6 @@ inline fun BitSet.forEachBit(block: (Int) -> Unit) { // See http://web.cs.ucla.edu/~palsberg/tba/papers/sundaresan-et-al-oopsla00.pdf for details. internal object Devirtualization { - private val DEBUG = 0 - - private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { - if (DEBUG > severity) block() - } - private val TAKE_NAMES = false // Take fqNames for all functions and types (for debug purposes). private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null @@ -276,7 +270,7 @@ internal object Devirtualization { fun BitSet.copy() = BitSet(this.size()).apply { this.or(this@copy) } - fun printPathToType(reversedEdges: IntArray, node: Node, type: Int) { + fun logPathToType(reversedEdges: IntArray, node: Node, type: Int) { val nodes = constraintGraph.nodes val visited = BitSet() val prev = mutableMapOf() @@ -322,11 +316,11 @@ internal object Devirtualization { try { var cur: Node = source do { - println(" #${cur.id}") + context.log { " #${cur.id}" } cur = prev[cur]!! } while (cur != node) } catch (t: Throwable) { - println("Unable to print path") + context.log { "Unable to print path" } } } @@ -416,6 +410,9 @@ internal object Devirtualization { } } + private fun DataFlowIR.Node.VirtualCall.debugString() = + irCallSite?.let { ir2stringWhole(it).trimEnd() } ?: this.toString() + fun analyze(): AnalysisResult { val functions = moduleDFG.functions + externalModulesDFG.functionDFGs assert(DataFlowIR.Type.Virtual !in symbolTable.classMap.values) { @@ -435,21 +432,20 @@ internal object Devirtualization { val (instantiatingClasses, directEdges, reversedEdges) = buildConstraintGraph(nodesMap, functions, typeHierarchy, rootSet) - DEBUG_OUTPUT(0) { - println("FULL CONSTRAINT GRAPH") + context.logMultiple { + +"FULL CONSTRAINT GRAPH" constraintGraph.nodes.forEach { - println(" NODE #${it.id}") - directEdges.forEachEdge(it.id) { - println(" EDGE: #${it}z") - } + +" NODE #${it.id}" + directEdges.forEachEdge(it.id) { +" EDGE: #${it}z" } it.directCastEdges?.forEach { - println(" CAST EDGE: #${it.node.id}z casted to ${it.suitableTypes.format(allTypes)}") + +" CAST EDGE: #${it.node.id}z casted to ${it.suitableTypes.format(allTypes)}" } allTypes.forEachIndexed { index, type -> if (it.types[index]) - println(" TYPE: $type") + +" TYPE: $type" } } + +"" } constraintGraph.nodes.forEach { @@ -459,22 +455,24 @@ internal object Devirtualization { } } - DEBUG_OUTPUT(0) { - println("CONSTRAINT GRAPH: ${constraintGraph.nodes.size} nodes, " + - "${constraintGraph.nodes.sumBy { (directEdges[it.id + 1] - directEdges[it.id]) + (it.directCastEdges?.size ?: 0) } } edges") + context.logMultiple { + val edgesCount = constraintGraph.nodes.sumBy { + (directEdges[it.id + 1] - directEdges[it.id]) + (it.directCastEdges?.size ?: 0) + } + +"CONSTRAINT GRAPH: ${constraintGraph.nodes.size} nodes, $edgesCount edges" + +"" } val condensation = CondensationBuilder(directEdges, reversedEdges).build() val topologicalOrder = condensation.topologicalOrder.map { constraintGraph.nodes[it] } - DEBUG_OUTPUT(0) { - println("CONDENSATION") + context.logMultiple { + +"CONDENSATION" topologicalOrder.forEachIndexed { index, multiNode -> - println(" MULTI-NODE #$index") - condensation.forEachNode(multiNode) { - println(" #${it.id}: ${it.toString(allTypes)}") - } + +" MULTI-NODE #$index" + condensation.forEachNode(multiNode) { +" #${it.id}: ${it.toString(allTypes)}" } } + +"" } topologicalOrder.forEachIndexed { index, multiNode -> @@ -587,17 +585,18 @@ internal object Devirtualization { } } - DEBUG_OUTPUT(0) { + context.logMultiple { topologicalOrder.forEachIndexed { index, multiNode -> - println("Types of multi-node #$index") + +"Types of multi-node #$index" condensation.forEachNode(multiNode) { node -> - println(" Node #${node.id}") + +" Node #${node.id}" allTypes.asSequence() .withIndex() .filter { node.types[it.index] }.toList() - .forEach { println(" ${it.value}") } + .forEach { +" ${it.value}" } } } + +"" } val result = mutableMapOf>() @@ -610,76 +609,66 @@ internal object Devirtualization { val receiverNode = constraintGraph.virtualCallSiteReceivers[virtualCall] ?: error("virtualCallSiteReceivers were not built for virtual call $virtualCall") if (receiverNode.types[VIRTUAL_TYPE_ID]) { - - DEBUG_OUTPUT(0) { - println("Unable to devirtualize callsite " + - (virtualCall.irCallSite?.let { ir2stringWhole(it) } - ?: virtualCall.callee.toString())) - println(" receiver is Virtual") - printPathToType(reversedEdges, receiverNode, VIRTUAL_TYPE_ID) + context.logMultiple { + +"Unable to devirtualize callsite ${virtualCall.debugString()}" + +" receiver is Virtual" + logPathToType(reversedEdges, receiverNode, VIRTUAL_TYPE_ID) + +"" } - return@forEachNonScopeNode } - DEBUG_OUTPUT(0) { - println("Devirtualized callsite " + - (virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.callee.toString())) - } - + context.log { "Devirtualized callsite ${virtualCall.debugString()}" } val receiverType = virtualCall.receiverType.resolved() val possibleReceivers = mutableListOf() forEachBitInBoth(receiverNode.types, typeHierarchy.inheritorsOf(receiverType)) { val type = allTypes[it] assert(instantiatingClasses[it]) { "Non-instantiating class $type" } if (type != nothing) { - - DEBUG_OUTPUT(0) { - println("Path to type $type") - printPathToType(reversedEdges, receiverNode, it) + context.logMultiple { + +"Path to type $type" + logPathToType(reversedEdges, receiverNode, it) } - possibleReceivers.add(type) } } + context.log { "" } result[virtualCall] = DevirtualizedCallSite(virtualCall.callee.resolved(), possibleReceivers.map { possibleReceiverType -> val callee = possibleReceiverType.calleeAt(virtualCall) if (callee is DataFlowIR.FunctionSymbol.Declared && callee.symbolTableIndex < 0) error("Function ${possibleReceiverType}.$callee cannot be called virtually," + - " but actually is at call site: " + - (virtualCall.irCallSite?.let { ir2stringWhole(it) } - ?: virtualCall.toString())) + " but actually is at call site: ${virtualCall.debugString()}") DevirtualizedCallee(possibleReceiverType, callee) }) to function.symbol } } - DEBUG_OUTPUT(0) { - println("Devirtualized from current module:") + context.logMultiple { + +"Devirtualized from current module:" result.forEach { virtualCall, devirtualizedCallSite -> if (virtualCall.irCallSite != null) { - println("DEVIRTUALIZED") - println("FUNCTION: ${devirtualizedCallSite.second}") - println("CALL SITE: ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}") - println("POSSIBLE RECEIVERS:") - devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") } - devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") } - println() + +"DEVIRTUALIZED" + +"FUNCTION: ${devirtualizedCallSite.second}" + +"CALL SITE: ${virtualCall.debugString()}" + +"POSSIBLE RECEIVERS:" + devirtualizedCallSite.first.possibleCallees.forEach { +" TYPE: ${it.receiverType}" } + devirtualizedCallSite.first.possibleCallees.forEach { +" FUN: ${it.callee}" } + +"" } } - println("Devirtualized from external modules:") + +"Devirtualized from external modules:" result.forEach { virtualCall, devirtualizedCallSite -> if (virtualCall.irCallSite == null) { - println("DEVIRTUALIZED") - println("FUNCTION: ${devirtualizedCallSite.second}") - println("CALL SITE: ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}") - println("POSSIBLE RECEIVERS:") - devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") } - devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") } - println() + +"DEVIRTUALIZED" + +"FUNCTION: ${devirtualizedCallSite.second}" + +"CALL SITE: ${virtualCall.debugString()}" + +"POSSIBLE RECEIVERS:" + devirtualizedCallSite.first.possibleCallees.forEach { +" TYPE: ${it.receiverType}" } + devirtualizedCallSite.first.possibleCallees.forEach { +" FUN: ${it.callee}" } + +"" } } } @@ -915,18 +904,17 @@ internal object Devirtualization { addEdge(functionNodesMap[body.returns]!!, functionConstraintGraph.returns) addEdge(functionNodesMap[body.throws]!!, functionConstraintGraph.throws) - DEBUG_OUTPUT(0) { - println("CONSTRAINT GRAPH FOR $symbol") + context.logMultiple { + +"CONSTRAINT GRAPH FOR $symbol" val ids = function.body.allScopes.flatMap { it.nodes }.withIndex().associateBy({ it.value }, { it.index }) function.body.forEachNonScopeNode { node -> - println("FT NODE #${ids[node]}") - DataFlowIR.Function.printNode(node, ids) + +"FT NODE #${ids[node]}" + +DataFlowIR.Function.nodeToString(node, ids) val constraintNode = functionNodesMap[node] ?: variables[node] ?: return@forEachNonScopeNode - println(" CG NODE #${constraintNode.id}: ${constraintNode.toString(allTypes)}") - println() + +" CG NODE #${constraintNode.id}: ${constraintNode.toString(allTypes)}" } - println("Returns: #${ids[function.body.returns]}") - println() + +"Returns: #${ids[function.body.returns]}" + +"" } } @@ -982,21 +970,17 @@ internal object Devirtualization { private fun addInstantiatingClass(type: DataFlowIR.Type.Declared) { if (instantiatingClasses[type.index]) return instantiatingClasses.set(type.index) - - DEBUG_OUTPUT(1) { println("Adding instantiating class: $type") } - + context.log { "Adding instantiating class: $type" } checkSupertypes(type, type, BitSet()) } private fun processVirtualCall(virtualCall: ConstraintGraphVirtualCall, receiverType: DataFlowIR.Type.Declared) { - DEBUG_OUTPUT(1) { - println("Processing virtual call: ${virtualCall.virtualCall.callee}") - println("Receiver type: $receiverType") + context.logMultiple { + +"Processing virtual call: ${virtualCall.virtualCall.callee}" + +"Receiver type: $receiverType" } - val callee = receiverType.calleeAt(virtualCall.virtualCall) - addEdge(doCall(virtualCall.caller, callee, virtualCall.arguments, callee.returnParameter.type.resolved()), virtualCall.returnsNode) } @@ -1006,18 +990,17 @@ internal object Devirtualization { seenTypes: BitSet) { seenTypes.set(type.index) - DEBUG_OUTPUT(1) { - println("Checking supertype $type of $inheritor") + context.logMultiple { + +"Checking supertype $type of $inheritor" typesVirtualCallSites[type.index].let { if (it.isEmpty()) - println("None virtual call sites encountered yet") + +"None virtual call sites encountered yet" else { - println("Virtual call sites:") - it.forEach { - println(" ${it.virtualCall.callee}") - } + +"Virtual call sites:" + it.forEach { +" ${it.virtualCall.callee}" } } } + +"" } typesVirtualCallSites[type.index].let { virtualCallSites -> @@ -1175,19 +1158,17 @@ internal object Devirtualization { val callee = node.callee val receiverType = node.receiverType.resolved() - DEBUG_OUTPUT(0) { - println("Virtual call") - println("Caller: ${function.symbol}") - println("Callee: $callee") - println("Receiver type: $receiverType") - } + context.logMultiple { + +"Virtual call" + +"Caller: ${function.symbol}" + +"Callee: $callee" + +"Receiver type: $receiverType" - DEBUG_OUTPUT(0) { - println("Possible callees:") + +"Possible callees:" forEachBitInBoth(typeHierarchy.inheritorsOf(receiverType), instantiatingClasses) { - println(allTypes[it].calleeAt(node)) + +allTypes[it].calleeAt(node).toString() } - println() + +"" } val returnType = node.returnType.resolved() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt index 75f4aef2717..e348b1ab11a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraphMultiNode 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.logMultiple import org.jetbrains.kotlin.ir.declarations.IrClass import kotlin.math.min @@ -104,12 +105,6 @@ internal object EscapeAnalysis { * seeing all edges directed. */ - private val DEBUG = 0 - - private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { - if (DEBUG > severity) block() - } - // A special marker field for external types implemented in the runtime (mainly, arrays). // The types being passed to the constructor are not used in the analysis - just put there anything. private val intestinesField = DataFlowIR.Field(null, DataFlowIR.Type.Virtual, 1L, "inte\$tines") @@ -317,16 +312,14 @@ internal object EscapeAnalysis { return true } - override fun toString() = debugOutput(null) + override fun toString() = debugString(null) - fun debugOutput(root: String?): String { - val result = StringBuilder() - result.append(root ?: kind.toString()) + fun debugString(root: String?) = buildString { + append(root ?: kind.toString()) path.forEach { - result.append('.') - result.append(it.name ?: "") + append('.') + append(it.name ?: "") } - return result.toString() } fun goto(field: DataFlowIR.Field?) = when (field) { @@ -447,7 +440,7 @@ internal object EscapeAnalysis { } private class InterproceduralAnalysis( - context: Context, + val context: Context, val callGraph: CallGraph, val intraproceduralAnalysisResults: Map, val externalModulesDFG: ExternalModulesDFG, @@ -466,48 +459,44 @@ internal object EscapeAnalysis { val escapeAnalysisResults = mutableMapOf() fun analyze() { - - DEBUG_OUTPUT(0) { - println("CALL GRAPH") - callGraph.directEdges.forEach { t, u -> - println(" FUN $t") + context.logMultiple { + +"CALL GRAPH" + callGraph.directEdges.forEach { (t, u) -> + +" FUN $t" u.callSites.forEach { val label = when { it.isVirtual -> "VIRTUAL" callGraph.directEdges.containsKey(it.actualCallee) -> "LOCAL" else -> "EXTERNAL" } - println(" CALLS $label ${it.actualCallee}") - } - callGraph.reversedEdges[t]!!.forEach { - println(" CALLED BY $it") + +" CALLS $label ${it.actualCallee}" } + callGraph.reversedEdges[t]!!.forEach { +" CALLED BY $it" } } + +"" } val condensation = DirectedGraphCondensationBuilder(callGraph).build() - DEBUG_OUTPUT(0) { - println("CONDENSATION") + context.logMultiple { + +"CONDENSATION" condensation.topologicalOrder.forEach { multiNode -> - println(" MULTI-NODE") - multiNode.nodes.forEach { - println(" $it") - } + +" MULTI-NODE" + multiNode.nodes.forEach { +" $it" } } - println("CONDENSATION(DETAILED)") + +"" + +"CONDENSATION(DETAILED)" condensation.topologicalOrder.forEach { multiNode -> - println(" MULTI-NODE") + +" MULTI-NODE" multiNode.nodes.forEach { - println(" $it") + +" $it" callGraph.directEdges[it]!!.callSites .filter { callGraph.directEdges.containsKey(it.actualCallee) } - .forEach { println(" CALLS ${it.actualCallee}") } - callGraph.reversedEdges[it]!!.forEach { - println(" CALLED BY $it") - } + .forEach { +" CALLS ${it.actualCallee}" } + callGraph.reversedEdges[it]!!.forEach { +" CALLED BY $it" } } } + +"" } for (functionSymbol in callGraph.directEdges.keys) { @@ -519,9 +508,9 @@ internal object EscapeAnalysis { for (multiNode in condensation.topologicalOrder.reversed()) analyze(callGraph, multiNode) - DEBUG_OUTPUT(1) { - println("Managed to alloc on stack: ${stackAllocsCount * 100.0 / (globalAllocsCount + stackAllocsCount)}%") - println("Total graph size: $totalGraphSize") + context.logMultiple { + +"Managed to alloc on stack: ${stackAllocsCount * 100.0 / (globalAllocsCount + stackAllocsCount)}%" + +"Total graph size: $totalGraphSize" } } @@ -532,15 +521,15 @@ internal object EscapeAnalysis { private fun analyze(callGraph: CallGraph, multiNode: DirectedGraphMultiNode) { val nodes = multiNode.nodes.filter { intraproceduralAnalysisResults.containsKey(it) }.toMutableSet() - DEBUG_OUTPUT(0) { - println("Analyzing multiNode:\n ${nodes.joinToString("\n ") { it.toString() }}") + context.logMultiple { + +"Analyzing multiNode:\n ${nodes.joinToString("\n ") { it.toString() }}" nodes.forEach { from -> - println("DataFlowIR") + +"DataFlowIR" intraproceduralAnalysisResults[from]!!.function.debugOutput() callGraph.directEdges[from]!!.callSites.forEach { to -> - println("CALL") - println(" from $from") - println(" to ${to.actualCallee}") + +"CALL" + +" from $from" + +" to ${to.actualCallee}" } } } @@ -553,30 +542,23 @@ internal object EscapeAnalysis { val function = toAnalyze.first() toAnalyze.remove(function) numberOfRuns[function] = numberOfRuns[function]!! + 1 - - DEBUG_OUTPUT(0) { println("Processing function $function") } + context.log { "Processing function $function" } val startResult = escapeAnalysisResults[function]!! - - DEBUG_OUTPUT(0) { println("Start escape analysis result:\n$startResult") } + context.log { "Start escape analysis result:\n$startResult" } analyze(callGraph, pointsToGraphs[function]!!, function) val endResult = escapeAnalysisResults[function]!! + if (startResult == endResult) { - - DEBUG_OUTPUT(0) { println("Escape analysis is not changed") } - + context.log { "Escape analysis is not changed" } } else { - - DEBUG_OUTPUT(0) { println("Escape analysis was refined:\n$endResult") } - + context.log { "Escape analysis was refined:\n$endResult" } if (numberOfRuns[function]!! > 1) { - - DEBUG_OUTPUT(0) { - println("WARNING: Escape analysis for $function seems not to be converging." + - " Assuming conservative results.") + context.log { + "WARNING: Escape analysis for $function seems not to be converging." + + " Assuming conservative results." } - escapeAnalysisResults[function] = FunctionEscapeAnalysisResult.pessimistic(function.parameters.size) nodes.remove(function) } @@ -632,11 +614,9 @@ internal object EscapeAnalysis { pointerSize /* typeinfo */ + 4 /* size */ + itemSize * length private fun analyze(callGraph: CallGraph, pointsToGraph: PointsToGraph, function: DataFlowIR.FunctionSymbol.Declared) { - DEBUG_OUTPUT(0) { - println("Before calls analysis") - pointsToGraph.print() - pointsToGraph.printDigraph(false) - } + context.log {"Before calls analysis" } + pointsToGraph.log() + pointsToGraph.logDigraph(false) callGraph.directEdges[function]!!.callSites.forEach { val callee = it.actualCallee @@ -648,20 +628,16 @@ internal object EscapeAnalysis { pointsToGraph.processCall(it, calleeEAResult) } - DEBUG_OUTPUT(0) { - println("After calls analysis") - pointsToGraph.print() - pointsToGraph.printDigraph(false) - } + context.log { "After calls analysis" } + pointsToGraph.log() + pointsToGraph.logDigraph(false) // Build transitive closure. val eaResult = pointsToGraph.buildClosure() - DEBUG_OUTPUT(0) { - println("After closure building") - pointsToGraph.print() - pointsToGraph.printDigraph(true) - } + context.log { "After closure building" } + pointsToGraph.log() + pointsToGraph.logDigraph(true) escapeAnalysisResults[function] = eaResult @@ -678,38 +654,29 @@ internal object EscapeAnalysis { val callee = callSite.actualCallee.resolved() val calleeEAResult = if (callSite.isVirtual) { - - DEBUG_OUTPUT(0) { println("A virtual call: $callee") } - + context.log { "A virtual call: $callee" } FunctionEscapeAnalysisResult.pessimistic(callee.parameters.size) } else { - - DEBUG_OUTPUT(0) { println("An external call: $callee") } - + context.log { "An external call: $callee" } if (callee.name?.startsWith("kfun:kotlin.") == true // TODO: Is it possible to do it in a more fine-grained fashion? && !callee.name.startsWith("kfun:kotlin.native.concurrent")) { - - DEBUG_OUTPUT(0) { println("A function from K/N runtime - can use annotations") } - + context.log { "A function from K/N runtime - can use annotations" } FunctionEscapeAnalysisResult.fromBits( callee.escapes ?: 0, (0..callee.parameters.size).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 } ) } else { - - DEBUG_OUTPUT(0) { println("An unknown function - assume pessimistic result") } - + context.log { "An unknown function - assume pessimistic result" } FunctionEscapeAnalysisResult.pessimistic(callee.parameters.size) } } - DEBUG_OUTPUT(0) { - println("Escape analysis result") - println(calleeEAResult.toString()) - println() + context.logMultiple { + +"Escape analysis result" + +calleeEAResult.toString() + +"" } - return calleeEAResult } @@ -806,12 +773,11 @@ internal object EscapeAnalysis { fun escapes(node: PointsToGraphNode) = node in reachableFromEscapeOrigins || node in referencingEscapeOrigins - val ids = if (DEBUG > 0) - (listOf(functionAnalysisResult.function.body.rootScope) - + functionAnalysisResult.function.body.allScopes.flatMap { it.nodes } - ) - .withIndex().associateBy({ it.value }, { it.index }) - else null + val ids = ( + listOf(functionAnalysisResult.function.body.rootScope) + + functionAnalysisResult.function.body.allScopes.flatMap { it.nodes } + ) + .withIndex().associateBy({ it.value }, { it.index }) fun lifetimeOf(node: DataFlowIR.Node) = nodes[node]!!.let { it.forcedLifetime ?: lifetimeOf(it) } @@ -846,16 +812,12 @@ internal object EscapeAnalysis { private val returnValues: Set init { - - DEBUG_OUTPUT(0) { - println("Building points-to graph for function $functionSymbol") - println("Results of preliminary function analysis") + context.logMultiple { + +"Building points-to graph for function $functionSymbol" + +"Results of preliminary function analysis" } - functionAnalysisResult.nodesRoles.forEach { (node, roles) -> - - DEBUG_OUTPUT(0) { println("NODE ${nodeToString(node)}: $roles") } - + context.log { "NODE ${nodeToString(node)}: $roles" } nodes[node] = newNode(roles, node) } @@ -904,13 +866,13 @@ internal object EscapeAnalysis { } } - private fun nodeToStringWhole(node: DataFlowIR.Node) = DataFlowIR.Function.nodeToString(node, ids!!) + private fun nodeToStringWhole(node: DataFlowIR.Node) = DataFlowIR.Function.nodeToString(node, ids) - private fun nodeToString(node: DataFlowIR.Node) = ids!![node].toString() + private fun nodeToString(node: DataFlowIR.Node) = ids[node].toString() - fun print() { - println("POINTS-TO GRAPH") - println("NODES") + fun log() = context.logMultiple { + +"POINTS-TO GRAPH" + +"NODES" val tempIds = mutableMapOf() var tempIndex = 0 allNodes.forEach { @@ -919,18 +881,17 @@ internal object EscapeAnalysis { } allNodes.forEach { val tempId = tempIds[it] - println(" ${lifetimeOf(it)} ${it.depth} ${if (it in escapeOrigins) "ESCAPES" else ""} ${it.node?.let { nodeToString(it) } ?: "t$tempId"}") - print(it.node?.let { nodeToStringWhole(it) } ?: " t$tempId\n") + +" ${lifetimeOf(it)} ${it.depth} ${if (it in escapeOrigins) "ESCAPES" else ""} ${it.node?.let { nodeToString(it) } ?: "t$tempId"}" + +(it.node?.let { nodeToStringWhole(it) } ?: " t$tempId") } } - fun printDigraph( + fun logDigraph( markDrains: Boolean, nodeFilter: (PointsToGraphNode) -> Boolean = { true }, nodeLabel: ((PointsToGraphNode) -> String)? = null - ) { - println("digraph {") - val ids = ids!! + ) = context.logMultiple { + +"digraph {" val tempIds = mutableMapOf() var tempIndex = 0 allNodes.forEach { @@ -951,24 +912,23 @@ internal object EscapeAnalysis { if (!nodeFilter(to)) continue when (it) { is PointsToGraphEdge.Assignment -> - println(" \"${from.format()}\" -> \"${to.format()}\";") + +" \"${from.format()}\" -> \"${to.format()}\";" is PointsToGraphEdge.Field -> - println(" \"${from.format()}\" -> \"${to.format()}\" [ label=\"${it.field.name}\"];") + +" \"${from.format()}\" -> \"${to.format()}\" [ label=\"${it.field.name}\"];" } } } - println("}") + +"}" } fun processCall(callSite: CallGraphNode.CallSite, calleeEscapeAnalysisResult: FunctionEscapeAnalysisResult) { val call = callSite.call - - DEBUG_OUTPUT(0) { - println("Processing callSite") - println(nodeToStringWhole(call)) - println("Actual callee: ${callSite.actualCallee}") - println("Callee escape analysis result:") - println(calleeEscapeAnalysisResult.toString()) + context.logMultiple { + +"Processing callSite" + +nodeToStringWhole(call) + +"Actual callee: ${callSite.actualCallee}" + +"Callee escape analysis result:" + +calleeEscapeAnalysisResult.toString() } val arguments = if (call is DataFlowIR.Node.NewObject) { @@ -1009,56 +969,44 @@ internal object EscapeAnalysis { calleeEscapeAnalysisResult.escapes.forEach { escapingNode -> val (arg, node) = mapNode(escapingNode) if (node == null) { - - DEBUG_OUTPUT(0) { println("WARNING: There is no node ${nodeToString(arg!!)}") } - + context.log { "WARNING: There is no node ${nodeToString(arg!!)}" } return@forEach } escapeOrigins += node - - DEBUG_OUTPUT(0) { - println("Node ${escapingNode.debugOutput(arg?.let { nodeToString(it) })} escapes") - } + context.log { "Node ${escapingNode.debugString(arg?.let { nodeToString(it) })} escapes" } } calleeEscapeAnalysisResult.pointsTo.edges.forEach { edge -> val (fromArg, fromNode) = mapNode(edge.from) if (fromNode == null) { - - DEBUG_OUTPUT(0) { println("WARNING: There is no node ${nodeToString(fromArg!!)}") } - + context.log { "WARNING: There is no node ${nodeToString(fromArg!!)}" } return@forEach } val (toArg, toNode) = mapNode(edge.to) if (toNode == null) { - - DEBUG_OUTPUT(0) { println("WARNING: There is no node ${nodeToString(toArg!!)}") } - + context.log { "WARNING: There is no node ${nodeToString(toArg!!)}" } return@forEach } fromNode.addAssignmentEdge(toNode) - DEBUG_OUTPUT(0) { - println("Adding edge") - println(" FROM ${edge.from.debugOutput(fromArg?.let { nodeToString(it) })}") - println(" TO ${edge.to.debugOutput(toArg?.let { nodeToString(it) })}") + context.logMultiple { + +"Adding edge" + +" FROM ${edge.from.debugString(fromArg?.let { nodeToString(it) })}" + +" TO ${edge.to.debugString(toArg?.let { nodeToString(it) })}" } } } fun buildClosure(): FunctionEscapeAnalysisResult { - - DEBUG_OUTPUT(0) { - println("BUILDING CLOSURE") - println("Return values:") - returnValues.forEach { - println(" ${nodeToString(it)}") - } + context.logMultiple { + +"BUILDING CLOSURE" + +"Return values:" + returnValues.forEach { +" ${nodeToString(it)}" } } buildDrains() - DEBUG_OUTPUT(0) { printDigraph(true) } + logDigraph(true) computeLifetimes() @@ -1072,9 +1020,7 @@ internal object EscapeAnalysis { */ val (numberOfDrains, nodeIds) = paintInterestingNodes() - DEBUG_OUTPUT(0) { - printDigraph(true, { nodeIds[it] != null }, { nodeIds[it].toString() }) - } + logDigraph(true, { nodeIds[it] != null }, { nodeIds[it].toString() }) // TODO: Remove redundant edges. val compressedEdges = mutableListOf() @@ -1616,11 +1562,7 @@ internal object EscapeAnalysis { if (lifetime != computedLifetime) { if (propagateExiledToHeapObjects && node.isAlloc) { - - DEBUG_OUTPUT(0) { - println("Forcing node ${nodeToString(node)} to escape") - } - + context.log { "Forcing node ${nodeToString(node)} to escape" } escapeOrigins += ptgNode propagateEscapeOrigin(ptgNode) } else { @@ -1640,11 +1582,7 @@ internal object EscapeAnalysis { allowedToAlloc = 0 // Do not exile primitive arrays - they ain't reference no object. if (irClass.symbol == symbols.array && propagateExiledToHeapObjects) { - - DEBUG_OUTPUT(0) { - println("Forcing node ${nodeToString(ptgNode.node!!)} to escape") - } - + context.log { "Forcing node ${nodeToString(ptgNode.node!!)} to escape" } escapeOrigins += ptgNode propagateEscapeOrigin(ptgNode) } else { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt index c2494f22ea7..cace5e8bdd6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt @@ -8,15 +8,10 @@ package org.jetbrains.kotlin.backend.konan.optimizations 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.logMultiple import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems internal object LocalEscapeAnalysis { - private val DEBUG = 0 - - private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { - if (DEBUG > severity) block() - } - private enum class EscapeState { GLOBAL_ESCAPE, // escape through global reference ARG_ESCAPE, // escapes through function arguments, return or throw @@ -187,7 +182,7 @@ internal object LocalEscapeAnalysis { } ir?.let { lifetimes.put(it, Lifetime.STACK) - DEBUG_OUTPUT(3) { println("${ir} does not escape") } + context.log { "$ir does not escape" } } } } @@ -195,18 +190,18 @@ internal object LocalEscapeAnalysis { fun analyze(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap) { moduleDFG.functions.forEach { (name, function) -> - DEBUG_OUTPUT(5) { - println("===============================================") - println("Visiting $name") - println("DATA FLOW IR:") - function.debugOutput() + context.logMultiple { + +"===============================================" + +"Visiting $name" + +"DATA FLOW IR:" + +function.debugString() } FunctionAnalyzer(function, context).analyze(lifetimes) } } fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap) { - DEBUG_OUTPUT(1) { println("In local EA") } + context.log { "In local EA" } assert(lifetimes.isEmpty()) analyze(context, moduleDFG, lifetimes) }