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 602d02b8caa..8ecf67293a6 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,10 @@ internal class KonanLower(val context: Context) { fun lower(irFile: IrFile) { val phaser = PhaseManager(context) + // TODO: consider place of the pass. + phaser.phase(KonanPhase.LOWER_STRING_CONCAT) { + StringConcatenationLowering(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 8ab4548e3b7..116afa093c1 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 @@ -22,6 +22,7 @@ enum class KonanPhase(val description: String, /* ... ... */ AUTOBOX("Autoboxing of primitive types"), /* ... ... */ LOWER_ENUMS("Enum classes lowering"), /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"), + /* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt index f0ced1ebbab..6b86820abaf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.backend.konan.KonanPlatformConfigurator +import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.PlatformConfigurator @@ -31,7 +32,6 @@ class KonanBuiltIns: KotlinBuiltIns { constructor(storageManager: StorageManager, withModule: Boolean) : this(storageManager) { if (withModule) createBuiltInsModule() } - } object KonanPlatform : TargetPlatform("Konan") { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrBuilders.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrBuilders.kt index a1ace20a169..1c3ed8593be 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrBuilders.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrBuilders.kt @@ -3,12 +3,18 @@ package org.jetbrains.kotlin.backend.konan.ir import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.types.KotlinType -inline fun IrBlockBuilder.irLetSequence( +inline fun IrBuilderWithScope.irLetSequence( value: IrExpression, nameHint: String? = null, + startOffset: Int = this.startOffset, + endOffset: Int = this.endOffset, + origin: IrStatementOrigin? = null, + resultType: KotlinType? = null, body: IrBlockBuilder.(VariableDescriptor) -> Unit -): IrExpression = block { +): IrExpression = irBlock(startOffset, endOffset, origin, resultType) { val irTemporary = defineTemporary(value, nameHint) this.body(irTemporary) -} +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationLowering.kt new file mode 100644 index 00000000000..8ee8ae7545b --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationLowering.kt @@ -0,0 +1,102 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.signature2Descriptor +import org.jetbrains.kotlin.backend.konan.ir.irLetSequence +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType + + +/** + * This lowering pass replaces [IrStringConcatenation]s with StringBuilder appends. + */ +internal class StringConcatenationLowering(val context: Context) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(StringConcatenationTransformer(this)) + } +} + +private class StringConcatenationTransformer(val lower: StringConcatenationLowering) : IrElementTransformerVoid() { + + private val buildersStack = mutableListOf() + private val context = lower.context + private val builtIns = context.builtIns + + private val typesWithSpecialAppendFunction = + PrimitiveType.values().map { builtIns.getPrimitiveKotlinType(it) } + builtIns.stringType + + private val kotlinTextFqName = FqName.fromSegments(listOf("kotlin", "text")) + private val nameToString = Name.identifier("toString") + private val nameStringBuilder = Name.identifier("StringBuilder") + private val nameAppend = Name.identifier("append") + + private val classStringBuilder = builtIns.builtInsModule.getPackage(kotlinTextFqName). + memberScope.getContributedClassifier(nameStringBuilder, + NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + //TODO: calculate and pass string length to the constructor. + private val constructor = classStringBuilder.constructors.firstOrNull { + it.valueParameters.size == 0 + }!! + + private val toStringFunction = classStringBuilder.signature2Descriptor(nameToString)!! + private val defaultAppendFunction = + classStringBuilder.signature2Descriptor(nameAppend, arrayOf(builtIns.nullableAnyType))!! + + private val appendFunctions: Map = + typesWithSpecialAppendFunction.map { + it to classStringBuilder.signature2Descriptor(nameAppend, arrayOf(it)) + }.toMap() + + private fun typeToAppendFunction(type : KotlinType) : CallableDescriptor { + return appendFunctions[type]?:defaultAppendFunction + } + + override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression { + assert(!buildersStack.isEmpty()) + + val blockBuilder = buildersStack.last() + + return blockBuilder.irLetSequence( + value = blockBuilder.irCall(constructor), + startOffset = expression.startOffset, + endOffset = expression.endOffset, + resultType = expression.type) { stringBuilderImpl -> + expression.arguments.forEach { arg -> + val appendFunction = typeToAppendFunction(arg.type) + +irCall(appendFunction).apply { + dispatchReceiver = irGet(stringBuilderImpl) + putValueArgument(0, arg) + } + } + +irCall(toStringFunction).apply { + dispatchReceiver = irGet(stringBuilderImpl) + } + } + } + + override fun visitDeclaration(declaration: IrDeclaration): IrStatement { + with(declaration) { + buildersStack.add( + IrBlockBuilder(IrGeneratorContext(context.irBuiltIns), Scope(descriptor), startOffset, endOffset) + ) + transformChildrenVoid(this@StringConcatenationTransformer) + buildersStack.removeAt(buildersStack.lastIndex) + return this@with + } + } +} \ No newline at end of file