Remove Concat and StringPlus intrinsics in favor of IrStringConcatenation.

This commit is contained in:
Steven Schäfer
2019-04-04 16:09:49 +02:00
committed by max-kammerer
parent 26c9d69252
commit f2392a6a28
20 changed files with 192 additions and 120 deletions
@@ -8,13 +8,15 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.types.isStringClassType
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -73,11 +75,13 @@ class FlattenStringConcatenationLowering(val context: CommonBackendContext) : Fi
return when (expression) {
is IrStringConcatenation -> true
is IrCall -> {
val dispatchReceiver = expression.dispatchReceiver
dispatchReceiver != null &&
dispatchReceiver.type.isString() &&
expression.symbol.owner.name == PLUS_NAME &&
expression.type.isString() &&
val function = expression.symbol.owner
val receiver = expression.dispatchReceiver ?: expression.extensionReceiver
receiver != null &&
receiver.type.isStringClassType() &&
function.getPackageFragment()?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
function.name == PLUS_NAME &&
expression.type.isStringClassType() &&
expression.valueArgumentsCount == 1
}
else -> false
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.descriptors.propertyIfAccessor
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
import org.jetbrains.kotlin.backend.jvm.intrinsics.Not
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
@@ -29,9 +30,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -774,12 +773,28 @@ class ExpressionCodegen(
override fun visitStringConcatenation(expression: IrStringConcatenation, data: BlockInfo): PromisedValue {
expression.markLineNumber(startOffset = true)
when (expression.arguments.size) {
0 -> mv.aconst("")
1 -> {
val arity = expression.arguments.size
when {
arity == 0 -> mv.aconst("")
arity == 1 -> {
// Convert single arg to string.
val type = expression.arguments[0].accept(this, data).materialized.type
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper).put(expression.asmType, mv)
if (!expression.arguments[0].type.isString())
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper).put(expression.asmType, mv)
}
arity == 2 && expression.arguments[0].type.isStringClassType() -> {
// Call the stringPlus intrinsic
expression.arguments.forEach {
val type = it.accept(this, data).materialized.type
if (type.sort != Type.OBJECT)
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper).put(expression.asmType, mv)
}
mv.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME,
"stringPlus",
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;",
false
)
}
else -> {
// Use StringBuilder to concatenate.
@@ -1,70 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class Concat : IntrinsicMethod() {
override fun toCallable(
expression: IrMemberAccessExpression,
signature: JvmMethodSignature,
context: JvmBackendContext
): IrIntrinsicFunction {
val argsTypes = expression.receiverAndArgs().asmTypes(context).toMutableList()
argsTypes[0] = AsmTypes.JAVA_STRING_TYPE
return object : IrIntrinsicFunction(expression, signature, context, argsTypes) {
override fun genInvokeInstruction(v: InstructionAdapter) {
AsmUtil.genInvokeAppendMethod(v, argsTypes[1], null)
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
}
override fun invoke(
v: InstructionAdapter,
codegen: org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen,
data: BlockInfo
): StackValue {
v.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
v.dup()
return super.invoke(v, codegen, data)
}
override fun genArg(
expression: IrExpression,
codegen: org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen,
index: Int,
data: BlockInfo
) {
super.genArg(expression, codegen, index, data)
if (index == 0) {
codegen.mv.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false)
}
}
}
}
}
@@ -55,7 +55,6 @@ public class IntrinsicMethods {
private static final IteratorNext ITERATOR_NEXT = new IteratorNext();
private static final ArraySet ARRAY_SET = new ArraySet();
private static final ArrayGet ARRAY_GET = new ArrayGet();
private static final StringPlus STRING_PLUS = new StringPlus();
private static final ToString TO_STRING = new ToString();
private static final Clone CLONE = new Clone();
@@ -119,13 +118,11 @@ public class IntrinsicMethods {
declareIntrinsicFunction(FQ_NAMES._boolean, "not", 0, new Not());
declareIntrinsicFunction(FQ_NAMES.string, "plus", 1, new Concat());
declareIntrinsicFunction(FQ_NAMES.string, "get", 1, new StringGetChar());
declareIntrinsicFunction(FQ_NAMES.cloneable, "clone", 0, CLONE);
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KotlinBuiltIns.FQ_NAMES.any, "toString", 0, TO_STRING);
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KotlinBuiltIns.FQ_NAMES.string, "plus", 1, STRING_PLUS);
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, null, "arrayOfNulls", 1, new NewArray());
for (PrimitiveType type : PrimitiveType.values()) {
@@ -92,7 +92,7 @@ open class IrIntrinsicFunction(
}
}
open fun genArg(expression: IrExpression, codegen: ExpressionCodegen, index: Int, data: BlockInfo) {
private fun genArg(expression: IrExpression, codegen: ExpressionCodegen, index: Int, data: BlockInfo) {
codegen.gen(expression, argsTypes[index], data)
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
class StringPlus : IntrinsicMethod() {
override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
return IrIntrinsicFunction.create(expression, signature, context) {
it.invokestatic("kotlin/jvm/internal/Intrinsics", "stringPlus",
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;", false)
}
}
}
@@ -16,9 +16,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName
private fun IrType.isNotNullClassType(fqName: FqNameUnsafe) = isClassType(fqName, hasQuestionMark = false)
private fun IrType.isNullableClassType(fqName: FqNameUnsafe) = isClassType(fqName, hasQuestionMark = true)
private fun IrType.isClassType(fqName: FqNameUnsafe, hasQuestionMark: Boolean): Boolean {
private fun IrType.isClassType(fqName: FqNameUnsafe, hasQuestionMark: Boolean? = null): Boolean {
if (this !is IrSimpleType) return false
if (this.hasQuestionMark != hasQuestionMark) return false
if (hasQuestionMark != null && this.hasQuestionMark != hasQuestionMark) return false
val classSymbol = this.classifier as? IrClassSymbol ?: return false
return classFqNameEquals(classSymbol, fqName)
}
@@ -37,6 +37,7 @@ fun IrType.isNullableAny(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAME
fun IrType.isString(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.string)
fun IrType.isNullableString(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES.string)
fun IrType.isStringClassType(): Boolean = isClassType(KotlinBuiltIns.FQ_NAMES.string)
fun IrType.isArray(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.array)
fun IrType.isNullableArray(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES.array)
fun IrType.isCollection(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.collection.toUnsafe())
@@ -0,0 +1,18 @@
fun f(s: String?, t: String): String {
return s.plus(t)
}
fun g(s: String, t: Any?): String {
return "$s$t"
}
fun h(s: String, t: Any?): String {
return s + t
}
fun box(): String {
if (f("O", "K") != "OK") return "Fail 1"
if (g("O", "K") != "OK") return "Fail 2"
if (h("O", "K") != "OK") return "Fail 3"
return "OK"
}
@@ -0,0 +1,8 @@
fun f(s: String?): String {
return "$s"
}
fun box(): String {
if (f(null) != "null") return "Fail"
return "OK"
}
@@ -0,0 +1,9 @@
operator fun String?.plus(p: String): String {
return "" + this
}
fun test(a: String?, b: String): String {
return a + b
}
fun box() = test("OK", " Fail")
@@ -4,7 +4,7 @@
// - In argument to 2nd call to foo() inside string literal
fun test(s1: String, s2: String, s3: String): String {
fun foo(s: String) = s
return "foo: " + foo(s1 + s2 + " ${foo("\${s3.length} = ${s3.length}")}")
return "foo: " + foo(s1 + s2 + " ${foo("\${s3.length} = ${s3.length} ")}") + " "
}
// 3 NEW java/lang/StringBuilder
@@ -0,0 +1,8 @@
// IGNORE_BACKEND: JVM
fun f(s: String) = "$s"
fun g(s: String?) = "$s"
// 1 valueOf
// 0 NEW java/lang/StringBuilder
@@ -0,0 +1,17 @@
// IGNORE_BACKEND: JVM
fun f(s: String?, t: String): String {
return s.plus(t)
}
fun g(s: String, t: Any?): String {
return "$s$t"
}
fun h(s: String, t: Any?): String {
return s + t
}
// 0 valueOf
// 0 NEW java/lang/StringBuilder
// 3 INVOKESTATIC kotlin/jvm/internal/Intrinsics.stringPlus
@@ -23565,6 +23565,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/strings/rawStringsWithManyQuotes.kt");
}
@TestMetadata("simpleStringPlus.kt")
public void testSimpleStringPlus() throws Exception {
runTest("compiler/testData/codegen/box/strings/simpleStringPlus.kt");
}
@TestMetadata("singleConcatNullable.kt")
public void testSingleConcatNullable() throws Exception {
runTest("compiler/testData/codegen/box/strings/singleConcatNullable.kt");
}
@TestMetadata("stringBuilderAppend.kt")
public void testStringBuilderAppend() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringBuilderAppend.kt");
@@ -23574,6 +23584,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testStringPlusOnlyWorksOnString() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOnlyWorksOnString.kt");
}
@TestMetadata("stringPlusOverride.kt")
public void testStringPlusOverride() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/super")
@@ -3496,6 +3496,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
public void testPrimitivesAsStringTemplates() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/primitivesAsStringTemplates.kt");
}
@TestMetadata("singleConcat.kt")
public void testSingleConcat() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/singleConcat.kt");
}
@TestMetadata("stringPlus.kt")
public void testStringPlus() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/unsignedTypes")
@@ -23565,6 +23565,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/strings/rawStringsWithManyQuotes.kt");
}
@TestMetadata("simpleStringPlus.kt")
public void testSimpleStringPlus() throws Exception {
runTest("compiler/testData/codegen/box/strings/simpleStringPlus.kt");
}
@TestMetadata("singleConcatNullable.kt")
public void testSingleConcatNullable() throws Exception {
runTest("compiler/testData/codegen/box/strings/singleConcatNullable.kt");
}
@TestMetadata("stringBuilderAppend.kt")
public void testStringBuilderAppend() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringBuilderAppend.kt");
@@ -23574,6 +23584,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
public void testStringPlusOnlyWorksOnString() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOnlyWorksOnString.kt");
}
@TestMetadata("stringPlusOverride.kt")
public void testStringPlusOverride() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/super")
@@ -23570,6 +23570,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/strings/rawStringsWithManyQuotes.kt");
}
@TestMetadata("simpleStringPlus.kt")
public void testSimpleStringPlus() throws Exception {
runTest("compiler/testData/codegen/box/strings/simpleStringPlus.kt");
}
@TestMetadata("singleConcatNullable.kt")
public void testSingleConcatNullable() throws Exception {
runTest("compiler/testData/codegen/box/strings/singleConcatNullable.kt");
}
@TestMetadata("stringBuilderAppend.kt")
public void testStringBuilderAppend() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringBuilderAppend.kt");
@@ -23579,6 +23589,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testStringPlusOnlyWorksOnString() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOnlyWorksOnString.kt");
}
@TestMetadata("stringPlusOverride.kt")
public void testStringPlusOverride() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/super")
@@ -3506,6 +3506,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
public void testPrimitivesAsStringTemplates() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/primitivesAsStringTemplates.kt");
}
@TestMetadata("singleConcat.kt")
public void testSingleConcat() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/singleConcat.kt");
}
@TestMetadata("stringPlus.kt")
public void testStringPlus() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/stringOperations/stringPlus.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/unsignedTypes")
@@ -17960,6 +17960,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/strings/rawStringsWithManyQuotes.kt");
}
@TestMetadata("simpleStringPlus.kt")
public void testSimpleStringPlus() throws Exception {
runTest("compiler/testData/codegen/box/strings/simpleStringPlus.kt");
}
@TestMetadata("singleConcatNullable.kt")
public void testSingleConcatNullable() throws Exception {
runTest("compiler/testData/codegen/box/strings/singleConcatNullable.kt");
}
@TestMetadata("stringBuilderAppend.kt")
public void testStringBuilderAppend() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringBuilderAppend.kt");
@@ -17969,6 +17979,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
public void testStringPlusOnlyWorksOnString() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOnlyWorksOnString.kt");
}
@TestMetadata("stringPlusOverride.kt")
public void testStringPlusOverride() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/super")
@@ -19105,6 +19105,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/strings/rawStringsWithManyQuotes.kt");
}
@TestMetadata("simpleStringPlus.kt")
public void testSimpleStringPlus() throws Exception {
runTest("compiler/testData/codegen/box/strings/simpleStringPlus.kt");
}
@TestMetadata("singleConcatNullable.kt")
public void testSingleConcatNullable() throws Exception {
runTest("compiler/testData/codegen/box/strings/singleConcatNullable.kt");
}
@TestMetadata("stringBuilderAppend.kt")
public void testStringBuilderAppend() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringBuilderAppend.kt");
@@ -19114,6 +19124,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testStringPlusOnlyWorksOnString() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOnlyWorksOnString.kt");
}
@TestMetadata("stringPlusOverride.kt")
public void testStringPlusOverride() throws Exception {
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/super")