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.loops.ForLoopsLowering
|
||||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
||||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
|
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.common.phaser.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator
|
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.*
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
|
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
|
||||||
import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer
|
import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer
|
||||||
@@ -408,6 +410,13 @@ internal val autoboxPhase = makeKonanFileLoweringPhase(
|
|||||||
prerequisite = setOf(bridgesPhase, coroutinesPhase)
|
prerequisite = setOf(bridgesPhase, coroutinesPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val unboxInlinePhase = makeKonanModuleLoweringPhase(
|
||||||
|
::UnboxInlineLowering,
|
||||||
|
name = "UnboxInline",
|
||||||
|
description = "Unbox functions inline lowering",
|
||||||
|
prerequisite = setOf(autoboxPhase, redundantCoercionsCleaningPhase)
|
||||||
|
)
|
||||||
|
|
||||||
internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase(
|
internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase(
|
||||||
::ExpressionBodyTransformer,
|
::ExpressionBodyTransformer,
|
||||||
name = "ExpressionBodyTransformer",
|
name = "ExpressionBodyTransformer",
|
||||||
|
|||||||
+2
@@ -583,6 +583,7 @@ internal val bitcodePhase = NamedCompilerPhase(
|
|||||||
// from dependencies can be changed during lowerings.
|
// from dependencies can be changed during lowerings.
|
||||||
inlineClassPropertyAccessorsPhase then
|
inlineClassPropertyAccessorsPhase then
|
||||||
redundantCoercionsCleaningPhase then
|
redundantCoercionsCleaningPhase then
|
||||||
|
unboxInlinePhase then
|
||||||
createLLVMDeclarationsPhase then
|
createLLVMDeclarationsPhase then
|
||||||
ghaPhase then
|
ghaPhase then
|
||||||
RTTIPhase 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
|
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken
|
||||||
// debug information.
|
// debug information.
|
||||||
disable(propertyAccessorInlinePhase)
|
disable(propertyAccessorInlinePhase)
|
||||||
|
disable(unboxInlinePhase)
|
||||||
disable(inlineClassPropertyAccessorsPhase)
|
disable(inlineClassPropertyAccessorsPhase)
|
||||||
disable(dcePhase)
|
disable(dcePhase)
|
||||||
disable(removeRedundantCallsToFileInitializersPhase)
|
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')
|
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") {
|
fileCheckTest("filecheck_intrinsics") {
|
||||||
annotatedSource = project.file('filecheck/intrinsics.kt')
|
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