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:
+7
@@ -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",
|
||||
|
||||
+2
@@ -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)
|
||||
|
||||
+153
@@ -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')
|
||||
|
||||
+71
@@ -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"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
Foo(bar=42),Foo(bar=42)
|
||||
null,null
|
||||
Foo(bar=42),bar
|
||||
null,null
|
||||
Foo(bar=42),bar
|
||||
null,bar
|
||||
+79
@@ -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"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
Foo(bar=42)
|
||||
null
|
||||
Foo(bar=42)
|
||||
foo
|
||||
null
|
||||
foo
|
||||
+91
@@ -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"))
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fooFoo(bar=42)
|
||||
nullnull
|
||||
foobar
|
||||
nullbar
|
||||
fooFoo(bar=42)
|
||||
nullnull
|
||||
foobar
|
||||
nullbar
|
||||
+30
@@ -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())
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
42153
|
||||
+80
@@ -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"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fooFoo(bar=42)
|
||||
foonull
|
||||
foobar
|
||||
foonull
|
||||
fooFoo(bar=42)
|
||||
foobar
|
||||
+23
@@ -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())
|
||||
}
|
||||
+11
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user