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:
Vladimir Sukharev
2022-08-09 18:07:02 +00:00
committed by Space
parent d0fb997967
commit c84a70c654
14 changed files with 260 additions and 0 deletions
@@ -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",
@@ -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)
@@ -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>
@@ -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