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
@@ -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.