JVM_IR KT-50076 avoid CHECKCAST on moved dispatch receiver parameter

This commit is contained in:
Dmitry Petrov
2021-12-13 10:33:04 +03:00
committed by Space
parent 6f148c594f
commit df460a842b
16 changed files with 223 additions and 11 deletions
@@ -1050,6 +1050,12 @@ class ExpressionCodegen(
MaterialValue(this, boxedRightType, expression.type)
}
IrTypeOperator.REINTERPRET_CAST -> {
val targetType = typeMapper.mapType(typeOperand)
expression.argument.accept(this, data).materialize()
MaterialValue(this, targetType, typeOperand)
}
IrTypeOperator.INSTANCEOF -> {
expression.argument.accept(this, data).materializeAt(context.irBuiltIns.anyNType)
val type = typeMapper.boxType(typeOperand)
@@ -29,6 +29,9 @@ import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -88,6 +91,7 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
val irFunction = context.cachedDeclarations.getDefaultImplsRedirection(classOverride)
val superMethod = firstSuperMethodFromKotlin(irFunction, interfaceImplementation).owner
val superClassType = superMethod.parentAsClass.defaultType
val defaultImplFun = context.cachedDeclarations.getDefaultImplsFunction(superMethod)
val classStartOffset = classOverride.parentAsClass.startOffset
context.createIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply {
@@ -100,7 +104,12 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size)
var offset = 0
irFunction.dispatchReceiverParameter?.let { putValueArgument(offset++, irGet(it)) }
irFunction.dispatchReceiverParameter?.let {
putValueArgument(
offset++,
irGet(it).reinterpretAsDispatchReceiverOfType(superClassType)
)
}
irFunction.extensionReceiverParameter?.let { putValueArgument(offset++, irGet(it)) }
irFunction.valueParameters.mapIndexed { i, parameter -> putValueArgument(i + offset, irGet(parameter)) }
}
@@ -153,7 +162,8 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl
}
override fun visitCall(expression: IrCall): IrExpression {
if (expression.superQualifierSymbol?.owner?.isInterface != true || expression.isSuperToAny()) {
val superQualifierClass = expression.superQualifierSymbol?.owner
if (superQualifierClass == null || !superQualifierClass.isInterface || expression.isSuperToAny()) {
return super.visitCall(expression)
}
@@ -162,10 +172,35 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl
val redirectTarget = context.cachedDeclarations.getDefaultImplsFunction(superCallee)
val newCall = createDelegatingCallWithPlaceholderTypeArguments(expression, redirectTarget, context.irBuiltIns)
postprocessMovedThis(newCall)
return super.visitCall(newCall)
}
private fun postprocessMovedThis(irCall: IrCall) {
val movedThisParameter = irCall.symbol.owner.valueParameters
.find { it.origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER }
?: return
val movedThisParameterIndex = movedThisParameter.index
irCall.putValueArgument(
movedThisParameterIndex,
irCall.getValueArgument(movedThisParameterIndex)?.reinterpretAsDispatchReceiverOfType(movedThisParameter.type)
)
}
}
// Given a dispatch receiver expression, wrap it in REINTERPRET_CAST to the given type,
// unless it's a value of inline class (which could be boxed at this point).
// Avoids a CHECKCAST on a moved dispatch receiver argument.
internal fun IrExpression.reinterpretAsDispatchReceiverOfType(irType: IrType): IrExpression =
if (this.type.isInlineClassType())
this
else
IrTypeOperatorCallImpl(
this.startOffset, this.endOffset,
irType, IrTypeOperator.REINTERPRET_CAST, irType,
this
)
internal val interfaceDefaultCallsPhase = makeIrFilePhase(
lowering = ::InterfaceDefaultCallsLowering,
name = "InterfaceDefaultCalls",
@@ -84,7 +84,9 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
override fun visitCall(expression: IrCall, data: IrDeclaration?): IrExpression {
expression.transformChildren(this, data)
if (expression.symbol.owner.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) {
val callee = expression.symbol.owner
if (callee.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) {
return optimizePropertyAccess(expression, data)
}
@@ -50,7 +50,6 @@ enum class IrTypeOperator {
/**
* SAM conversion: value of functional type F is used where Single Abstract Method interface value is expected.
* Currently this is possible in Kotlin/JVM only, however, there's a big demand for SAM conversion for Kotlin interfaces.
*/
SAM_CONVERSION,
@@ -61,7 +60,8 @@ enum class IrTypeOperator {
IMPLICIT_DYNAMIC_CAST,
/**
* C-like reinterpret_cast<T> using as primitive type operation in JS
* C-like reinterpret_cast<T> using as primitive type operation in JS.
* On JVM, tells back-end to treat argument as a value of a given type (even though exact JVM types might differ).
*/
REINTERPRET_CAST;
}