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"
|
description = "String concatenation lowering"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val stringConcatenationTypeNarrowingPhase = makeKonanFileLoweringPhase(
|
||||||
|
::StringConcatenationTypeNarrowing,
|
||||||
|
name = "StringConcatenationTypeNarrowing",
|
||||||
|
description = "String concatenation type narrowing",
|
||||||
|
prerequisite = setOf(stringConcatenationPhase)
|
||||||
|
)
|
||||||
|
|
||||||
internal val kotlinNothingValueExceptionPhase = makeKonanFileLoweringPhase(
|
internal val kotlinNothingValueExceptionPhase = makeKonanFileLoweringPhase(
|
||||||
::KotlinNothingValueExceptionLowering,
|
::KotlinNothingValueExceptionLowering,
|
||||||
name = "KotlinNothingValueException",
|
name = "KotlinNothingValueException",
|
||||||
|
|||||||
+2
@@ -279,6 +279,7 @@ internal val allLoweringsPhase = NamedCompilerPhase(
|
|||||||
foldConstantLoweringPhase,
|
foldConstantLoweringPhase,
|
||||||
computeStringTrimPhase,
|
computeStringTrimPhase,
|
||||||
stringConcatenationPhase,
|
stringConcatenationPhase,
|
||||||
|
stringConcatenationTypeNarrowingPhase,
|
||||||
enumConstructorsPhase,
|
enumConstructorsPhase,
|
||||||
initializersPhase,
|
initializersPhase,
|
||||||
localFunctionsPhase,
|
localFunctionsPhase,
|
||||||
@@ -626,6 +627,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
|
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
|
||||||
disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
|
disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
|
||||||
disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
|
disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS))
|
||||||
|
disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled)
|
||||||
disableUnless(optimizeTLSDataLoadsPhase, config.optimizationsEnabled)
|
disableUnless(optimizeTLSDataLoadsPhase, config.optimizationsEnabled)
|
||||||
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
|
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
|
||||||
disableUnless(linkerPhase, 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')
|
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") {
|
fileCheckTest("filecheck_escape_analysis_enabled") {
|
||||||
annotatedSource = project.file('filecheck/escape_analysis.kt')
|
annotatedSource = project.file('filecheck/escape_analysis.kt')
|
||||||
enabled = project.globalTestArgs.contains('-opt')
|
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
|
fun foo(x: Int) = x
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: define void @"kfun:#main(){}"()
|
// CHECK-LABEL: define void @"kfun:#main(){}"()
|
||||||
// CHECK-NOT: Int-box
|
// CHECK-NOT: Int-box
|
||||||
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
|
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
|
||||||
// CHECK: Int-unbox
|
// CHECK: Int-unbox
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class C<T> {
|
|||||||
fun foo(x: T) = x
|
fun foo(x: T) = x
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: define void @"kfun:#main(){}"()
|
// CHECK-LABEL: define void @"kfun:#main(){}"()
|
||||||
// CHECK-NOT: Int-box
|
// CHECK-NOT: Int-box
|
||||||
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
|
// TODO Remove next check after fix of https://youtrack.jetbrains.com/issue/KT-53100/Optimization-needed-T-unboxCONSTANTPRIMITIVEx-T-x
|
||||||
// CHECK: Int-unbox
|
// 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.
|
* 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
|
// CHECK-SAME: i32
|
||||||
|
|
||||||
fun <T> T.foo() { println(this) }
|
fun <T> T.foo() { println(this) }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fun interface Foo {
|
|||||||
|
|
||||||
fun baz(x: Any): Int = x.hashCode()
|
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
|
// Boxing needs to be used now due to non-devirtualized call
|
||||||
// CHECK: Int-box
|
// CHECK: Int-box
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fun interface Foo<T> {
|
|||||||
|
|
||||||
fun baz(x: Any): Int = x.hashCode()
|
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-box
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fun interface Foo {
|
|||||||
|
|
||||||
fun baz(x: Int): Int = x.hashCode()
|
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-box
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fun interface Foo<T> {
|
|||||||
|
|
||||||
fun baz(x: Any): Int = x.hashCode()
|
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-box
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
fun plus1(x: Int) = x + 1
|
fun plus1(x: Int) = x + 1
|
||||||
|
|
||||||
// CHECK: define void @"kfun:#main(){}"()
|
// CHECK-LABEL: define void @"kfun:#main(){}"()
|
||||||
// CHECK-NOT: Int-box
|
// CHECK-NOT: Int-box
|
||||||
// CHECK-NOT: Int-unbox
|
// CHECK-NOT: Int-unbox
|
||||||
// CHECK: ret void
|
// CHECK: ret void
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ package kotlin
|
|||||||
import kotlin.native.internal.ExportTypeInfo
|
import kotlin.native.internal.ExportTypeInfo
|
||||||
import kotlin.native.internal.Frozen
|
import kotlin.native.internal.Frozen
|
||||||
import kotlin.native.internal.GCUnsafeCall
|
import kotlin.native.internal.GCUnsafeCall
|
||||||
|
import kotlin.native.internal.IntrinsicType
|
||||||
|
import kotlin.native.internal.TypedIntrinsic
|
||||||
|
|
||||||
@ExportTypeInfo("theStringTypeInfo")
|
@ExportTypeInfo("theStringTypeInfo")
|
||||||
@OptIn(FreezingIsDeprecated::class)
|
@OptIn(FreezingIsDeprecated::class)
|
||||||
@@ -23,6 +25,7 @@ public final class String : Comparable<String>, CharSequence {
|
|||||||
return plusImpl(other.toString())
|
return plusImpl(other.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||||
override public fun toString(): String {
|
override public fun toString(): String {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@@ -47,8 +50,9 @@ public final class String : Comparable<String>, CharSequence {
|
|||||||
@GCUnsafeCall("Kotlin_String_getStringLength")
|
@GCUnsafeCall("Kotlin_String_getStringLength")
|
||||||
private external fun getStringLength(): Int
|
private external fun getStringLength(): Int
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
@GCUnsafeCall("Kotlin_String_plusImpl")
|
@GCUnsafeCall("Kotlin_String_plusImpl")
|
||||||
private external fun plusImpl(other: String): String
|
internal external fun plusImpl(other: String): String
|
||||||
|
|
||||||
@GCUnsafeCall("Kotlin_String_equals")
|
@GCUnsafeCall("Kotlin_String_equals")
|
||||||
external override fun equals(other: Any?): Boolean
|
external override fun equals(other: Any?): Boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user