Reuse existing StringBuilder when appending concatenation
^KT-24949: Fixed
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java
index f963b8d..6914ea5 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java
@@ -49280,6 +49280,12 @@
}
@Test
+ @TestMetadata("kt24949.kt")
+ public void testKt24949() throws Exception {
+ runTest("compiler/testData/codegen/box/strings/kt24949.kt");
+ }
+
+ @Test
@TestMetadata("kt2592.kt")
public void testKt2592() throws Exception {
runTest("compiler/testData/codegen/box/strings/kt2592.kt");
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java
index 302f63f..697e2de 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java
@@ -49280,6 +49280,12 @@
}
@Test
+ @TestMetadata("kt24949.kt")
+ public void testKt24949() throws Exception {
+ runTest("compiler/testData/codegen/box/strings/kt24949.kt");
+ }
+
+ @Test
@TestMetadata("kt2592.kt")
public void testKt2592() throws Exception {
runTest("compiler/testData/codegen/box/strings/kt2592.kt");
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt
index ead6dab..9eccf90 100644
--- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt
+++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt
@@ -23,6 +23,7 @@
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
+import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -173,7 +174,7 @@
return appendAnyNFunction
}
- override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
+ private fun handleStringConcatenation(expression: IrStringConcatenation, stringBuilder: IrExpression): IrExpression {
expression.transformChildrenVoid(this)
return context.createJvmIrBuilder(currentScope!!, expression).run {
// When `String.plus(Any?)` is invoked with receiver of platform type String or String with enhanced nullability, this could
@@ -191,7 +192,7 @@
arguments.size < MAX_STRING_CONCAT_DEPTH -> {
irCall(toStringFunction).apply {
- dispatchReceiver = appendWindow(arguments, irCall(constructor))
+ dispatchReceiver = appendWindow(arguments, stringBuilder)
}
}
@@ -205,7 +206,7 @@
// tmp.toString()
// }
irBlock {
- val tmpStringBuilder = irTemporary(irCall(constructor))
+ val tmpStringBuilder = irTemporary(stringBuilder)
val argsWindowed =
arguments.windowed(
size = MAX_STRING_CONCAT_DEPTH,
@@ -222,6 +223,27 @@
}
}
+ override fun visitCall(expression: IrCall): IrExpression {
+ // Optimization: don't create a new StringBuilder when appending concatenation to existing StringBuilder. See KT-24949
+ if (isAppendOfStringConcatenation(expression)) {
+ return handleStringConcatenation(expression.getValueArgument(0) as IrStringConcatenation, expression.dispatchReceiver!!)
+ }
+ return super.visitCall(expression)
+ }
+
+ private fun isAppendOfStringConcatenation(expression: IrCall): Boolean {
+ return isAppendStringFunction(expression.symbol.owner) && expression.getValueArgument(0) is IrStringConcatenation
+ }
+
+ private fun isAppendStringFunction(function: IrSimpleFunction): Boolean {
+ return stringBuilder.fqNameWhenAvailable == function.dispatchReceiverParameter?.type?.classFqName
+ && function.isAppendFunction() && function.valueParameters[0].type.classOrNull == context.irBuiltIns.stringClass
+ }
+
+ override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
+ return handleStringConcatenation(expression, context.createJvmIrBuilder(currentScope!!, expression).irCall(constructor))
+ }
+
private fun JvmIrBuilder.appendWindow(arguments: List<IrExpression>, stringBuilder0: IrExpression): IrExpression {
return arguments.fold(stringBuilder0) { stringBuilder, arg ->
val argument = normalizeArgument(arg)
diff --git a/compiler/testData/codegen/box/strings/kt24949.kt b/compiler/testData/codegen/box/strings/kt24949.kt
new file mode 100644
index 0000000..9f15484
--- /dev/null
+++ b/compiler/testData/codegen/box/strings/kt24949.kt
@@ -0,0 +1,24 @@
+// TARGET_BACKEND: JVM_IR
+
+fun testShortConcat(): String {
+ val c = "a"
+ val sb = StringBuilder()
+ sb.append(c + c + c)
+ return sb.toString()
+}
+
+fun testLongConcat(): String {
+ val c = "a"
+ val sb = StringBuilder()
+ sb.append(c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c + c)
+ return sb.toString()
+}
+
+fun box(): String {
+ if (testShortConcat() != "aaa") return "Fail 1"
+ if (testLongConcat() != "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") return "Fail 2"
+ return "OK"
+}
+
+// CHECK_BYTECODE_TEXT
+// 2 NEW java/lang/StringBuilder
\ No newline at end of file
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java
index 0b67d21..af567f4 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java
@@ -49280,6 +49280,12 @@
}
@Test
+ @TestMetadata("kt24949.kt")
+ public void testKt24949() throws Exception {
+ runTest("compiler/testData/codegen/box/strings/kt24949.kt");
+ }
+
+ @Test
@TestMetadata("kt2592.kt")
public void testKt2592() throws Exception {
runTest("compiler/testData/codegen/box/strings/kt2592.kt");