From df87c8e794850dbebfa57e4f471fd16151805f23 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 22 Dec 2016 10:29:45 +0700 Subject: [PATCH 01/13] TEST: test for inlining added --- backend.native/tests/codegen/inline/inline0.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 backend.native/tests/codegen/inline/inline0.kt diff --git a/backend.native/tests/codegen/inline/inline0.kt b/backend.native/tests/codegen/inline/inline0.kt new file mode 100644 index 00000000000..7bf9a7f0a08 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline0.kt @@ -0,0 +1,13 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i: Int): Int { + return i + 1 +} + +fun bar(i: Int): Int { + return foo(i) +} + +fun main(args: Array) { + println(bar(1).toString()) +} + From 303e6a7c798de40c78d3e97a3690d062f75e46dd Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Fri, 23 Dec 2016 11:25:24 +0700 Subject: [PATCH 02/13] Inlining phase inserted --- .../kotlin/backend/konan/KonanLower.kt | 3 ++ .../kotlin/backend/konan/KonanPhases.kt | 1 + .../backend/konan/lower/FunctionInlining.kt | 33 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index ae7b3109e63..f841ea4dc52 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -35,5 +35,8 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.AUTOBOX) { Autoboxing(context).lower(irFile) } + phaser.phase(KonanPhase.LOWER_INLINE) { + FunctionInlining(context).inline(irFile) + } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 5df794ed9d3..a678f76a973 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -15,6 +15,7 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"), /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering"), /* ... ... */ LOWER_CALLABLES("Callable references Lowering"), + /* ... ... */ LOWER_INLINE("Functions inlining"), /* ... ... */ AUTOBOX("Autoboxing of primitive types"), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt new file mode 100644 index 00000000000..29ab990175b --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -0,0 +1,33 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid + +class FunctionInlining(val context: Context) { + fun inline(irFile: IrFile) { + irFile.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitCall(expression: IrCall) { + val functionDescriptor = expression.descriptor as FunctionDescriptor + if (functionDescriptor.isInline == false) { + super.visitCall(expression) + } else { + val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] + println("inline_akm") + } + } + }) + + } +} From ee184de9930f9ebc942e00eb746662e63d0d7669 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Fri, 23 Dec 2016 11:27:12 +0700 Subject: [PATCH 03/13] Create map FunctionDescriptor to FunctionDeclaration --- .../kotlin/backend/konan/ir/ModuleIndex.kt | 14 ++++++++++++++ .../kotlin/backend/konan/lower/FunctionInlining.kt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt index 4cd7f713cd1..21fb20af825 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt @@ -1,7 +1,9 @@ package org.jetbrains.kotlin.backend.konan.ir +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -16,6 +18,11 @@ class ModuleIndex(val module: IrModuleFragment) { */ val classes: Map + /** + * Contains all functions declared in [module] + */ + val functions = mutableMapOf() + init { val map = mutableMapOf() @@ -33,6 +40,13 @@ class ModuleIndex(val module: IrModuleFragment) { } } + override fun visitFunction(declaration: IrFunction) { + super.visitFunction(declaration) + + val functionDescriptor = declaration.descriptor + functions[functionDescriptor] = declaration + } + }) classes = map diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 29ab990175b..d5535a709bf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid -class FunctionInlining(val context: Context) { +internal class FunctionInlining(val context: Context) { fun inline(irFile: IrFile) { irFile.acceptVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { From c106e4622cdd3e2c8db2d99274a0fdee67fb32ea Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Mon, 26 Dec 2016 15:39:13 +0700 Subject: [PATCH 04/13] Function without args returnung void has been inlined --- .../backend/konan/lower/FunctionInlining.kt | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index d5535a709bf..0725568c4bb 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -1,33 +1,38 @@ package org.jetbrains.kotlin.backend.konan.lower -import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid internal class FunctionInlining(val context: Context) { fun inline(irFile: IrFile) { - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } + irFile.accept(object : IrElementTransformerVoid() { + override fun visitElement(element: IrElement) = element.accept(this, null) - override fun visitCall(expression: IrCall) { - val functionDescriptor = expression.descriptor as FunctionDescriptor - if (functionDescriptor.isInline == false) { - super.visitCall(expression) - } else { - val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] - println("inline_akm") - } + override fun visitCall(expression: IrCall): IrExpression { + val functionDescriptor = expression.descriptor as FunctionDescriptor // + if (functionDescriptor.isInline == false) return super.visitCall(expression) // function is not to be inlined - do nothing + + if (!functionDescriptor.name.asString().contains("foo")) return super.visitCall(expression) + + val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor]!! // get FunctionDeclaration by FunctionDescriptor + val body = functionDeclaration.body!! as IrBlockBodyImpl + + val startOffset = functionDeclaration.startOffset + val endOffset = functionDeclaration.endOffset + val returnType = functionDeclaration.descriptor.returnType!! + val statements = body.statements + val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // create + return irBlock } - }) + }, null) } } From 252af6ce1525db055ddf548b233ffeaaf058038e Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Mon, 26 Dec 2016 15:40:13 +0700 Subject: [PATCH 05/13] TESTS: test for inlining integrated in the testsuite --- backend.native/tests/build.gradle | 7 ++++++- backend.native/tests/codegen/inline/inline0.kt | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index cc6eb766176..841c44da28e 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -825,4 +825,9 @@ task unit3(type: RunKonanTest) { task unit4(type: RunKonanTest) { goldValue = "Done\n" source = "codegen/basics/unit4.kt" -} \ No newline at end of file +} + +task inline0(type: RunKonanTest) { + goldValue = "Ok\n3\n" + source = "codegen/inline/inline0.kt" +} diff --git a/backend.native/tests/codegen/inline/inline0.kt b/backend.native/tests/codegen/inline/inline0.kt index 7bf9a7f0a08..ac852a05f90 100644 --- a/backend.native/tests/codegen/inline/inline0.kt +++ b/backend.native/tests/codegen/inline/inline0.kt @@ -1,13 +1,14 @@ @Suppress("NOTHING_TO_INLINE") -inline fun foo(i: Int): Int { - return i + 1 +inline fun foo() { + println("Ok") } -fun bar(i: Int): Int { - return foo(i) +fun bar(i: Int, j: Int): Int { + foo() + return i + j } fun main(args: Array) { - println(bar(1).toString()) + println(bar(1, 2).toString()) } From c46ba1aa3f5ad1c0a6ff8fa1ef1cda98f9f2b9b4 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 29 Dec 2016 15:52:51 +0700 Subject: [PATCH 06/13] TESTS: passing changes in the test --- backend.native/tests/build.gradle | 2 +- backend.native/tests/codegen/inline/inline0.kt | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 841c44da28e..ae6e0ed2824 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -828,6 +828,6 @@ task unit4(type: RunKonanTest) { } task inline0(type: RunKonanTest) { - goldValue = "Ok\n3\n" + goldValue = "84\n" source = "codegen/inline/inline0.kt" } diff --git a/backend.native/tests/codegen/inline/inline0.kt b/backend.native/tests/codegen/inline/inline0.kt index ac852a05f90..c1284b1a0d4 100644 --- a/backend.native/tests/codegen/inline/inline0.kt +++ b/backend.native/tests/codegen/inline/inline0.kt @@ -1,14 +1,13 @@ @Suppress("NOTHING_TO_INLINE") -inline fun foo() { - println("Ok") +inline fun foo(i1: Int, j1: Int): Int { + return i1 + j1 } fun bar(i: Int, j: Int): Int { - foo() - return i + j + return i + foo(i, j) } fun main(args: Array) { - println(bar(1, 2).toString()) + println(bar(41, 2).toString()) } From 0d06299fe3faac76fac109092b82acaf4c16a54d Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 29 Dec 2016 15:53:39 +0700 Subject: [PATCH 07/13] Inlining enabled for functions with arguments --- .../backend/konan/lower/FunctionInlining.kt | 79 ++++++++++++++----- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 0725568c4bb..c7145b69056 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -1,38 +1,75 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.ir.getArguments import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrBody -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.util.DeepCopyIrTree import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -internal class FunctionInlining(val context: Context) { - fun inline(irFile: IrFile) { - irFile.accept(object : IrElementTransformerVoid() { - override fun visitElement(element: IrElement) = element.accept(this, null) +//-----------------------------------------------------------------------------// - override fun visitCall(expression: IrCall): IrExpression { - val functionDescriptor = expression.descriptor as FunctionDescriptor // - if (functionDescriptor.isInline == false) return super.visitCall(expression) // function is not to be inlined - do nothing +internal class FunctionInlining(val context: Context): IrElementTransformerVoid() { - if (!functionDescriptor.name.asString().contains("foo")) return super.visitCall(expression) + fun inline(irFile: IrFile) = irFile.accept(this, null) - val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor]!! // get FunctionDeclaration by FunctionDescriptor - val body = functionDeclaration.body!! as IrBlockBodyImpl + //-------------------------------------------------------------------------// - val startOffset = functionDeclaration.startOffset - val endOffset = functionDeclaration.endOffset - val returnType = functionDeclaration.descriptor.returnType!! - val statements = body.statements - val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // create - return irBlock - } - }, null) + fun removeReturn(statements: MutableList) = + statements.map { statement -> + (statement as? IrReturn)?.value ?: statement + } as MutableList + //-------------------------------------------------------------------------// + + override fun visitCall(expression: IrCall): IrExpression { + val functionDescriptor = expression.descriptor as FunctionDescriptor // + if (!functionDescriptor.isInline) return super.visitCall(expression) // function is not to be inlined - do nothing + + val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // get FunctionDeclaration by FunctionDescriptor + if (functionDeclaration == null) return super.visitCall(expression) // TODO what if we do not have declaration? + val copyFuncDeclaration = functionDeclaration.accept(DeepCopyIrTree(), null) as IrFunction + + val body = copyFuncDeclaration.body!! as IrBlockBody + val statements = removeReturn(body.statements) + val startOffset = copyFuncDeclaration.startOffset + val endOffset = copyFuncDeclaration.endOffset + val returnType = copyFuncDeclaration.descriptor.returnType!! + val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // create + + val parameterToExpression = expression.getArguments() + val parametersTransformer = ParametersTransformer(parameterToExpression) + irBlock.accept(parametersTransformer, null) + + return irBlock } + + //-------------------------------------------------------------------------// + + override fun visitElement(element: IrElement) = element.accept(this, null) } + +//-----------------------------------------------------------------------------// + +internal class ParametersTransformer(val parameterToExpression: List >): IrElementTransformerVoid() { + + override fun visitElement(element: IrElement) = element.accept(this, null) + + //-------------------------------------------------------------------------// + + override fun visitGetValue(expression: IrGetValue): IrExpression { + val descriptor = expression.descriptor + if (descriptor !is ParameterDescriptor) { + return super.visitGetValue(expression) + } + val parExp = parameterToExpression.find { it.first == descriptor } + return parExp?.let { parExp.second } ?: super.visitGetValue(expression) + } +} \ No newline at end of file From 49bedf1cc3bb54c597b595383cf4b1348d27b8b1 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Mon, 9 Jan 2017 10:51:37 +0700 Subject: [PATCH 08/13] Comments added --- .../backend/konan/lower/FunctionInlining.kt | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index c7145b69056..5c41d1a313e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.util.DeepCopyIrTree import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -30,25 +29,25 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( //-------------------------------------------------------------------------// override fun visitCall(expression: IrCall): IrExpression { - val functionDescriptor = expression.descriptor as FunctionDescriptor // - if (!functionDescriptor.isInline) return super.visitCall(expression) // function is not to be inlined - do nothing + val functionDescriptor = expression.descriptor as FunctionDescriptor + if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing. - val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // get FunctionDeclaration by FunctionDescriptor - if (functionDeclaration == null) return super.visitCall(expression) // TODO what if we do not have declaration? - val copyFuncDeclaration = functionDeclaration.accept(DeepCopyIrTree(), null) as IrFunction + val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor. + if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module. + val copyFuncDeclaration = functionDeclaration.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function. val body = copyFuncDeclaration.body!! as IrBlockBody - val statements = removeReturn(body.statements) + val statements = removeReturn(body.statements) // Replace "return" with its value. val startOffset = copyFuncDeclaration.startOffset val endOffset = copyFuncDeclaration.endOffset val returnType = copyFuncDeclaration.descriptor.returnType!! - val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // create + val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // Create IrBlock containing function statements. - val parameterToExpression = expression.getArguments() + val parameterToExpression = expression.getArguments() // Build map parameter -> expression. val parametersTransformer = ParametersTransformer(parameterToExpression) - irBlock.accept(parametersTransformer, null) + irBlock.accept(parametersTransformer, null) // Replace parameters with expression. - return irBlock + return irBlock // Return newly created IrBlock instead of IrCall. } //-------------------------------------------------------------------------// @@ -66,10 +65,10 @@ internal class ParametersTransformer(val parameterToExpression: List Date: Mon, 9 Jan 2017 16:56:26 +0700 Subject: [PATCH 09/13] TESTS: new tests added --- backend.native/tests/build.gradle | 5 +++++ backend.native/tests/codegen/inline/inline1.kt | 14 ++++++++++++++ backend.native/tests/codegen/inline/inline2.kt | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 backend.native/tests/codegen/inline/inline1.kt create mode 100644 backend.native/tests/codegen/inline/inline2.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index ae6e0ed2824..1825eb0e36b 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -831,3 +831,8 @@ task inline0(type: RunKonanTest) { goldValue = "84\n" source = "codegen/inline/inline0.kt" } + +task inline1(type: RunKonanTest) { + goldValue = "Hello world\n" + source = "codegen/inline/inline1.kt" +} diff --git a/backend.native/tests/codegen/inline/inline1.kt b/backend.native/tests/codegen/inline/inline1.kt new file mode 100644 index 00000000000..36b23aee79d --- /dev/null +++ b/backend.native/tests/codegen/inline/inline1.kt @@ -0,0 +1,14 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(s4: String, s5: String): String { + return s4 + s5 +} + +fun bar(s1: String, s2: String, s3: String): String { + return s1 + foo(s2, s3) +} + +fun main(args: Array) { + println(bar("Hello ", "wor", "ld")) +} + + diff --git a/backend.native/tests/codegen/inline/inline2.kt b/backend.native/tests/codegen/inline/inline2.kt new file mode 100644 index 00000000000..8b0edf296c0 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline2.kt @@ -0,0 +1,16 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i4: Int, i5: Int): Int { + try { + return i4 / i5 + } catch (e: Throwable) { + return i4 + } +} + +fun bar(i1: Int, i2: Int, i3: Int): Int { + return i1 + foo(i2, i3) +} + +fun main(args: Array) { + println(bar(1, 8, 2).toString()) +} From eba08c567413c6316f8cd45ab30fb78b519bc51b Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Wed, 11 Jan 2017 16:21:42 +0700 Subject: [PATCH 10/13] New "return" remove scheme --- .../kotlin/backend/konan/lower/FunctionInlining.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 5c41d1a313e..5e14d853522 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -37,10 +37,10 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( val copyFuncDeclaration = functionDeclaration.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function. val body = copyFuncDeclaration.body!! as IrBlockBody - val statements = removeReturn(body.statements) // Replace "return" with its value. val startOffset = copyFuncDeclaration.startOffset val endOffset = copyFuncDeclaration.endOffset val returnType = copyFuncDeclaration.descriptor.returnType!! + val statements = body.statements val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // Create IrBlock containing function statements. val parameterToExpression = expression.getArguments() // Build map parameter -> expression. @@ -63,6 +63,14 @@ internal class ParametersTransformer(val parameterToExpression: List Date: Fri, 13 Jan 2017 11:37:37 +0700 Subject: [PATCH 11/13] Refactoring --- .../kotlin/backend/konan/lower/FunctionInlining.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 5e14d853522..b1602490b8b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -21,13 +21,6 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( //-------------------------------------------------------------------------// - fun removeReturn(statements: MutableList) = - statements.map { statement -> - (statement as? IrReturn)?.value ?: statement - } as MutableList - - //-------------------------------------------------------------------------// - override fun visitCall(expression: IrCall): IrExpression { val functionDescriptor = expression.descriptor as FunctionDescriptor if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing. From 551218c133eeab852dbfdc239a03825fd534035b Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Wed, 18 Jan 2017 17:21:04 +0700 Subject: [PATCH 12/13] TESTS: new tests added --- .../jetbrains/kotlin/backend/konan/ir/KonanIr.kt | 5 +++++ backend.native/tests/build.gradle | 10 ++++++++++ backend.native/tests/codegen/inline/inline2.kt | 14 +++++--------- backend.native/tests/codegen/inline/inline3.kt | 16 ++++++++++++++++ backend.native/tests/codegen/inline/inline4.kt | 13 +++++++++++++ 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt create mode 100644 backend.native/tests/codegen/inline/inline3.kt create mode 100644 backend.native/tests/codegen/inline/inline4.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt new file mode 100644 index 00000000000..4b978c127a9 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt @@ -0,0 +1,5 @@ +package org.jetbrains.kotlin.backend.konan.ir + +/** + * Created by jetbrains on 13/01/2017. + */ diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 1825eb0e36b..1e0b15521aa 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -836,3 +836,13 @@ task inline1(type: RunKonanTest) { goldValue = "Hello world\n" source = "codegen/inline/inline1.kt" } + +task inline2(type: RunKonanTest) { + goldValue = "hello 1 8\n" + source = "codegen/inline/inline2.kt" +} + +task inline3(type: RunKonanTest) { + goldValue = "5\n" + source = "codegen/inline/inline3.kt" +} diff --git a/backend.native/tests/codegen/inline/inline2.kt b/backend.native/tests/codegen/inline/inline2.kt index 8b0edf296c0..8ab7e4218fd 100644 --- a/backend.native/tests/codegen/inline/inline2.kt +++ b/backend.native/tests/codegen/inline/inline2.kt @@ -1,16 +1,12 @@ @Suppress("NOTHING_TO_INLINE") -inline fun foo(i4: Int, i5: Int): Int { - try { - return i4 / i5 - } catch (e: Throwable) { - return i4 - } +inline fun foo(i4: Int, i5: Int) { + println("hello $i4 $i5") } -fun bar(i1: Int, i2: Int, i3: Int): Int { - return i1 + foo(i2, i3) +fun bar(i1: Int, i2: Int) { + foo(i1, i2) } fun main(args: Array) { - println(bar(1, 8, 2).toString()) + bar(1, 8) } diff --git a/backend.native/tests/codegen/inline/inline3.kt b/backend.native/tests/codegen/inline/inline3.kt new file mode 100644 index 00000000000..8b0edf296c0 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline3.kt @@ -0,0 +1,16 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i4: Int, i5: Int): Int { + try { + return i4 / i5 + } catch (e: Throwable) { + return i4 + } +} + +fun bar(i1: Int, i2: Int, i3: Int): Int { + return i1 + foo(i2, i3) +} + +fun main(args: Array) { + println(bar(1, 8, 2).toString()) +} diff --git a/backend.native/tests/codegen/inline/inline4.kt b/backend.native/tests/codegen/inline/inline4.kt new file mode 100644 index 00000000000..04f6a475291 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline4.kt @@ -0,0 +1,13 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i4: Int, i5: Int): Int { + if (i4 > 0) return i4 + return i5 +} + +fun bar(i1: Int, i2: Int): Int { + return foo(i1, i2) +} + +fun main(args: Array) { + println(bar(1, 8).toString()) +} From 67013f8a60346f6c6d432a8d528a4adcdc208cfe Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Wed, 18 Jan 2017 17:22:14 +0700 Subject: [PATCH 13/13] Two passes inline scheme implemented --- .../kotlin/backend/konan/ir/KonanIr.kt | 38 +++++++++++- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 62 ++++++++++++++++++- .../backend/konan/lower/FunctionInlining.kt | 13 +--- 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt index 4b978c127a9..f711be1b9e2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt @@ -1,5 +1,37 @@ package org.jetbrains.kotlin.backend.konan.ir -/** - * Created by jetbrains on 13/01/2017. - */ +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.expressions.IrBlock +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.impl.IrContainerExpressionBase +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBase +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.KotlinType + +//-----------------------------------------------------------------------------// + +class IrInlineFunctionBody(startOffset: Int, endOffset: Int, type: KotlinType, origin: IrStatementOrigin? = null): + IrContainerExpressionBase(startOffset, endOffset, type, origin), IrBlock { + constructor(startOffset: Int, endOffset: Int, type: KotlinType, origin: IrStatementOrigin?, statements: List) : + this(startOffset, endOffset, type, origin) { + this.statements.addAll(statements) + } + + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitBlock(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + statements.forEach { it.accept(visitor, data) } + } + + override fun transformChildren(transformer: IrElementTransformer, data: D) { + statements.forEachIndexed { i, irStatement -> + statements[i] = irStatement.transform(transformer, data) + } + } +} + +//-----------------------------------------------------------------------------// + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 8e89ee3e285..05e87172719 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -629,6 +629,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid is IrThrow -> return evaluateThrow (value) is IrTry -> return evaluateTry (value) is IrStringConcatenation -> return evaluateStringConcatenation(value) + is IrInlineFunctionBody -> return evaluateInlineFunction (value) is IrContainerExpression -> return evaluateContainerExpression(value) is IrWhileLoop -> return evaluateWhileLoop (value) is IrDoWhileLoop -> return evaluateDoWhileLoop (value) @@ -741,7 +742,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid */ private fun continuationBlock(type: KotlinType, code: (ContinuationBlock) -> Unit = {}): ContinuationBlock { - val entry = codegen.basicBlock() + val entry = codegen.basicBlock("continuation_block") codegen.appendingTo(entry) { val valuePhi = if (type.isUnit()) { @@ -1479,6 +1480,65 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// + private inner class InlinedFunctionScope(val inlineBody: IrInlineFunctionBody) : InnerScopeImpl() { + + var bbExit : LLVMBasicBlockRef? = null + var resultPhi : LLVMValueRef? = null + + fun getExit(): LLVMBasicBlockRef? { + if (bbExit == null) bbExit = codegen.basicBlock("inline_body_exit") + return bbExit + } + + fun getResult(): LLVMValueRef? { + if (resultPhi == null) { + val bbCurrent = codegen.currentBlock + codegen.positionAtEnd(getExit()!!) + resultPhi = codegen.phi(codegen.getLLVMType(inlineBody.type)) + codegen.positionAtEnd(bbCurrent) + } + return resultPhi + } + + override fun genReturn(target: CallableDescriptor, value: LLVMValueRef?) { + if (KotlinBuiltIns.isUnit(inlineBody.type) == false) { + codegen.assignPhis(getResult()!! to value!!) + } + codegen.br(getExit()!!) + } + } + + //-------------------------------------------------------------------------// + + private fun evaluateInlineFunction(value: IrInlineFunctionBody): LLVMValueRef { + context.log("evaluateInlineFunction : ${value.statements.forEach { ir2string(it) }}") + + val inlinedFunctionScope = InlinedFunctionScope(value) + using(inlinedFunctionScope) { + value.statements.forEach { + generateStatement(it) + } + } + + if (!codegen.isAfterTerminator()) { // TODO should we solve this problem once and for all + if (inlinedFunctionScope.resultPhi != null) { + codegen.unreachable() + } + } + + if (inlinedFunctionScope.bbExit != null) { + codegen.positionAtEnd(inlinedFunctionScope.bbExit!!) + } + + if (inlinedFunctionScope.resultPhi != null) { + return inlinedFunctionScope.resultPhi!! + } else { + return codegen.theUnitInstanceRef.llvm + } + } + + //-------------------------------------------------------------------------// + private fun evaluateContainerExpression(value: IrContainerExpression): LLVMValueRef { context.log("evaluateContainerExpression : ${value.statements.forEach { ir2string(it) }}") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index b1602490b8b..4e0d9f7433e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -1,15 +1,14 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody import org.jetbrains.kotlin.backend.konan.ir.getArguments import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.util.DeepCopyIrTree import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -34,7 +33,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid( val endOffset = copyFuncDeclaration.endOffset val returnType = copyFuncDeclaration.descriptor.returnType!! val statements = body.statements - val irBlock = IrBlockImpl(startOffset, endOffset, returnType, null, statements) // Create IrBlock containing function statements. + val irBlock = IrInlineFunctionBody(startOffset, endOffset, returnType, null, statements) val parameterToExpression = expression.getArguments() // Build map parameter -> expression. val parametersTransformer = ParametersTransformer(parameterToExpression) @@ -56,14 +55,6 @@ internal class ParametersTransformer(val parameterToExpression: List