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 a70c3467067..2b5fc2ed07f 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 @@ -66,6 +66,8 @@ internal class SpecialDeclarationsFactory(val context: Context) { private val loweredEnums = mutableMapOf() private val ordinals = mutableMapOf>() + val loweredInlineFunctions = mutableSetOf() + object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index f07796bd033..0d7f7b86a2a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -131,7 +131,7 @@ internal val extractLocalClassesFromInlineBodies = namedIrModulePhase( internal val inlinePhase = namedIrModulePhase( lower = object : SameTypeCompilerPhase { override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState, context: Context, input: IrModuleFragment): IrModuleFragment { - FunctionInlining(context).run { + FunctionInlining(context, NativeInlineFunctionResolver(context)).run { input.files.forEach { lower(it) } } return input diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 7834d1d7d61..edc65c50c92 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -25,11 +25,7 @@ import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator -import org.jetbrains.kotlin.ir.util.SymbolTable -import org.jetbrains.kotlin.ir.util.addChild -import org.jetbrains.kotlin.ir.util.addFile -import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration @@ -320,6 +316,7 @@ internal val linkerPhase = konanUnitPhase( internal val allLoweringsPhase = namedIrModulePhase( name = "IrLowering", description = "IR Lowering", + // TODO: The lowerings before inlinePhase should be aligned with [NativeInlineFunctionResolver.kt] lower = removeExpectDeclarationsPhase then stripTypeAliasDeclarationsPhase then lowerBeforeInlinePhase then @@ -392,6 +389,7 @@ internal val dependenciesLowerPhase = SameTypeNamedPhaseWrapper( ?: return@forEach input.files += libModule.files } + input.files += files return input diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt new file mode 100644 index 00000000000..9a7e6c0dbc8 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.backend.common.lower.* +import org.jetbrains.kotlin.backend.common.lower.inline.DefaultInlineFunctionResolver +import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering +import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering +import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLambdasLowering +import org.jetbrains.kotlin.backend.common.runPostfix +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.util.dump + +// TODO: This is a bit hacky. Think about adopting persistent IR ideas. +internal class NativeInlineFunctionResolver(override val context: Context) : DefaultInlineFunctionResolver(context) { + override fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction { + val function = super.getFunctionDeclaration(symbol) + val body = function.body ?: return function + + if (function in context.specialDeclarationsFactory.loweredInlineFunctions) + return function + + context.specialDeclarationsFactory.loweredInlineFunctions.add(function) + + PreInlineLowering(context).lower(body, function) + + ArrayConstructorLowering(context).lower(body, function) + + NullableFieldsForLateinitCreationLowering(context).lowerWithLocalDeclarations(function) + NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(function) + LateinitUsageLowering(context).lower(body, function) + + SharedVariablesLowering(context).lower(body, function) + + LocalClassesInInlineLambdasLowering(context).lower(body, function) + + if (context.llvmModuleSpecification.containsDeclaration(function)) { + // Do not extract local classes off of inline functions from cached libraries. + LocalClassesInInlineFunctionsLowering(context).lower(body, function) + LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, function) + } + + return function + } + + private fun DeclarationTransformer.lowerWithLocalDeclarations(function: IrFunction) { + if (runPostfix(true).transformFlat(function) != null) + error("Unexpected transformation of function ${function.dump()}") + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt index 2328b9222b8..6954dfabf75 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt @@ -5,11 +5,12 @@ package org.jetbrains.kotlin.backend.konan.lower -import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanConfigKeys -import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +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.expressions.impl.IrCompositeImpl @@ -20,15 +21,15 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid * This pass runs before inlining and performs the following additional transformations over some operations: * - Assertion call removal. */ -internal class PreInlineLowering(val context: Context) : FileLoweringPass { +internal class PreInlineLowering(val context: Context) : BodyLoweringPass { private val symbols get() = context.ir.symbols private val asserts = symbols.asserts private val enableAssertions = context.config.configuration.getBoolean(KonanConfigKeys.ENABLE_ASSERTIONS) - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrBuildingTransformer(context) { + override fun lower(irBody: IrBody, container: IrDeclaration) { + irBody.transformChildrenVoid(object : IrBuildingTransformer(context) { override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 9e02c83ad92..b1722d7e142 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3302,6 +3302,18 @@ linkTest("inline_defaultArgs_linkTest") { lib = "codegen/inline/defaultArgs_linkTest_lib.kt" } +linkTest("inline_sharedVar_linkTest") { + goldValue = "6\n" + source = "codegen/inline/sharedVar_linkTest_main.kt" + lib = "codegen/inline/sharedVar_linkTest_lib.kt" +} + +linkTest("inline_lateinitProperty_linkTest") { + goldValue = "OK\n" + source = "codegen/inline/lateinitProperty_linkTest_main.kt" + lib = "codegen/inline/lateinitProperty_linkTest_lib.kt" +} + def generateWithSpaceDefFile() { def mapOption = "--Map \"${buildDir}/cutom map.map\"" if (isAppleTarget(project)) { diff --git a/backend.native/tests/codegen/inline/lateinitProperty_linkTest_lib.kt b/backend.native/tests/codegen/inline/lateinitProperty_linkTest_lib.kt new file mode 100644 index 00000000000..abfed1ee210 --- /dev/null +++ b/backend.native/tests/codegen/inline/lateinitProperty_linkTest_lib.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package a + +fun IntArray.forEachNoInline(block: (Int) -> Unit) = this.forEach { block(it) } + +inline fun foo(values: IntArray, crossinline block: (Int, Int, Int) -> Int): Int { + val o = object { + lateinit var s: String + var x: Int = 42 + } + values.forEachNoInline { + o.x = block(o.x, o.s.length, it) + } + return o.x +} \ No newline at end of file diff --git a/backend.native/tests/codegen/inline/lateinitProperty_linkTest_main.kt b/backend.native/tests/codegen/inline/lateinitProperty_linkTest_main.kt new file mode 100644 index 00000000000..d4b1cb03c17 --- /dev/null +++ b/backend.native/tests/codegen/inline/lateinitProperty_linkTest_main.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +import a.* + +fun main() { + try { + val res = foo(intArrayOf(1, 2, 3)) { x, y, z -> x + y - z } + println(res) + } catch (t: UninitializedPropertyAccessException) { + println("OK") + } +} \ No newline at end of file diff --git a/backend.native/tests/codegen/inline/sharedVar_linkTest_lib.kt b/backend.native/tests/codegen/inline/sharedVar_linkTest_lib.kt new file mode 100644 index 00000000000..e27fc6f994d --- /dev/null +++ b/backend.native/tests/codegen/inline/sharedVar_linkTest_lib.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package a + +fun IntArray.forEachNoInline(block: (Int) -> Unit) = this.forEach { block(it) } + +inline fun fold(initial: Int, values: IntArray, crossinline block: (Int, Int) -> Int): Int { + var res = initial + values.forEachNoInline { + res = block(res, it) + } + return res +} \ No newline at end of file diff --git a/backend.native/tests/codegen/inline/sharedVar_linkTest_main.kt b/backend.native/tests/codegen/inline/sharedVar_linkTest_main.kt new file mode 100644 index 00000000000..f5d4b0788a1 --- /dev/null +++ b/backend.native/tests/codegen/inline/sharedVar_linkTest_main.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +import a.* + +fun main() { + println(fold(0, intArrayOf(1, 2, 3)) { x, y -> x + y }) +} \ No newline at end of file