Always inline primitive unbox functions in IR level
Merge-request: KT-MR-6708 Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
+9
@@ -10,8 +10,10 @@ import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLamb
|
||||
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
|
||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
|
||||
import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering
|
||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.redundantCoercionsCleaningPhase
|
||||
import org.jetbrains.kotlin.backend.konan.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
|
||||
import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer
|
||||
@@ -408,6 +410,13 @@ internal val autoboxPhase = makeKonanFileLoweringPhase(
|
||||
prerequisite = setOf(bridgesPhase, coroutinesPhase)
|
||||
)
|
||||
|
||||
internal val unboxInlinePhase = makeKonanModuleLoweringPhase(
|
||||
::UnboxInlineLowering,
|
||||
name = "UnboxInline",
|
||||
description = "Unbox functions inline lowering",
|
||||
prerequisite = setOf(autoboxPhase, redundantCoercionsCleaningPhase)
|
||||
)
|
||||
|
||||
internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase(
|
||||
::ExpressionBodyTransformer,
|
||||
name = "ExpressionBodyTransformer",
|
||||
|
||||
+2
@@ -583,6 +583,7 @@ internal val bitcodePhase = NamedCompilerPhase(
|
||||
// from dependencies can be changed during lowerings.
|
||||
inlineClassPropertyAccessorsPhase then
|
||||
redundantCoercionsCleaningPhase then
|
||||
unboxInlinePhase then
|
||||
createLLVMDeclarationsPhase then
|
||||
ghaPhase then
|
||||
RTTIPhase then
|
||||
@@ -699,6 +700,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
||||
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken
|
||||
// debug information.
|
||||
disable(propertyAccessorInlinePhase)
|
||||
disable(unboxInlinePhase)
|
||||
disable(inlineClassPropertyAccessorsPhase)
|
||||
disable(dcePhase)
|
||||
disable(removeRedundantCallsToFileInitializersPhase)
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION
|
||||
import org.jetbrains.kotlin.backend.konan.getUnboxFunction
|
||||
import org.jetbrains.kotlin.ir.builders.irGetField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.util.statements
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
/**
|
||||
* In case the body of <T-unbox> is exactly RETURN(GET_FIELD(IrExpression, backing_field)), it is inlined.
|
||||
* So, the snippets `CALL 'public final fun <T-unbox> (IrExpression)` are transformed to 'GET_FIELD(IrExpression, backing_field)'.
|
||||
*/
|
||||
internal class UnboxInlineLowering(
|
||||
private val context: CommonBackendContext,
|
||||
) : BodyLoweringPass {
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
irBody.transformChildrenVoid(AccessorInliner(context))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessorInliner(commonBackendContext: CommonBackendContext) : IrElementTransformerVoid() {
|
||||
|
||||
private val context = commonBackendContext as Context
|
||||
private val anyType = context.irBuiltIns.anyType
|
||||
|
||||
private fun IrFunction.isEasyInlineableUnbox(): Boolean =
|
||||
origin == DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION &&
|
||||
name.asString().endsWith("-unbox>") &&
|
||||
!returnType.isNullable()
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
return if (expression.symbol.owner.isEasyInlineableUnbox())
|
||||
tryInlineUnbox(expression) ?: expression
|
||||
else expression
|
||||
}
|
||||
|
||||
private fun tryInlineUnbox(call: IrCall): IrExpression? {
|
||||
val returnClass = call.type.getClass()!!
|
||||
val singleStatement = context.getUnboxFunction(returnClass).body?.statements?.singleOrNull()
|
||||
return if (singleStatement is IrReturn) {
|
||||
val retVal = singleStatement.value
|
||||
if (retVal is IrGetField) {
|
||||
// Boxed primitive types (Int, Short,..) have `value` field
|
||||
// Inline unsigned classes (UInt, UShort,..) have `data` field
|
||||
val field = retVal.symbol.owner
|
||||
context.createIrBuilder(call.symbol, call.startOffset, call.endOffset).irGetField(call.getValueArgument(0), field)
|
||||
} else {
|
||||
context.log { "Cannot inline unbox function ${call.symbol} with body `IrReturn(expression)`, where `expression` is not IrGetField(...)" }
|
||||
null
|
||||
}
|
||||
} else {
|
||||
context.log { "Cannot inline unbox function ${call.symbol} with body which is not IrReturn(IrGetField(...))" }
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6225,6 +6225,59 @@ fileCheckTest("filecheck_kt49847_generic_receiver") {
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_inline_unbox") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_inline_unbox.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
if (target.architecture == Architecture.ARM64) {
|
||||
checkPrefix = "CHECK-ARM64"
|
||||
}
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_value_unbox") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_value_unbox.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_CPointer.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_CPointer.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_NativePointed.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_NativePointed.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_NonNullNativePtr.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_NonNullNativePtr.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_StableRef") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_StableRef.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_UByteArray.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_UByteArray.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_UIntArray.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_UIntArray.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_ULongArray.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_ULongArray.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_kt53261_noinline_UShortArray.kt") {
|
||||
annotatedSource = project.file('filecheck/kt53261/kt53261_noinline_UShortArray.kt')
|
||||
enabled = project.globalTestArgs.contains('-opt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_intrinsics") {
|
||||
annotatedSource = project.file('filecheck/intrinsics.kt')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK-NOT: {{call|invoke}} i64 @"kfun:kotlin#<Long-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} i64 @"kfun:kotlin#<ULong-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} i32 @"kfun:kotlin#<Int-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} i32 @"kfun:kotlin#<UInt-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} signext i16 @"kfun:kotlin#<Short-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} zeroext i16 @"kfun:kotlin#<UShort-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} signext i8 @"kfun:kotlin#<Byte-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} zeroext i8 @"kfun:kotlin#<UByte-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} zeroext i16 @"kfun:kotlin#<Char-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} zeroext i1 @"kfun:kotlin#<Boolean-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} double @"kfun:kotlin#<Double-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} float @"kfun:kotlin#<Float-unbox>
|
||||
|
||||
// CHECK-NOT: {{call|invoke}} i8* @"kfun:kotlin.native.internal#<NativePtr-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} i32 @"kfun:kotlin.native.concurrent#<Future-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} i32 @"kfun:kotlin.native.concurrent#<Worker-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} <4 x float> @"kfun:kotlin.native#<Vector128-unbox>
|
||||
// CHECK-NOT: {{call|invoke}} %struct.ObjHeader* @"kfun:kotlin#<Result-unbox>
|
||||
|
||||
// On ARM64, generated functions IntToNSNumber and UIntToNSNumber may contain non-converted invocations of unbox functions.
|
||||
// CHECK-ARM64: IntToNSNumber
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} i8* @"kfun:kotlinx.cinterop#<CPointer-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} i8* @"kfun:kotlinx.cinterop#<NativePointed-unbox>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} i8* @"kfun:kotlin.native.internal#<NonNullNativePtr-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} i8* @"kfun:kotlinx.cinterop#<StableRef-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} %struct.ObjHeader* @"kfun:kotlin#<UByteArray-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} %struct.ObjHeader* @"kfun:kotlin#<UIntArray-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} %struct.ObjHeader* @"kfun:kotlin#<ULongArray-unbox>
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
fun main(arr: Array<String>) {
|
||||
println(arr[0].toInt() + 1)
|
||||
}
|
||||
// CHECK: {{call|invoke}} %struct.ObjHeader* @"kfun:kotlin#<UShortArray-unbox>
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
value class C(val x: Any)
|
||||
val c = C(42)
|
||||
|
||||
fun main() {
|
||||
println(c.x)
|
||||
}
|
||||
// CHECK: {{call|invoke}} %struct.ObjHeader* @"kfun:#<C-unbox>
|
||||
|
||||
// Note: <C-unbox> is called from IR of generated methods like
|
||||
// FUN BRIDGE_METHOD(target=FUN GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER name:equals
|
||||
// FUN BRIDGE_METHOD(target=FUN GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER name:hashCode
|
||||
// FUN BRIDGE_METHOD(target=FUN GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER name:toString
|
||||
Reference in New Issue
Block a user