psi2ir: don't generate temporaries in dynamic array augmented assignment

In constructs such as 'd[i] += x', where both indexed get and indexed
set are dynamic calls, it's safe to generate augmented assignment body
directly, without temporary variables for array ('d') and index ('i').
Note that corresponding IntermediateValue's are OnceExpressionValue's,
which would throw an exception if this assumption is violated.
This commit is contained in:
Dmitry Petrov
2019-02-13 14:16:54 +03:00
parent 580eb6fcac
commit 77cbd10f9c
3 changed files with 76 additions and 96 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.psi2ir.generators.pregenerateValueArgumentsUsing import org.jetbrains.kotlin.psi2ir.generators.pregenerateValueArgumentsUsing
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
class ArrayAccessAssignmentReceiver( class ArrayAccessAssignmentReceiver(
@@ -42,32 +43,59 @@ class ArrayAccessAssignmentReceiver(
private val origin: IrStatementOrigin private val origin: IrStatementOrigin
) : AssignmentReceiver { ) : AssignmentReceiver {
private val indexedGetDescriptor = indexedGetResolvedCall?.resultingDescriptor
private val indexedSetDescriptor = indexedSetResolvedCall?.resultingDescriptor
private val descriptor = private val descriptor =
indexedGetResolvedCall?.resultingDescriptor indexedGetDescriptor
?: indexedSetResolvedCall?.resultingDescriptor ?: indexedSetDescriptor
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call") ?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
override fun assign(withLValue: (LValue) -> IrExpression): IrExpression { override fun assign(withLValue: (LValue) -> IrExpression): IrExpression {
val kotlinType: KotlinType = val kotlinType: KotlinType =
indexedGetResolvedCall?.run { resultingDescriptor.returnType!! } indexedGetDescriptor?.returnType
?: indexedSetResolvedCall?.run { resultingDescriptor.valueParameters.last().type } ?: indexedSetDescriptor?.run { valueParameters.last().type }
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call") ?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
val hasResult = origin.isAssignmentOperatorWithResult() val hasResult = origin.isAssignmentOperatorWithResult()
val resultType = if (hasResult) kotlinType else callGenerator.context.builtIns.unitType val resultType = if (hasResult) kotlinType else callGenerator.context.builtIns.unitType
val irResultType = callGenerator.translateType(resultType) val irResultType = callGenerator.translateType(resultType)
if (indexedGetDescriptor?.isDynamic() != false && indexedSetDescriptor?.isDynamic() != false) {
return withLValue(
createLValue(kotlinType, OnceExpressionValue(irArray)) { _, irIndex ->
OnceExpressionValue(irIndex)
}
)
}
val irBlock = IrBlockImpl(startOffset, endOffset, irResultType, origin) val irBlock = IrBlockImpl(startOffset, endOffset, irResultType, origin)
val irArrayValue = callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irArray, irBlock, "array") val irArrayValue = callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irArray, irBlock, "array")
val ktExpressionToIrIndexValue = HashMap<KtExpression, IntermediateValue>() irBlock.inlineStatement(
withLValue(
createLValue(kotlinType, irArrayValue) { i, irIndex ->
callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irIndex, irBlock, "index$i")
}
)
)
return irBlock
}
private fun createLValue(
kotlinType: KotlinType,
irArrayValue: IntermediateValue,
createIndexValue: (Int, IrExpression) -> IntermediateValue
): LValueWithGetterAndSetterCalls {
val ktExpressionToIrIndexValue = HashMap<KtExpression, IntermediateValue>()
for ((i, irIndex) in irIndexExpressions.withIndex()) { for ((i, irIndex) in irIndexExpressions.withIndex()) {
ktExpressionToIrIndexValue[ktIndexExpressions[i]] = ktExpressionToIrIndexValue[ktIndexExpressions[i]] =
callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irIndex, irBlock, "index$i") createIndexValue(i, irIndex)
} }
val irLValue = LValueWithGetterAndSetterCalls( return LValueWithGetterAndSetterCalls(
callGenerator, callGenerator,
descriptor, descriptor,
{ indexedGetCall()?.fillArrayAndIndexArguments(irArrayValue, indexedGetResolvedCall!!, ktExpressionToIrIndexValue) }, { indexedGetCall()?.fillArrayAndIndexArguments(irArrayValue, indexedGetResolvedCall!!, ktExpressionToIrIndexValue) },
@@ -75,9 +103,6 @@ class ArrayAccessAssignmentReceiver(
callGenerator.translateType(kotlinType), callGenerator.translateType(kotlinType),
startOffset, endOffset, origin startOffset, endOffset, origin
) )
irBlock.inlineStatement(withLValue(irLValue))
return irBlock
} }
override fun assign(value: IrExpression): IrExpression { override fun assign(value: IrExpression): IrExpression {
@@ -2,53 +2,28 @@ FILE fqName:<root> fileName:/dynamicArrayAugmentedAssignment.kt
FUN name:testArrayAugmentedAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags: FUN name:testArrayAugmentedAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags: VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY BLOCK_BODY
BLOCK type=kotlin.Unit origin=PLUSEQ DYN_OP operator=PLUSEQ type=kotlin.Unit
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="KEY"
CONST String type=kotlin.String value="KEY" 0: CONST String type=kotlin.String value="+="
DYN_OP operator=PLUSEQ type=kotlin.Unit DYN_OP operator=MINUSEQ type=kotlin.Unit
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp0_array: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp1_index0: String' type=kotlin.String origin=null 0: CONST String type=kotlin.String value="KEY"
0: CONST String type=kotlin.String value="+=" 0: CONST String type=kotlin.String value="-="
BLOCK type=kotlin.Unit origin=MINUSEQ DYN_OP operator=MULEQ type=kotlin.Unit
VAR IR_TEMPORARY_VARIABLE name:tmp2_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp3_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="KEY"
CONST String type=kotlin.String value="KEY" 0: CONST String type=kotlin.String value="*="
DYN_OP operator=MINUSEQ type=kotlin.Unit DYN_OP operator=DIVEQ type=kotlin.Unit
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp2_array: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp3_index0: String' type=kotlin.String origin=null 0: CONST String type=kotlin.String value="KEY"
0: CONST String type=kotlin.String value="-=" 0: CONST String type=kotlin.String value="/="
BLOCK type=kotlin.Unit origin=MULTEQ DYN_OP operator=MODEQ type=kotlin.Unit
VAR IR_TEMPORARY_VARIABLE name:tmp4_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp5_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="KEY"
CONST String type=kotlin.String value="KEY" 0: CONST String type=kotlin.String value="%="
DYN_OP operator=MULEQ type=kotlin.Unit
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp4_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp5_index0: String' type=kotlin.String origin=null
0: CONST String type=kotlin.String value="*="
BLOCK type=kotlin.Unit origin=DIVEQ
VAR IR_TEMPORARY_VARIABLE name:tmp6_array type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp7_index0 type:kotlin.String flags:val
CONST String type=kotlin.String value="KEY"
DYN_OP operator=DIVEQ type=kotlin.Unit
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp6_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp7_index0: String' type=kotlin.String origin=null
0: CONST String type=kotlin.String value="/="
BLOCK type=kotlin.Unit origin=PERCEQ
VAR IR_TEMPORARY_VARIABLE name:tmp8_array type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp9_index0 type:kotlin.String flags:val
CONST String type=kotlin.String value="KEY"
DYN_OP operator=MODEQ type=kotlin.Unit
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp8_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp9_index0: String' type=kotlin.String origin=null
0: CONST String type=kotlin.String value="%="
@@ -3,42 +3,22 @@ FILE fqName:<root> fileName:/dynamicArrayIncrementDecrement.kt
VALUE_PARAMETER name:d index:0 type:dynamic flags: VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY BLOCK_BODY
VAR name:t1 type:dynamic flags:val VAR name:t1 type:dynamic flags:val
BLOCK type=dynamic origin=PREFIX_INCR DYN_OP operator=PREFIX_INCREMENT type=dynamic
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="prefixIncr"
CONST String type=kotlin.String value="prefixIncr"
DYN_OP operator=PREFIX_INCREMENT type=dynamic
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp0_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp1_index0: String' type=kotlin.String origin=null
VAR name:t2 type:dynamic flags:val VAR name:t2 type:dynamic flags:val
BLOCK type=dynamic origin=PREFIX_DECR DYN_OP operator=PREFIX_DECREMENT type=dynamic
VAR IR_TEMPORARY_VARIABLE name:tmp2_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp3_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="prefixDecr"
CONST String type=kotlin.String value="prefixDecr"
DYN_OP operator=PREFIX_DECREMENT type=dynamic
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp2_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp3_index0: String' type=kotlin.String origin=null
VAR name:t3 type:dynamic flags:val VAR name:t3 type:dynamic flags:val
BLOCK type=dynamic origin=POSTFIX_INCR DYN_OP operator=POSTFIX_INCREMENT type=dynamic
VAR IR_TEMPORARY_VARIABLE name:tmp4_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp5_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="postfixIncr"
CONST String type=kotlin.String value="postfixIncr"
DYN_OP operator=POSTFIX_INCREMENT type=dynamic
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp4_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp5_index0: String' type=kotlin.String origin=null
VAR name:t4 type:dynamic flags:val VAR name:t4 type:dynamic flags:val
BLOCK type=dynamic origin=POSTFIX_DECR DYN_OP operator=POSTFIX_DECREMENT type=dynamic
VAR IR_TEMPORARY_VARIABLE name:tmp6_array type:dynamic flags:val receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp7_index0 type:kotlin.String flags:val 0: CONST String type=kotlin.String value="postfixDecr"
CONST String type=kotlin.String value="postfixDecr"
DYN_OP operator=POSTFIX_DECREMENT type=dynamic
receiver: DYN_OP operator=ARRAY_ACCESS type=dynamic
receiver: GET_VAR 'tmp6_array: dynamic' type=dynamic origin=null
0: GET_VAR 'tmp7_index0: String' type=kotlin.String origin=null