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 fc2f3c73d05..3aa0e4fd2dd 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 @@ -17,6 +17,9 @@ internal class KonanLower(val context: Context) { fun lower(irFile: IrFile) { val phaser = PhaseManager(context) + phaser.phase(KonanPhase.LOWER_INLINE) { + FunctionInlining(context).inline(irFile) + } phaser.phase(KonanPhase.LOWER_ENUMS) { EnumClassLowering(context).run(irFile) } @@ -48,12 +51,5 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.AUTOBOX) { Autoboxing(context).lower(irFile) } - phaser.phase(KonanPhase.LOWER_INLINE) { - //FunctionInlining(context).inline(irFile) - } - - phaser.phase(KonanPhase.LOWER_INTEROP) { - InteropLowering(context).runOnFilePostfix(irFile) - } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index e0a13aaab54..494abc7943c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -25,7 +25,7 @@ internal abstract class PlatformFlags(val distribution: Distribution) { } -internal open class MacOSPlatform(distribution: Distribution) +internal open class MacOSPlatform(distribution: Distribution) : PlatformFlags(distribution) { override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx") @@ -34,7 +34,7 @@ internal open class MacOSPlatform(distribution: Distribution) override val linkerOptimizationFlags = properties.propertyList("linkerOptimizationFlags.osx") override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx") - override val linker = "${distribution.sysRoot}/usr/bin/ld" + override val linker = "${distribution.sysRoot}/usr/bin/ld" open val arch = properties.propertyString("arch.osx")!! open val osVersionMin = properties.propertyList("osVersionMin.osx") @@ -48,8 +48,8 @@ internal open class MacOSPlatform(distribution: Distribution) return mutableListOf(linker, "-demangle") + if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distribution.libLTO) else {listOf()} + listOf( "-dynamic", "-arch", arch) + - osVersionMin + - listOf("-syslibroot", "$targetSysRoot", + osVersionMin + + listOf("-syslibroot", "$targetSysRoot", "-o", executable) + objectFiles + if (optimize) linkerOptimizationFlags else {listOf()} + @@ -58,7 +58,7 @@ internal open class MacOSPlatform(distribution: Distribution) } } -internal class IPhoneOSfromMacOSPlatform(distribution: Distribution) +internal class IPhoneOSfromMacOSPlatform(distribution: Distribution) : MacOSPlatform(distribution) { override val arch = properties.propertyString("arch.osx-ios")!! @@ -67,7 +67,7 @@ internal class IPhoneOSfromMacOSPlatform(distribution: Distribution) override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios")!!}" } -internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution) +internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution) : MacOSPlatform(distribution) { override val arch = properties.propertyString("arch.osx-ios-sim")!! @@ -75,7 +75,7 @@ internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution) override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios-sim") override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios-sim")!!}" } -internal class LinuxPlatform(distribution: Distribution) +internal class LinuxPlatform(distribution: Distribution) : PlatformFlags(distribution) { override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.linux") @@ -84,7 +84,7 @@ internal class LinuxPlatform(distribution: Distribution) override val linkerOptimizationFlags = properties.propertyList("linkerOptimizationFlags.linux") override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.linux") - override val linker = "${distribution.sysRoot}/../bin/ld.gold" + override val linker = "${distribution.sysRoot}/../bin/ld.gold" val pluginOptimizationFlags = properties.propertyList("pluginOptimizationFlags.linux") @@ -122,7 +122,7 @@ internal class LinkStage(val context: Context) { val config = context.config.configuration val targetManager = TargetManager(config) - private val distribution = + private val distribution = Distribution(context.config.configuration) private val properties = distribution.properties @@ -131,7 +131,7 @@ internal class LinkStage(val context: Context) { KonanTarget.MACBOOK -> when (targetManager.current) { KonanTarget.IPHONE_SIM -> IPhoneSimulatorFromMacOSPlatform(distribution) - KonanTarget.IPHONE + KonanTarget.IPHONE -> IPhoneOSfromMacOSPlatform(distribution) KonanTarget.MACBOOK -> MacOSPlatform(distribution) @@ -209,7 +209,7 @@ internal class LinkStage(val context: Context) { fun linkStage() { context.log("# Compiler root: ${distribution.konanHome}") - val bitcodeFiles = listOf(emitted, distribution.start, distribution.runtime, + val bitcodeFiles = listOf(emitted, distribution.start, distribution.runtime, distribution.launcher) + libraries val objectFiles = if (optimize) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 5e0ac5f26fa..bfffb02c174 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -17,6 +17,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { // TODO: remove, to make CodeGenerator descriptor-agnostic. var constructedClass: ClassDescriptor? = null val vars = VariableManager(this) + var functionDescriptor: FunctionDescriptor? = null private var returnSlot: LLVMValueRef? = null private var slotsPhi: LLVMValueRef? = null private var slotCount = 0 @@ -40,6 +41,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { if (descriptor is ConstructorDescriptor) { constructedClass = descriptor.constructedClass } + functionDescriptor = descriptor } fun prologue(function:LLVMValueRef, returnType:LLVMTypeRef) { 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 bb27b9f83fb..865a49f17a6 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 @@ -33,9 +33,9 @@ internal fun emitLLVM(context: Context) { val irModule = context.irModule!! // Note that we don't set module target explicitly. - // It is determined by the target of runtime.bc + // It is determined by the target of runtime.bc // (see Llvm class in ContextUtils) - // Which in turn is determined by the clang flags + // Which in turn is determined by the clang flags // used to compile runtime.bc. val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose context.llvmModule = llvmModule @@ -1511,10 +1511,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } override fun genReturn(target: CallableDescriptor, value: LLVMValueRef?) { - if (KotlinBuiltIns.isUnit(inlineBody.type) == false) { - codegen.assignPhis(getResult()!! to value!!) + if (target == codegen.functionDescriptor) { // It is "non local return". + super.genReturn(target, value) // Generate real "return". + return } - codegen.br(getExit()!!) + // It is local return. + if (KotlinBuiltIns.isUnit(inlineBody.type) == false) { // If function returns more then "unit" + codegen.assignPhis(getResult()!! to value!!) // Assign return value to result PHI node. + } + codegen.br(getExit()!!) // Generate branch on exit block. } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt new file mode 100644 index 00000000000..e69de29bb2d 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 533802e0b84..6fc298c39eb 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,16 +1,22 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.isFunctionInvoke 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.builders.Scope 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.IrContainerExpressionBase +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.util.DeepCopyIrTree import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.types.KotlinType //-----------------------------------------------------------------------------// @@ -20,27 +26,96 @@ 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. + fun isLambda(value: IrExpression) : Boolean { + if (value !is IrContainerExpressionBase) return false + if (value.origin != IrStatementOrigin.LAMBDA) return false + return true + } + //-------------------------------------------------------------------------// + + fun getLambdaStatements(value: IrExpression) : MutableList { + val statements = (value as IrContainerExpressionBase).statements + val lambdaFunction = statements[0] as IrFunction + val lambdaBody = lambdaFunction.body as IrBlockBody + return lambdaBody.statements + } + + //-------------------------------------------------------------------------// + + fun getLambdaReturnType(value: IrExpression) : KotlinType { + val statements = (value as IrContainerExpressionBase).statements + val lambdaFunction = statements[0] as IrFunction + return lambdaFunction.descriptor.returnType!! + } + + //-------------------------------------------------------------------------// + + fun evaluateParameters(irCall: IrCall, + statements: MutableList): List> { + + val scope = Scope(irCall.descriptor as FunctionDescriptor) + val parametersOld = irCall.getArguments() // Create map inline_function_parameter -> containing_function_expression. + val parametersNew = parametersOld.map { + val parameter = it.first + val expression = it.second + if (expression is IrGetValue) return@map it // There is nothing to evaluate. + if (isLambda(expression)) { // The expression is lambda. + val inlineFunctionBody = inlineLambda(expression) // Create IrInlineFunctionBody to replace this parameter. + return@map parameter to inlineFunctionBody + } + + val newVar = scope.createTemporaryVariable(expression, "inline", false) // Create new variable and init it with the expression. + statements.add(0, newVar) // Add initialization of the new variable in statement list. + + val getVal = IrGetValueImpl(0, 0, newVar.descriptor) // Create new IR element representing access the new variable. + parameter to getVal // Parameter will be replaced with the new variable. + } + return parametersNew + } + + //-------------------------------------------------------------------------// + + fun inlineFunction(irCall: IrCall): IrBlock { + + val functionDescriptor = irCall.descriptor as FunctionDescriptor val functionDeclaration = context.ir.originalModuleIndex .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 copyFuncDeclaration = functionDeclaration!!.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function. - val body = copyFuncDeclaration.body!! as IrBlockBody val startOffset = copyFuncDeclaration.startOffset val endOffset = copyFuncDeclaration.endOffset val returnType = copyFuncDeclaration.descriptor.returnType!! - val statements = body.statements + val inlineBody = copyFuncDeclaration.body!! as IrBlockBody + val statements = inlineBody.statements + val parameters = evaluateParameters(irCall, statements) // Evaluate parameters representing expression. val irBlock = IrInlineFunctionBody(startOffset, endOffset, returnType, null, statements) - val parameterToExpression = expression.getArguments() // Build map parameter -> expression. - val parametersTransformer = ParametersTransformer(parameterToExpression) - irBlock.accept(parametersTransformer, null) // Replace parameters with expression. + val transformer = ParametersTransformer(parameters) + irBlock.accept(transformer, null) // Replace parameters with expression. + return irBlock + } - return irBlock // Return newly created IrBlock instead of IrCall. + //-------------------------------------------------------------------------// + + fun inlineLambda(value: IrExpression): IrBlock { + + val lambdaStatements = getLambdaStatements(value) + val lambdaReturnType = getLambdaReturnType(value) + val irBlock = IrInlineFunctionBody(0, 0, lambdaReturnType, null, lambdaStatements) + + return irBlock + } + + //-------------------------------------------------------------------------// + + 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.originalModuleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor. + if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module. + return inlineFunction(expression) // Return newly created IrBlock instead of IrCall. } //-------------------------------------------------------------------------// @@ -56,12 +131,27 @@ internal class ParametersTransformer(val parameterToExpression: List Int): Int { + return i2 + body() +} + +fun bar(i1: Int): Int { + return foo(i1) { 1 } +} + +fun main(args: Array) { + println(bar(1).toString()) +} diff --git a/backend.native/tests/codegen/inline/inline4.kt b/backend.native/tests/codegen/inline/inline4.kt index 04f6a475291..8424ebcdded 100644 --- a/backend.native/tests/codegen/inline/inline4.kt +++ b/backend.native/tests/codegen/inline/inline4.kt @@ -9,5 +9,5 @@ fun bar(i1: Int, i2: Int): Int { } fun main(args: Array) { - println(bar(1, 8).toString()) + println(bar(3, 8).toString()) } diff --git a/backend.native/tests/codegen/inline/inline5.kt b/backend.native/tests/codegen/inline/inline5.kt new file mode 100644 index 00000000000..7e921c8d83b --- /dev/null +++ b/backend.native/tests/codegen/inline/inline5.kt @@ -0,0 +1,13 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i2: Int, body: () -> Int): Int { + return i2 + body() +} + +fun bar(i1: Int): Int { + return foo(i1) { return 33 } +} + +fun main(args: Array) { + println(bar(1).toString()) +} + diff --git a/backend.native/tests/codegen/inline/inline6.kt b/backend.native/tests/codegen/inline/inline6.kt new file mode 100644 index 00000000000..4f6efa66604 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline6.kt @@ -0,0 +1,17 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(body: () -> Unit) { + println("hello1") + body() + println("hello4") +} + +fun bar() { + foo { + println("hello2") + println("hello3") + } +} + +fun main(args: Array) { + bar() +} diff --git a/backend.native/tests/codegen/inline/inline7.kt b/backend.native/tests/codegen/inline/inline7.kt new file mode 100644 index 00000000000..6bb5dd067c2 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline7.kt @@ -0,0 +1,14 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(vararg args: Int) { + for (a in args) { + println(a.toString()) + } +} + +fun bar() { + foo(1, 2, 3) +} + +fun main(args: Array) { + bar() +} diff --git a/backend.native/tests/codegen/inline/inline8.kt b/backend.native/tests/codegen/inline/inline8.kt new file mode 100644 index 00000000000..714ae940700 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline8.kt @@ -0,0 +1,12 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i3: Int, i4: Int) : Int { + return i3 + i3 + i4 +} + +fun bar(i1: Int, i2: Int) : Int { + return foo(i1 + i2, 2) +} + +fun main(args: Array) { + println(bar(1, 2).toString()) +} diff --git a/backend.native/tests/codegen/inline/inline9.kt b/backend.native/tests/codegen/inline/inline9.kt new file mode 100644 index 00000000000..bd33c725059 --- /dev/null +++ b/backend.native/tests/codegen/inline/inline9.kt @@ -0,0 +1,17 @@ +@Suppress("NOTHING_TO_INLINE") +inline fun foo(i3: Int, i4: Int): Int { + return i3 + i3 + i4 +} + +fun quiz(i: Int) : Int { + println("hello") + return i + 1 +} + +fun bar(i1: Int, i2: Int): Int { + return foo(quiz(i1), i2) +} + +fun main(args: Array) { + println(bar(1, 2).toString()) +}