[K/N] Refactor default argument lowering to avoid boxing
Current inline classes API is not compatible between different backends. So implementing common function required for this optimization was not possible in Native backend. So, common default arguments lowering was refactored to make bigger piece of code replaceable in backends. ^KT-57860
This commit is contained in:
committed by
Space Team
parent
2aea5822b2
commit
f55fd481e9
@@ -26,14 +26,8 @@ abstract class Ir<out T : CommonBackendContext>(val context: T) {
|
||||
|
||||
abstract val symbols: Symbols
|
||||
|
||||
val defaultParameterDeclarationsCache = mutableMapOf<IrFunction, IrFunction>()
|
||||
|
||||
internal val localScopeWithCounterMap = LocalDeclarationsLowering.LocalScopeWithCounterMap()
|
||||
|
||||
// If irType is an inline class type, return the underlying type according to the
|
||||
// unfolding rules of the current backend. Otherwise, returns null.
|
||||
open fun unfoldInlineClassType(irType: IrType): IrType? = null
|
||||
|
||||
open fun shouldGenerateHandlerParameterForDefaultBodyFun() = false
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -15,12 +15,14 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.utils.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
abstract class DefaultArgumentFunctionFactory(open val context: CommonBackendContext) {
|
||||
abstract class DefaultArgumentFunctionFactory(val context: CommonBackendContext) {
|
||||
|
||||
protected fun IrFunction.generateDefaultArgumentsFunctionName() =
|
||||
Name.identifier("${name}\$default")
|
||||
|
||||
@@ -40,11 +42,19 @@ abstract class DefaultArgumentFunctionFactory(open val context: CommonBackendCon
|
||||
contextReceiverParametersCount = original.contextReceiverParametersCount
|
||||
}
|
||||
|
||||
protected fun IrFunction.copyValueParametersFrom(original: IrFunction, wrapWithNullable: Boolean = true) {
|
||||
/**
|
||||
* Whether `null` will be used for this type if no argument is passed.
|
||||
* In that case, the type of the default dispatch function will be made nullable.
|
||||
*
|
||||
* By default, always returns `true` – this is valid, but suboptimal.
|
||||
* Better performance can be achieved in a backend-specific way.
|
||||
*/
|
||||
protected open fun IrType.hasNullAsUndefinedValue(): Boolean = true
|
||||
|
||||
protected fun IrFunction.copyValueParametersFrom(original: IrFunction) {
|
||||
valueParameters = original.valueParameters.memoryOptimizedMap {
|
||||
val newType = it.type.remapTypeParameters(original.classIfConstructor, classIfConstructor)
|
||||
val makeNullable = wrapWithNullable && it.defaultValue != null &&
|
||||
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
|
||||
val makeNullable = it.defaultValue != null && it.type.hasNullAsUndefinedValue()
|
||||
it.copyTo(
|
||||
this,
|
||||
type = if (makeNullable) newType.makeNullable() else newType,
|
||||
|
||||
+9
-9
@@ -33,12 +33,12 @@ import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
|
||||
// TODO: fix expect/actual default parameters
|
||||
|
||||
open class DefaultArgumentStubGenerator(
|
||||
open val context: CommonBackendContext,
|
||||
open class DefaultArgumentStubGenerator<TContext : CommonBackendContext>(
|
||||
val context: TContext,
|
||||
private val factory: DefaultArgumentFunctionFactory,
|
||||
private val skipInlineMethods: Boolean = true,
|
||||
private val skipExternalMethods: Boolean = false,
|
||||
private val forceSetOverrideSymbols: Boolean = true,
|
||||
private val factory: DefaultArgumentFunctionFactory = MaskedDefaultArgumentFunctionFactory(context)
|
||||
private val forceSetOverrideSymbols: Boolean = true
|
||||
) : DeclarationTransformer {
|
||||
override val withLocalDeclarations: Boolean get() = true
|
||||
|
||||
@@ -255,12 +255,12 @@ open class DefaultArgumentStubGenerator(
|
||||
private fun log(msg: () -> String) = context.log { "DEFAULT-REPLACER: ${msg()}" }
|
||||
}
|
||||
|
||||
open class DefaultParameterInjector(
|
||||
open val context: CommonBackendContext,
|
||||
open class DefaultParameterInjector<TContext : CommonBackendContext>(
|
||||
protected val context: TContext,
|
||||
protected val factory: DefaultArgumentFunctionFactory,
|
||||
protected val skipInline: Boolean = true,
|
||||
protected val skipExternalMethods: Boolean = false,
|
||||
protected val forceSetOverrideSymbols: Boolean = true,
|
||||
protected val factory: DefaultArgumentFunctionFactory = MaskedDefaultArgumentFunctionFactory(context),
|
||||
) : IrElementTransformerVoid(), BodyLoweringPass {
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
@@ -499,8 +499,8 @@ class DefaultParameterPatchOverridenSymbolsLowering(
|
||||
}
|
||||
}
|
||||
|
||||
private class MaskedDefaultArgumentFunctionFactory(context: CommonBackendContext) : DefaultArgumentFunctionFactory(context) {
|
||||
override fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean) {
|
||||
open class MaskedDefaultArgumentFunctionFactory(context: CommonBackendContext) : DefaultArgumentFunctionFactory(context) {
|
||||
final override fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean) {
|
||||
copyAttributesFrom(original)
|
||||
copyTypeParametersFrom(original)
|
||||
copyReturnTypeFrom(original)
|
||||
|
||||
@@ -288,10 +288,6 @@ class JsIrBackendContext(
|
||||
}
|
||||
}
|
||||
|
||||
override fun unfoldInlineClassType(irType: IrType): IrType? {
|
||||
return inlineClassesUtils.getInlinedClass(irType)?.typeWith()
|
||||
}
|
||||
|
||||
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.DefaultArgumentFunctionFactory
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.copyTypeParametersFrom
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
@@ -17,13 +19,16 @@ import org.jetbrains.kotlin.ir.util.isTopLevel
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class JsDefaultArgumentFunctionFactory(override val context: JsIrBackendContext) : DefaultArgumentFunctionFactory(context) {
|
||||
class JsDefaultArgumentFunctionFactory(context: CommonBackendContext) : DefaultArgumentFunctionFactory(context) {
|
||||
|
||||
override fun IrType.hasNullAsUndefinedValue() = false
|
||||
|
||||
override fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean) {
|
||||
copyAttributesFrom(original)
|
||||
copyTypeParametersFrom(original)
|
||||
copyReturnTypeFrom(original)
|
||||
copyReceiversFrom(original)
|
||||
copyValueParametersFrom(original, wrapWithNullable = false)
|
||||
copyValueParametersFrom(original)
|
||||
|
||||
if (!original.isTopLevel) {
|
||||
introduceContextParam()
|
||||
|
||||
+5
-5
@@ -29,12 +29,12 @@ import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
|
||||
class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) :
|
||||
DefaultArgumentStubGenerator(
|
||||
context,
|
||||
class JsDefaultArgumentStubGenerator(context: JsIrBackendContext) :
|
||||
DefaultArgumentStubGenerator<JsIrBackendContext>(
|
||||
context = context,
|
||||
factory = JsDefaultArgumentFunctionFactory(context),
|
||||
skipExternalMethods = true,
|
||||
forceSetOverrideSymbols = false,
|
||||
factory = JsDefaultArgumentFunctionFactory(context)
|
||||
forceSetOverrideSymbols = false
|
||||
) {
|
||||
|
||||
private fun IrBuilderWithScope.createDefaultResolutionExpression(
|
||||
|
||||
+4
-5
@@ -22,16 +22,15 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.copyAnnotations
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.isTopLevel
|
||||
import org.jetbrains.kotlin.ir.util.isVararg
|
||||
|
||||
class JsDefaultParameterInjector(override val context: JsIrBackendContext) :
|
||||
DefaultParameterInjector(
|
||||
class JsDefaultParameterInjector(context: JsIrBackendContext) :
|
||||
DefaultParameterInjector<JsIrBackendContext>(
|
||||
context,
|
||||
factory = JsDefaultArgumentFunctionFactory(context),
|
||||
skipExternalMethods = true,
|
||||
forceSetOverrideSymbols = false,
|
||||
factory = JsDefaultArgumentFunctionFactory(context)
|
||||
forceSetOverrideSymbols = false
|
||||
) {
|
||||
override fun nullConst(startOffset: Int, endOffset: Int, irParameter: IrValueParameter): IrExpression? =
|
||||
if (irParameter.isVararg && !irParameter.hasDefaultValue()) {
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
internal class JvmDefaultArgumentFunctionFactory(context: CommonBackendContext) : MaskedDefaultArgumentFunctionFactory(context) {
|
||||
override fun IrType.hasNullAsUndefinedValue() =
|
||||
(InlineClassAbi.unboxType(this) ?: this) !in context.irBuiltIns.primitiveIrTypes
|
||||
}
|
||||
+6
-1
@@ -19,7 +19,12 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.isFinalClass
|
||||
import org.jetbrains.kotlin.ir.util.isTopLevelDeclaration
|
||||
|
||||
class JvmDefaultArgumentStubGenerator(override val context: JvmBackendContext) : DefaultArgumentStubGenerator(context, false, false) {
|
||||
class JvmDefaultArgumentStubGenerator(context: JvmBackendContext) : DefaultArgumentStubGenerator<JvmBackendContext>(
|
||||
context = context,
|
||||
factory = JvmDefaultArgumentFunctionFactory(context),
|
||||
skipInlineMethods = false,
|
||||
skipExternalMethods = false
|
||||
) {
|
||||
override fun defaultArgumentStubVisibility(function: IrFunction) = function.getJvmVisibilityOfDefaultArgumentStub()
|
||||
|
||||
override fun useConstructorMarker(function: IrFunction): Boolean =
|
||||
|
||||
+6
-4
@@ -16,10 +16,12 @@ import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
class JvmDefaultParameterInjector(context: JvmBackendContext) :
|
||||
DefaultParameterInjector(context, skipInline = false, skipExternalMethods = false) {
|
||||
|
||||
override val context: JvmBackendContext get() = super.context as JvmBackendContext
|
||||
class JvmDefaultParameterInjector(context: JvmBackendContext) : DefaultParameterInjector<JvmBackendContext>(
|
||||
context = context,
|
||||
factory = JvmDefaultArgumentFunctionFactory(context),
|
||||
skipInline = false,
|
||||
skipExternalMethods = false
|
||||
) {
|
||||
|
||||
override fun nullConst(startOffset: Int, endOffset: Int, irParameter: IrValueParameter): IrExpression? =
|
||||
nullConst(startOffset, endOffset, irParameter.type)
|
||||
|
||||
@@ -293,10 +293,6 @@ class JvmBackendContext(
|
||||
) : Ir<JvmBackendContext>(this) {
|
||||
override val symbols = JvmSymbols(this@JvmBackendContext, symbolTable)
|
||||
|
||||
override fun unfoldInlineClassType(irType: IrType): IrType? {
|
||||
return InlineClassAbi.unboxType(irType)
|
||||
}
|
||||
|
||||
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -369,7 +369,7 @@ private val addMainFunctionCallsLowering = makeCustomWasmModulePhase(
|
||||
)
|
||||
|
||||
private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
|
||||
{ context -> DefaultArgumentStubGenerator(context, skipExternalMethods = true) },
|
||||
{ context -> DefaultArgumentStubGenerator(context, MaskedDefaultArgumentFunctionFactory(context), skipExternalMethods = true) },
|
||||
name = "DefaultArgumentStubGenerator",
|
||||
description = "Generate synthetic stubs for functions with default parameter values"
|
||||
)
|
||||
@@ -382,7 +382,7 @@ private val defaultArgumentPatchOverridesPhase = makeWasmModulePhase(
|
||||
)
|
||||
|
||||
private val defaultParameterInjectorPhase = makeWasmModulePhase(
|
||||
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) },
|
||||
{ context -> DefaultParameterInjector(context, MaskedDefaultArgumentFunctionFactory(context), skipExternalMethods = true) },
|
||||
name = "DefaultParameterInjector",
|
||||
description = "Replace call site with default parameters with corresponding stub function",
|
||||
prerequisite = setOf(innerClassesLoweringPhase)
|
||||
|
||||
+2
-2
@@ -226,9 +226,9 @@ private val volatilePhase = createFileLoweringPhase(
|
||||
|
||||
private val defaultParameterExtentPhase = createFileLoweringPhase(
|
||||
{ context, irFile ->
|
||||
KonanDefaultArgumentStubGenerator(context).lower(irFile)
|
||||
NativeDefaultArgumentStubGenerator(context).lower(irFile)
|
||||
DefaultParameterCleaner(context, replaceDefaultValuesWithStubs = true).lower(irFile)
|
||||
KonanDefaultParameterInjector(context).lower(irFile)
|
||||
NativeDefaultParameterInjector(context).lower(irFile)
|
||||
},
|
||||
name = "DefaultParameterExtent",
|
||||
description = "Default parameter extent lowering",
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||
import org.jetbrains.kotlin.backend.konan.computePrimitiveBinaryTypeOrNull
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
internal class NativeDefaultArgumentFunctionFactory(context: CommonBackendContext) : MaskedDefaultArgumentFunctionFactory(context) {
|
||||
override fun IrType.hasNullAsUndefinedValue(): Boolean {
|
||||
val binaryType = computePrimitiveBinaryTypeOrNull() ?: return true
|
||||
return binaryType == PrimitiveBinaryType.POINTER || binaryType == PrimitiveBinaryType.VECTOR128
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -12,9 +12,11 @@ import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
|
||||
internal class KonanDefaultArgumentStubGenerator(override val context: Context)
|
||||
: DefaultArgumentStubGenerator(context, skipInlineMethods = false)
|
||||
{
|
||||
internal class NativeDefaultArgumentStubGenerator(context: Context) : DefaultArgumentStubGenerator<Context>(
|
||||
context = context,
|
||||
factory = NativeDefaultArgumentFunctionFactory(context),
|
||||
skipInlineMethods = false
|
||||
) {
|
||||
override fun IrBlockBodyBuilder.selectArgumentOrDefault(
|
||||
defaultFlag: IrExpression,
|
||||
parameter: IrValueParameter,
|
||||
+6
-3
@@ -15,11 +15,14 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
|
||||
internal class KonanDefaultParameterInjector(private val konanContext: KonanBackendContext)
|
||||
: DefaultParameterInjector(konanContext, skipInline = false) {
|
||||
internal class NativeDefaultParameterInjector(context: KonanBackendContext) : DefaultParameterInjector<KonanBackendContext>(
|
||||
context = context,
|
||||
factory = NativeDefaultArgumentFunctionFactory(context),
|
||||
skipInline = false
|
||||
) {
|
||||
|
||||
override fun nullConst(startOffset: Int, endOffset: Int, type: IrType): IrExpression {
|
||||
val symbols = konanContext.ir.symbols
|
||||
val symbols = context.ir.symbols
|
||||
|
||||
val nullConstOfEquivalentType = when (type.computePrimitiveBinaryTypeOrNull()) {
|
||||
null -> IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType)
|
||||
@@ -6487,6 +6487,10 @@ fileCheckTest("filecheck_intrinsics") {
|
||||
annotatedSource = project.file('filecheck/intrinsics.kt')
|
||||
}
|
||||
|
||||
fileCheckTest("filecheck_default_parameters_dont_box") {
|
||||
annotatedSource = project.file('filecheck/default_parameters_dont_box.kt')
|
||||
}
|
||||
|
||||
task kt53119_side_effect(type: KonanLocalTest) {
|
||||
useGoldenData = true
|
||||
source = "codegen/stringConcatenationTypeNarrowing/kt53119_side_effect.kt"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 A(val i: Int)
|
||||
value class B(val a: A)
|
||||
value class C(val s: String)
|
||||
|
||||
fun defaultInt(a: Int = 1, aa: Int = 1) = a
|
||||
fun defaultA(a: A = A(1), aa: A = A(1)) = a.i
|
||||
fun defaultB(b: B = B(A(1)), bb: B = B(A(1))) = b.a.i
|
||||
fun defaultC(c: C = C("1"), cc: C = C("1")) = c.s
|
||||
|
||||
// CHECK-LABEL: "kfun:#main(){}"
|
||||
fun main(){
|
||||
// CHECK-LABEL: entry
|
||||
// CHECK-NOT: <Int-box>
|
||||
// CHECK-NOT: <A-box>
|
||||
// CHECK-NOT: <B-box>
|
||||
// CHECK-NOT: <C-box>
|
||||
|
||||
defaultInt()
|
||||
defaultA()
|
||||
defaultB()
|
||||
defaultC()
|
||||
defaultInt(1)
|
||||
defaultA(A(1))
|
||||
defaultB(B(A(1)))
|
||||
defaultC(C("1"))
|
||||
defaultInt(1, 1)
|
||||
defaultA(A(1), A(1))
|
||||
defaultB(B(A(1)), B(A(1)))
|
||||
defaultC(C("1"), C("1"))
|
||||
// CHECK-LABEL: epilogue
|
||||
}
|
||||
Reference in New Issue
Block a user