[JVM IR] Use append(Char) for 1-length string literals in string
templates and plus concatenations. This is slightly more efficient and mirrors the behavior of the non-IR backend for templates (but not for plus concatenations). #KT-36638 Fixed
This commit is contained in:
committed by
Dmitry Petrov
parent
bd9877cc62
commit
4234fa79c0
+14
-8
@@ -15,13 +15,12 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
|||||||
import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder
|
import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
|
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
|
||||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
|
||||||
import org.jetbrains.kotlin.ir.builders.irImplicitCast
|
|
||||||
import org.jetbrains.kotlin.ir.builders.irString
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
|
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||||
@@ -79,16 +78,23 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F
|
|||||||
private fun typeToAppendFunction(type: IrType): IrSimpleFunction =
|
private fun typeToAppendFunction(type: IrType): IrSimpleFunction =
|
||||||
appendFunctions[type] ?: defaultAppendFunction
|
appendFunctions[type] ?: defaultAppendFunction
|
||||||
|
|
||||||
// There is no special append or valueOf function for byte and short on the JVM.
|
private fun IrBuilderWithScope.normalizeArgument(expression: IrExpression): IrExpression =
|
||||||
private fun IrBuilderWithScope.widenIntegerType(expression: IrExpression): IrExpression =
|
|
||||||
if (expression.type.isByte() || expression.type.isShort()) {
|
if (expression.type.isByte() || expression.type.isShort()) {
|
||||||
|
// There is no special append or valueOf function for byte and short on the JVM.
|
||||||
irImplicitCast(expression, context.irBuiltIns.intType)
|
irImplicitCast(expression, context.irBuiltIns.intType)
|
||||||
|
} else if (expression is IrConst<*> && expression.kind == IrConstKind.String && (expression.value as String).length == 1) {
|
||||||
|
// PSI2IR generates const Strings for 1-length literals in string templates (e.g., the space between x and y in "$x $y").
|
||||||
|
// We want to use the more efficient `append(Char)` function in such cases. This mirrors the behavior of the non-IR backend.
|
||||||
|
//
|
||||||
|
// In addition, this also means `append(Char)` will be used for the space in the following case: `x + " " + y`. The non-IR
|
||||||
|
// backend will still use `append(String)` in this case.
|
||||||
|
irChar((expression.value as String)[0])
|
||||||
} else {
|
} else {
|
||||||
expression
|
expression
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JvmIrBuilder.callToString(expression: IrExpression): IrExpression {
|
private fun JvmIrBuilder.callToString(expression: IrExpression): IrExpression {
|
||||||
val argument = widenIntegerType(expression)
|
val argument = normalizeArgument(expression)
|
||||||
val argumentType = if (argument.type.isPrimitiveType()) argument.type else context.irBuiltIns.anyNType
|
val argumentType = if (argument.type.isPrimitiveType()) argument.type else context.irBuiltIns.anyNType
|
||||||
|
|
||||||
return irCall(backendContext.ir.symbols.typeToStringValueOfFunction(argumentType)).apply {
|
return irCall(backendContext.ir.symbols.typeToStringValueOfFunction(argumentType)).apply {
|
||||||
@@ -146,7 +152,7 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F
|
|||||||
else -> {
|
else -> {
|
||||||
var stringBuilder = irCall(constructor)
|
var stringBuilder = irCall(constructor)
|
||||||
for (arg in arguments) {
|
for (arg in arguments) {
|
||||||
val argument = widenIntegerType(arg)
|
val argument = normalizeArgument(arg)
|
||||||
val appendFunction = typeToAppendFunction(argument.type)
|
val appendFunction = typeToAppendFunction(argument.type)
|
||||||
stringBuilder = irCall(appendFunction).apply {
|
stringBuilder = irCall(appendFunction).apply {
|
||||||
dispatchReceiver = stringBuilder
|
dispatchReceiver = stringBuilder
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TODO KT-36638 Use 'java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;' when appending single character in JVM_IR
|
|
||||||
|
|
||||||
// KT-5016 wrong StringBuilder append method invoked
|
// KT-5016 wrong StringBuilder append method invoked
|
||||||
class kt5016 {
|
class kt5016 {
|
||||||
fun f1(name : String) : String {
|
fun f1(name : String) : String {
|
||||||
@@ -10,4 +7,6 @@ class kt5016 {
|
|||||||
|
|
||||||
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 2 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 2 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TODO KT-36638 Use 'java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;' when appending single character in JVM_IR
|
|
||||||
|
|
||||||
// KT-5016 wrong StringBuilder append method invoked
|
// KT-5016 wrong StringBuilder append method invoked
|
||||||
class kt5016int {
|
class kt5016int {
|
||||||
fun f1(num : Int) : String {
|
fun f1(num : Int) : String {
|
||||||
@@ -11,4 +8,6 @@ class kt5016int {
|
|||||||
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 0 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TODO KT-36638 Use 'java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;' when appending single character in JVM_IR
|
|
||||||
|
|
||||||
// KT-5016 wrong StringBuilder append method invoked
|
// KT-5016 wrong StringBuilder append method invoked
|
||||||
class kt5016intOrNull {
|
class kt5016intOrNull {
|
||||||
fun f1(num : Int?) : String {
|
fun f1(num : Int?) : String {
|
||||||
@@ -10,4 +7,6 @@ class kt5016intOrNull {
|
|||||||
|
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/Object;\)Ljava/lang/StringBuilder
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.toString
|
||||||
|
|||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun test(s: String, i: Int) = "${"x"}${s}${" "}${i}${"y"}"
|
||||||
|
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 5 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringInTemplate_2.kt
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun test(s: String, i: Int) = "x${s} ${i}y"
|
||||||
|
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 5 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
fun test(s: String, i: Int) = "x" + s + " " + i + "y"
|
||||||
|
|
||||||
|
// The IR is equivalent for this test and "useAppendCharForOneCharStringInTemplate*.kt" because there is an optimization for 1-length
|
||||||
|
// string literals in any string concatenation, whether using templates or + operator (see JvmStringConcatenationLowering).
|
||||||
|
// However, for the non-IR backend, `append(String)` will still be used for these 1-length strings.
|
||||||
|
|
||||||
|
// JVM_TEMPLATES
|
||||||
|
// 4 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
|
// 5 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
|
|
||||||
|
// JVM_IR_TEMPLATES
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(Ljava/lang/String;\)Ljava/lang/StringBuilder
|
||||||
|
// 1 INVOKEVIRTUAL java/lang/StringBuilder.append \(I\)Ljava/lang/StringBuilder
|
||||||
|
// 3 INVOKEVIRTUAL java/lang/StringBuilder.append \(C\)Ljava/lang/StringBuilder
|
||||||
|
// 5 INVOKEVIRTUAL java/lang/StringBuilder.append
|
||||||
@@ -4249,6 +4249,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
public void testStringPlus() throws Exception {
|
public void testStringPlus() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringInTemplate.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringInTemplate() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringInTemplate.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringInTemplate_2.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringInTemplate_2() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringInTemplate_2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringUsingPlus.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringUsingPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringUsingPlus.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/toArray")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/toArray")
|
||||||
|
|||||||
+15
@@ -4167,6 +4167,21 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
public void testStringPlus() throws Exception {
|
public void testStringPlus() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringInTemplate.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringInTemplate() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringInTemplate.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringInTemplate_2.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringInTemplate_2() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringInTemplate_2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("useAppendCharForOneCharStringUsingPlus.kt")
|
||||||
|
public void testUseAppendCharForOneCharStringUsingPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/stringOperations/useAppendCharForOneCharStringUsingPlus.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/toArray")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/toArray")
|
||||||
|
|||||||
Reference in New Issue
Block a user