KT53119: Implement String Concatenation Type Narrowing to improve escape analysis

Merge-request: KT-MR-6661
Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
Vladimir Sukharev
2022-07-20 16:28:33 +00:00
committed by Space
parent 73226aba00
commit d378d7d5f5
25 changed files with 647 additions and 9 deletions
@@ -210,6 +210,13 @@ internal val stringConcatenationPhase = makeKonanFileLoweringPhase(
description = "String concatenation lowering"
)
internal val stringConcatenationTypeNarrowingPhase = makeKonanFileLoweringPhase(
::StringConcatenationTypeNarrowing,
name = "StringConcatenationTypeNarrowing",
description = "String concatenation type narrowing",
prerequisite = setOf(stringConcatenationPhase)
)
internal val kotlinNothingValueExceptionPhase = makeKonanFileLoweringPhase(
::KotlinNothingValueExceptionLowering,
name = "KotlinNothingValueException",
@@ -279,6 +279,7 @@ internal val allLoweringsPhase = NamedCompilerPhase(
foldConstantLoweringPhase,
computeStringTrimPhase,
stringConcatenationPhase,
stringConcatenationTypeNarrowingPhase,
enumConstructorsPhase,
initializersPhase,
localFunctionsPhase,
@@ -626,6 +627,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled)
disableUnless(optimizeTLSDataLoadsPhase, config.optimizationsEnabled)
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
disableUnless(linkerPhase, config.produce.involvesLinkStage)
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
/**
* This pass replaces calls to:
* - StringBuilder.append(Any?) with StringBuilder.append(String?)
* - StringBuilder.append(Any) with StringBuilder.append(String)
* - String.plus(Any?) with String.plusImpl(String)
* - String?.plus(Any?) with String.plusImpl(String)
* For this, toString() is called for non-String arguments. This call can be later devirtualized, improving escape analysis
* For nullable arguments, the following snippet is used:
* - "if (arg==null) null else arg.toString()" to pass to StringBuilder.append(String?)
* - "if (arg==null) "null" else arg.toString()" to pass to other methods as non-nullable String
*/
internal class StringConcatenationTypeNarrowing(val context: Context) : FileLoweringPass, IrBuildingTransformer(context) {
private val string = context.ir.symbols.string.owner
private val stringBuilder = context.ir.symbols.stringBuilder.owner
private val namePlusImpl = Name.identifier("plusImpl")
private val nameAppend = Name.identifier("append")
private val appendStringFunction = stringBuilder.functions.single { // StringBuilder.append(String)
it.name == nameAppend &&
it.valueParameters.singleOrNull()?.type?.isString() == true
}
private val appendNullableStringFunction = stringBuilder.functions.single { // StringBuilder.append(String?)
it.name == nameAppend &&
it.valueParameters.singleOrNull()?.type?.isNullableString() == true
}
private val appendAnyFunction = stringBuilder.functions.single { // StringBuilder.append(Any?)
it.name == nameAppend &&
it.valueParameters.singleOrNull()?.type?.isNullableAny() == true
}
private val plusImplFunction = string.functions.single { // external fun String.plusImpl(String)
it.name == namePlusImpl &&
it.valueParameters.size == 1 &&
it.valueParameters.single().type.isString()
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
return with(expression) {
builder.at(this)
when (symbol) {
appendAnyFunction.symbol -> { // StringBuilder.append(Any?)
val argument = getValueArgument(0)!!
if (argument.type.isNullable()) {
// Transform `StringBuilder.append(Any?)` to `StringBuilder.append(ARG?.toString())`, using "StringBuilder.append(String?)"
buildConcatenationCall(appendNullableStringFunction, dispatchReceiver!!, buildNullableArgToNullableString(argument))
} else {
// Transform `StringBuilder.append(Any)` to `StringBuilder.append(ARG.toString())`, using "StringBuilder.append(String)"
buildConcatenationCall(appendStringFunction, dispatchReceiver!!, buildNonNullableArgToString(argument))
}
}
context.irBuiltIns.memberStringPlus ->
buildConcatenationCall(plusImplFunction, dispatchReceiver!!, buildNullableArgToString(getValueArgument(0)!!))
context.irBuiltIns.extensionStringPlus ->
buildConcatenationCall(plusImplFunction, buildNullableArgToString(extensionReceiver!!), buildNullableArgToString(getValueArgument(0)!!))
else -> expression
}
}
}
private fun IrCall.buildConcatenationCall(function: IrSimpleFunction, receiver: IrExpression, argument: IrExpression): IrExpression =
builder.irCall(function.symbol, function.returnType, valueArgumentsCount = 1, typeArgumentsCount = 0)
.apply {
putValueArgument(0, argument)
dispatchReceiver = receiver
}
/** Builds snippet of type String
* - "if(argument==null) "null" else argument.toString()", if argument's type is nullable. Note: fortunately, all "null" string structures are unified
* - "argument.toString()", otherwise
*/
private fun buildNullableArgToString(argument: IrExpression): IrExpression =
if (argument.type.isNullable()) {
builder.irBlock {
nullableArgToStringType(argument, context.irBuiltIns.stringType, irString("null"))
}
} else buildNonNullableArgToString(argument)
/** Builds snippet of type String?
* "if(argument==null) null else argument.toString()", that is similar to "argument?.toString()"
*/
private fun buildNullableArgToNullableString(argument: IrExpression): IrExpression =
builder.irBlock {
nullableArgToStringType(argument, context.irBuiltIns.stringType.makeNullable(), irNull())
}
private fun IrBlockBuilder.nullableArgToStringType(argument: IrExpression, stringType: IrType, ifNull: IrExpression) {
val (firstExpression, secondExpression) = twoExpressionsForSubsequentUsages(argument)
+irIfThenElse(
stringType,
condition = irEqeqeq(firstExpression, irNull()),
thenPart = ifNull,
elsePart = buildNonNullableArgToString(secondExpression),
origin = null
)
}
/** Builds snippet of type String
* - "argument", in case argument's type is String, since String.toString() is no-op
* - "argument", in case argument's type is String?, due to smart-cast and no-op
* - "argument.toString()", otherwise
*/
private fun buildNonNullableArgToString(argument: IrExpression): IrExpression {
return if (argument.type.isString() || argument.type.isNullableString())
argument
else builder
.irCall(context.ir.symbols.memberToString, context.irBuiltIns.stringType, valueArgumentsCount = 1, typeArgumentsCount = 0)
.apply { dispatchReceiver = argument }
}
/**
* This function returns two expressions based on the parameter:
* - <original [argument] and its shallow copy>, should its second usage be idempotent and have runtime cost not greater than local val read.
* This reduces excessive local variable usage without performance degradation.
* - <two [IrGetValue] nodes for newly-created temporary val, initialized with original expression>, otherwise.
*/
private fun IrBlockBuilder.twoExpressionsForSubsequentUsages(argument: IrExpression): Pair<IrExpression, IrExpression> =
if (argument is IrGetValue)
Pair(argument, argument.shallowCopy())
else
createTmpVariable(argument).let { Pair(irGet(it), irGet(it)) }
}
@@ -6222,6 +6222,66 @@ fileCheckTest("filecheck_intrinsics") {
annotatedSource = project.file('filecheck/intrinsics.kt')
}
task kt53119_side_effect(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_side_effect.kt"
}
fileCheckTest("filecheck_kt53119_plus_generated_noescape") {
annotatedSource = project.file('codegen/stringConcatenationTypeNarrowing/kt53119_plus_generated_noescape.kt')
enabled = project.globalTestArgs.contains('-opt')
extraOpts = ["-tr"]
}
task kt53119_plus_generated_noescape(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_plus_generated_noescape.kt"
}
fileCheckTest("filecheck_kt53119_plus_member") {
annotatedSource = project.file('codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt')
enabled = project.globalTestArgs.contains('-opt')
extraOpts = ["-tr"]
}
task kt53119_plus_member(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt"
}
fileCheckTest("filecheck_kt53119_plus_extension") {
annotatedSource = project.file('codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt')
enabled = project.globalTestArgs.contains('-opt')
extraOpts = ["-tr"]
}
task kt53119_plus_extension(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt"
}
fileCheckTest("filecheck_kt53119_append_generated") {
annotatedSource = project.file('codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt')
enabled = project.globalTestArgs.contains('-opt')
extraOpts = ["-tr"]
}
task kt53119_append_generated(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt"
}
fileCheckTest("filecheck_kt53119_append_manual") {
annotatedSource = project.file('codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt')
enabled = project.globalTestArgs.contains('-opt')
extraOpts = ["-tr"]
}
task kt53119_append_manual(type: KonanLocalTest) {
useGoldenData = true
source = "codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt"
}
fileCheckTest("filecheck_escape_analysis_enabled") {
annotatedSource = project.file('filecheck/escape_analysis.kt')
enabled = project.globalTestArgs.contains('-opt')
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package codegen.stringConcatenationTypeNarrowing.kt53119_append_generated
import kotlin.test.*
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyMaybeAny
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: ret %struct.ObjHeader*
fun maybeAnyMaybeAny(maybeAny1: Any?, maybeAny2: Any?): String {
return "$maybeAny1,$maybeAny2"
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyMaybeString
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: ret %struct.ObjHeader*
fun maybeAnyMaybeString(maybeAny1: Any?, maybeString2: String?): String {
return "$maybeAny1,$maybeString2"
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyString
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: ret %struct.ObjHeader*
fun maybeAnyString(maybeAny1: Any?, string: String): String {
return "$maybeAny1,$string"
}
data class Foo(val bar: Int)
@Test
fun runTest() {
val foo = Foo(42)
println(maybeAnyMaybeAny(foo, foo))
println(maybeAnyMaybeAny(null, null))
println(maybeAnyMaybeString(foo, "bar"))
println(maybeAnyMaybeString(null, null))
println(maybeAnyString(foo, "bar"))
println(maybeAnyString(null, "bar"))
}
@@ -0,0 +1,6 @@
Foo(bar=42),Foo(bar=42)
null,null
Foo(bar=42),bar
null,null
Foo(bar=42),bar
null,bar
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package codegen.stringConcatenationTypeNarrowing.kt53119_append_manual
import kotlin.test.*
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendMaybeAny(kotlin.Any?)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: ret %struct.ObjHeader*
fun appendMaybeAny(maybeAny: Any?): String {
val sb = kotlin.text.StringBuilder()
sb.append(maybeAny)
return sb.toString()
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendAny(kotlin.Any)
// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String)
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK: ret %struct.ObjHeader*
fun appendAny(any: Any): String {
val sb = kotlin.text.StringBuilder()
sb.append(any)
return sb.toString()
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendMaybeString(kotlin.String?)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String
// CHECK: ret %struct.ObjHeader*
fun appendMaybeString(maybeStr: String?): String {
val sb = kotlin.text.StringBuilder()
sb.append(maybeStr)
return sb.toString()
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendString(kotlin.String)
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String
// CHECK: ret %struct.ObjHeader*
fun appendString(str: String): String {
val sb = kotlin.text.StringBuilder()
sb.append(str)
return sb.toString()
}
data class Foo(val bar: Int)
@Test
fun runTest() {
val foo = Foo(42)
println(appendMaybeAny(foo))
println(appendMaybeAny(null))
println(appendAny(foo))
println(appendMaybeString("foo"))
println(appendMaybeString(null))
println(appendString("foo"))
}
@@ -0,0 +1,6 @@
Foo(bar=42)
null
Foo(bar=42)
foo
null
foo
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension
import kotlin.test.*
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#manualPlusExtensionAny
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension.Foo#toString(){}kotlin.String"
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun manualPlusExtensionAny(maybeStr: String?, maybeAny: kotlin.Any?): kotlin.String =
maybeStr + maybeAny
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#manualPlusExtensionString
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun manualPlusExtensionString(maybeStr: String?, str: String): kotlin.String =
maybeStr + str
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionAny
// CHECK-NOT: kfun:kotlin#plus__at__kotlin.String?(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin#plus__at__kotlin.String?(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin#plus__at__kotlin.String?(kotlin.Any?)
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK: ret %struct.ObjHeader*
fun generatedPlusExtensionAny(maybeStr: String?, maybeAny: Any?): String {
return "$maybeStr$maybeAny"
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionString
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun generatedPlusExtensionString(maybeStr: String?, str: String): String {
return "$maybeStr$str"
}
data class Foo(val bar: Int)
@Test
fun runTest() {
val foo = Foo(42)
println(manualPlusExtensionAny("foo", foo))
println(manualPlusExtensionAny(null, null))
println(manualPlusExtensionString("foo", "bar"))
println(manualPlusExtensionString(null, "bar"))
println(generatedPlusExtensionAny("foo", foo))
println(generatedPlusExtensionAny(null, null))
println(generatedPlusExtensionString("foo", "bar"))
println(generatedPlusExtensionString(null, "bar"))
}
@@ -0,0 +1,8 @@
fooFoo(bar=42)
nullnull
foobar
nullbar
fooFoo(bar=42)
nullnull
foobar
nullbar
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_generated_noescape
import kotlin.test.*
import kotlin.random.*
// Without "string concatenation type narrowing", pointer to IntRange is conservatively considered to escape,
// since within String.plus(Any?) (within Random.nextInt), the pointer is passed as a receiver of virtual call Any.toString()
// As a result, no stack allocation is possible
// With "string concatenation type narrowing", IntRange.toString() is explicitly called, that does not escape the receiver.
// Hence, escape analysis can find out the pointer to IntRange does not escape anywhere, and stack allocation is possible.
// CHECK-LABEL: define i32 @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_generated_noescape#getNextInt(){}kotlin.Int
// CHECK-NOT ret i32
// CHECK alloca %"kclassbody:kotlin.ranges.IntRange#internal"
// CHECK ret i32
fun getNextInt(): Int {
return Random.nextInt(IntRange(42153, 42153))
}
@Test
fun runTest() {
println(getNextInt())
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_member
import kotlin.test.*
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberAny
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun manualPlusMemberAny(str: String, maybeAny: kotlin.Any?): kotlin.String =
str + maybeAny
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberString
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK: ret %struct.ObjHeader*
fun manualPlusMemberString(str1: String, str2: String): kotlin.String =
str1 + str2
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#generatedPlusMemberAny
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member.Foo#toString(){}kotlin.String"
// CHECK-NOT: Foo#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun generatedPlusMemberAny(str: String, maybeAny: Any?): String {
return "$str$maybeAny"
}
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#generatedPlusMemberString
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
// CHECK: ret %struct.ObjHeader*
fun generatedPlusMemberString(str1: String, str2: String): String {
return "$str1$str2"
}
data class Foo(val bar: Int)
@Test
fun runTest() {
val foo = Foo(42)
println(manualPlusMemberAny("foo", foo))
println(manualPlusMemberAny("foo", null))
println(manualPlusMemberString("foo", "bar"))
println(generatedPlusMemberAny("foo", null))
println(generatedPlusMemberAny("foo", foo))
println(generatedPlusMemberString("foo", "bar"))
}
@@ -0,0 +1,6 @@
fooFoo(bar=42)
foonull
foobar
foonull
fooFoo(bar=42)
foobar
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// The same tests are also tested with FileCheck in folder ../../filecheck/kt53119_*.kt
// Please keep them in sync
package codegen.stringConcatenationTypeNarrowing.kt53119_side_effect
import kotlin.test.*
fun withSideEffect(): String? {
println("Side effect")
return "Result."
}
@Test
fun runTest() {
println("Retval is: " + withSideEffect())
println("Retval is: " + withSideEffect() + "!")
println(withSideEffect() + withSideEffect())
println(withSideEffect() + withSideEffect() + withSideEffect())
}
@@ -0,0 +1,11 @@
Side effect
Retval is: Result.
Side effect
Retval is: Result.!
Side effect
Side effect
Result.Result.
Side effect
Side effect
Side effect
Result.Result.Result.
@@ -7,7 +7,7 @@ class C {
fun foo(x: Int) = x
}
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
// CHECK: Int-unbox
@@ -7,7 +7,7 @@ class C<T> {
fun foo(x: T) = x
}
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
// CHECK: Int-unbox
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// CHECK: define internal void @"kfun:$foo$FUNCTION_REFERENCE$0.<init>#internal"
// CHECK-LABEL: define internal void @"kfun:$foo$FUNCTION_REFERENCE$0.<init>#internal"
// CHECK-SAME: i32
fun <T> T.foo() { println(this) }
@@ -9,7 +9,7 @@ fun interface Foo {
fun baz(x: Any): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// Boxing needs to be used now due to non-devirtualized call
// CHECK: Int-box
@@ -9,7 +9,7 @@ fun interface Foo<T> {
fun baz(x: Any): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// CHECK-NOT: Int-unbox
// CHECK: ret void
@@ -9,7 +9,7 @@ fun interface Foo {
fun baz(x: Int): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// CHECK-NOT: Int-unbox
// CHECK: ret void
@@ -9,7 +9,7 @@ fun interface Foo<T> {
fun baz(x: Any): Int = x.hashCode()
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// CHECK-NOT: Int-unbox
// CHECK: ret void
@@ -5,7 +5,7 @@
fun plus1(x: Int) = x + 1
// CHECK: define void @"kfun:#main(){}"()
// CHECK-LABEL: define void @"kfun:#main(){}"()
// CHECK-NOT: Int-box
// CHECK-NOT: Int-unbox
// CHECK: ret void
@@ -8,6 +8,8 @@ package kotlin
import kotlin.native.internal.ExportTypeInfo
import kotlin.native.internal.Frozen
import kotlin.native.internal.GCUnsafeCall
import kotlin.native.internal.IntrinsicType
import kotlin.native.internal.TypedIntrinsic
@ExportTypeInfo("theStringTypeInfo")
@OptIn(FreezingIsDeprecated::class)
@@ -23,6 +25,7 @@ public final class String : Comparable<String>, CharSequence {
return plusImpl(other.toString())
}
@TypedIntrinsic(IntrinsicType.IDENTITY)
override public fun toString(): String {
return this
}
@@ -47,8 +50,9 @@ public final class String : Comparable<String>, CharSequence {
@GCUnsafeCall("Kotlin_String_getStringLength")
private external fun getStringLength(): Int
@PublishedApi
@GCUnsafeCall("Kotlin_String_plusImpl")
private external fun plusImpl(other: String): String
internal external fun plusImpl(other: String): String
@GCUnsafeCall("Kotlin_String_equals")
external override fun equals(other: Any?): Boolean