Rematerialize simple expressions (local variables, literals, $this, $receiver) when generating calls.

This commit is contained in:
Dmitry Petrov
2016-08-17 13:09:59 +03:00
committed by Dmitry Petrov
parent c1583c4368
commit 287d804131
10 changed files with 118 additions and 46 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.generators.values.IrTemporaryVariableValue import org.jetbrains.kotlin.psi2ir.generators.values.IrTemporaryVariableValue
import org.jetbrains.kotlin.psi2ir.generators.values.IrValue import org.jetbrains.kotlin.psi2ir.generators.values.IrValue
import org.jetbrains.kotlin.psi2ir.generators.values.createRematerializableValue
import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.psi2ir.toExpectedType
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
@@ -42,12 +43,30 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
private val receiverValues = HashMap<ReceiverValue, IrValue>() private val receiverValues = HashMap<ReceiverValue, IrValue>()
private val valueArgumentValues = HashMap<ValueParameterDescriptor, IrValue>() private val valueArgumentValues = HashMap<ValueParameterDescriptor, IrValue>()
fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String? = null): IrVariable { fun introduceTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String? = null): IrVariable? {
val rematerializable = createRematerializableValue(irExpression)
if (rematerializable != null) {
putValue(ktExpression, rematerializable)
return null
}
val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, nameHint) val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, nameHint)
putValue(ktExpression, IrTemporaryVariableValue(irTmpVar)) putValue(ktExpression, IrTemporaryVariableValue(irTmpVar))
return irTmpVar return irTmpVar
} }
fun introduceTemporary(valueParameterDescriptor: ValueParameterDescriptor, irExpression: IrExpression): IrVariable? {
val rematerializable = createRematerializableValue(irExpression)
if (rematerializable != null) {
putValue(valueParameterDescriptor, rematerializable)
return null
}
val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, valueParameterDescriptor.name.asString())
putValue(valueParameterDescriptor, IrTemporaryVariableValue(irTmpVar))
return irTmpVar
}
fun putValue(ktExpression: KtExpression, irValue: IrValue) { fun putValue(ktExpression: KtExpression, irValue: IrValue) {
expressionValues[ktExpression] = irValue expressionValues[ktExpression] = irValue
} }
@@ -140,30 +159,30 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
// TODO use IrLetExpression? // TODO use IrLetExpression?
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val irBlock = IrBlockExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, resultType, val irBlock = IrBlockExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, resultType,
hasResult = isUsedAsExpression(ktExpression), hasResult = isUsedAsExpression(ktExpression),
isDesugared = true) isDesugared = true)
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>() val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
val valueParameter = resolvedCall.resultingDescriptor.valueParameters[index] val valueParameter = valueParameters[index]
valueArgumentsToValueParameters[valueArgument] = valueParameter valueArgumentsToValueParameters[valueArgument] = valueParameter
} }
val temporariesForValueArguments = HashMap<ResolvedValueArgument, Pair<VariableDescriptor, IrExpression>>()
for (valueArgument in valueArgumentsInEvaluationOrder) { for (valueArgument in valueArgumentsInEvaluationOrder) {
val valueParameter = valueArgumentsToValueParameters[valueArgument]!! val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue
val irTemporary = temporaryVariableFactory.createTemporaryVariable(irArgument, valueParameter.name.asString()) val irTmpArg = introduceTemporary(valueParameter, irArgument)
temporariesForValueArguments[valueArgument] = Pair(irTemporary.descriptor, irArgument) irBlock.addIfNotNull(irTmpArg)
irBlock.addStatement(irTemporary)
} }
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
val (temporaryDescriptor, irArgument) = temporariesForValueArguments[valueArgument]!! val valueParameter = valueParameters[index]
val valueParameter = resolvedCall.resultingDescriptor.valueParameters[index] val irGetTemporary = valueArgumentValues[valueParameter]!!.load()
val irGetTemporary = IrGetVariableExpressionImpl(irArgument.startOffset, irArgument.endOffset, temporaryDescriptor)
irCall.putArgument(index, irGetTemporary.toExpectedType(valueParameter.type)) irCall.putArgument(index, irGetTemporary.toExpectedType(valueParameter.type))
} }
@@ -28,15 +28,24 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi2ir.generators.values.* import org.jetbrains.kotlin.psi2ir.generators.values.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.types.expressions.OperatorConventions
val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf( val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf(
KtTokens.PLUSEQ to IrOperator.PLUSEQ, KtTokens.PLUSEQ to IrOperator.PLUSEQ,
KtTokens.MINUSEQ to IrOperator.MINUSEQ, KtTokens.MINUSEQ to IrOperator.MINUSEQ,
KtTokens.MULTEQ to IrOperator.MULTEQ, KtTokens.MULTEQ to IrOperator.MULTEQ,
KtTokens.DIVEQ to IrOperator.DIVEQ, KtTokens.DIVEQ to IrOperator.DIVEQ,
KtTokens.PERCEQ to IrOperator.PERCEQ KtTokens.PERCEQ to IrOperator.PERCEQ,
KtTokens.PLUS to IrOperator.PLUS,
KtTokens.MINUS to IrOperator.MINUS,
KtTokens.MUL to IrOperator.MUL,
KtTokens.DIV to IrOperator.DIV
) )
val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS
val BINARY_OPERATORS_WITH_CALLS = OperatorConventions.BINARY_OPERATION_NAMES.keys
class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator { class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator {
override val context: IrGeneratorContext get() = irStatementGenerator.context override val context: IrGeneratorContext get() = irStatementGenerator.context
@@ -45,7 +54,7 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
return when (ktOperator) { return when (ktOperator) {
KtTokens.EQ -> generateAssignment(expression) KtTokens.EQ -> generateAssignment(expression)
in KtTokens.AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator) in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
else -> createDummyExpression(expression, ktOperator.toString()) else -> createDummyExpression(expression, ktOperator.toString())
} }
} }
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.psi2ir.generators.values package org.jetbrains.kotlin.psi2ir.generators.values
import org.jetbrains.kotlin.ir.expressions.IrBlockExpression import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrBlockExpressionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -98,11 +95,11 @@ class IrIndexedLValue(
} }
private fun defineContextVariables(irBlock: IrBlockExpression, callGenerator: IrCallGenerator) { private fun defineContextVariables(irBlock: IrBlockExpression, callGenerator: IrCallGenerator) {
irBlock.addStatement(callGenerator.createTemporary(ktArrayAccessExpression.arrayExpression!!, irArray, "array")) irBlock.addIfNotNull(callGenerator.introduceTemporary(ktArrayAccessExpression.arrayExpression!!, irArray, "array"))
var index = 0 var index = 0
for ((ktIndexExpression, irIndexValue) in indexValues) { for ((ktIndexExpression, irIndexValue) in indexValues) {
irBlock.addStatement(callGenerator.createTemporary(ktIndexExpression, irIndexValue, "index${index++}")) irBlock.addIfNotNull(callGenerator.introduceTemporary(ktIndexExpression, irIndexValue, "index${index++}"))
} }
} }
} }
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir.generators.values
import org.jetbrains.kotlin.ir.expressions.*
interface IrRematerializableValue : IrValue
fun createRematerializableValue(irExpression: IrExpression): IrRematerializableValue? =
when (irExpression) {
is IrLiteralExpression<*> -> IrRematerializableLiteralValue(irExpression)
is IrGetVariableExpression -> IrRematerializableVariableValue(irExpression)
is IrGetExtensionReceiverExpression -> IrRematerializableExtensionReceiverValue(irExpression)
is IrThisExpression -> IrRematerializableThisValue(irExpression)
else -> null
}
class IrRematerializableLiteralValue(val irExpression: IrLiteralExpression<*>): IrRematerializableValue {
override fun load(): IrExpression =
IrLiteralExpressionImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type,
irExpression.kind, irExpression.kind.valueOf(irExpression))
}
class IrRematerializableVariableValue(val irExpression: IrGetVariableExpression) : IrRematerializableValue {
override fun load(): IrExpression =
IrGetVariableExpressionImpl(irExpression.startOffset, irExpression.endOffset, irExpression.descriptor)
}
class IrRematerializableExtensionReceiverValue(val irExpression: IrGetExtensionReceiverExpression) : IrRematerializableValue {
override fun load(): IrExpression =
IrGetExtensionReceiverExpressionImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.descriptor)
}
class IrRematerializableThisValue(val irExpression: IrThisExpression): IrRematerializableValue {
override fun load(): IrExpression =
IrThisExpressionImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.classDescriptor)
}
@@ -33,6 +33,10 @@ interface IrBlockExpression : IrExpression {
fun addStatement(statement: IrStatement) fun addStatement(statement: IrStatement)
} }
fun IrBlockExpression.addIfNotNull(statement: IrStatement?) {
if (statement != null) addStatement(statement)
}
class IrBlockExpressionImpl( class IrBlockExpressionImpl(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
+2 -6
View File
@@ -5,11 +5,7 @@ IrFile /arrayAssignment.kt
VAR val x: kotlin.IntArray VAR val x: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator= CALL .intArrayOf type=kotlin.IntArray operator=
elements: DUMMY vararg type=kotlin.Int elements: DUMMY vararg type=kotlin.Int
VAR val tmp0_array: kotlin.IntArray
GET_VAR x type=kotlin.IntArray
VAR val tmp1_index0: kotlin.Int
LITERAL Int type=kotlin.Int value='1'
CALL .set type=kotlin.Unit operator=EQ CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR tmp0_array type=kotlin.IntArray $this: GET_VAR x type=kotlin.IntArray
index: GET_VAR tmp1_index0 type=kotlin.Int index: LITERAL Int type=kotlin.Int value='1'
value: LITERAL Int type=kotlin.Int value='0' value: LITERAL Int type=kotlin.Int value='0'
+9 -15
View File
@@ -10,27 +10,21 @@ IrFile /arrayAugmentedAssignment1.kt
BLOCK type=<no-type> hasResult=false isDesugared=false BLOCK type=<no-type> hasResult=false isDesugared=false
VAR var x: kotlin.IntArray VAR var x: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator= CALL .foo type=kotlin.IntArray operator=
VAR val tmp0_array: kotlin.IntArray
GET_VAR x type=kotlin.IntArray
VAR val tmp1_index0: kotlin.Int
LITERAL Int type=kotlin.Int value='0'
CALL .set type=kotlin.Unit operator=PLUSEQ CALL .set type=kotlin.Unit operator=PLUSEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray $this: GET_VAR x type=kotlin.IntArray
index: GET_VAR tmp1_index0 type=kotlin.Int index: LITERAL Int type=kotlin.Int value='0'
value: CALL .plus type=kotlin.Int operator=PLUSEQ value: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .get type=kotlin.Int operator=PLUSEQ $this: CALL .get type=kotlin.Int operator=PLUSEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray $this: GET_VAR x type=kotlin.IntArray
index: GET_VAR tmp1_index0 type=kotlin.Int index: LITERAL Int type=kotlin.Int value='0'
other: LITERAL Int type=kotlin.Int value='1' other: LITERAL Int type=kotlin.Int value='1'
VAR val tmp2_array: kotlin.IntArray VAR val tmp0_array: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator= CALL .foo type=kotlin.IntArray operator=
VAR val tmp3_index0: kotlin.Int
LITERAL Int type=kotlin.Int value='0'
CALL .set type=kotlin.Unit operator=MULTEQ CALL .set type=kotlin.Unit operator=MULTEQ
$this: GET_VAR tmp2_array type=kotlin.IntArray $this: GET_VAR tmp0_array type=kotlin.IntArray
index: GET_VAR tmp3_index0 type=kotlin.Int index: LITERAL Int type=kotlin.Int value='0'
value: CALL .times type=kotlin.Int operator=MULTEQ value: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .get type=kotlin.Int operator=MULTEQ $this: CALL .get type=kotlin.Int operator=MULTEQ
$this: GET_VAR tmp2_array type=kotlin.IntArray $this: GET_VAR tmp0_array type=kotlin.IntArray
index: GET_VAR tmp3_index0 type=kotlin.Int index: LITERAL Int type=kotlin.Int value='0'
other: LITERAL Int type=kotlin.Int value='2' other: LITERAL Int type=kotlin.Int value='2'
+4 -8
View File
@@ -4,16 +4,12 @@ IrFile /arrayAugmentedAssignment2.kt
IrFunction public fun IB.test(/*0*/ a: IA): kotlin.Unit IrFunction public fun IB.test(/*0*/ a: IA): kotlin.Unit
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false BLOCK type=<no-type> hasResult=false isDesugared=false
VAR val tmp0_array: IA
GET_VAR a type=IA
VAR val tmp1_index0: kotlin.String
LITERAL String type=kotlin.String value=''
CALL .set type=kotlin.Unit operator=PLUSEQ CALL .set type=kotlin.Unit operator=PLUSEQ
$this: $RECEIVER of: test type=IB $this: $RECEIVER of: test type=IB
$receiver: GET_VAR tmp0_array type=IA $receiver: GET_VAR a type=IA
index: GET_VAR tmp1_index0 type=kotlin.String index: LITERAL String type=kotlin.String value=''
value: CALL .plus type=kotlin.Int operator=PLUSEQ value: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .get type=kotlin.Int operator=PLUSEQ $this: CALL .get type=kotlin.Int operator=PLUSEQ
$this: GET_VAR tmp0_array type=IA $this: GET_VAR a type=IA
index: GET_VAR tmp1_index0 type=kotlin.String index: LITERAL String type=kotlin.String value=''
other: LITERAL Int type=kotlin.Int value='42' other: LITERAL Int type=kotlin.Int value='42'
@@ -9,4 +9,5 @@ fun reordered2() = 2
fun test() { fun test() {
foo(a = noReorder1(), b = noReorder2()) foo(a = noReorder1(), b = noReorder2())
foo(b = reordered1(), a = reordered2()) foo(b = reordered1(), a = reordered2())
foo(b = 1, a = reordered2())
} }
@@ -35,3 +35,8 @@ IrFile /callWithReorderedArguments.kt
CALL .foo type=kotlin.Unit operator= CALL .foo type=kotlin.Unit operator=
a: GET_VAR tmp1_a type=kotlin.Int a: GET_VAR tmp1_a type=kotlin.Int
b: GET_VAR tmp0_b type=kotlin.Int b: GET_VAR tmp0_b type=kotlin.Int
VAR val tmp2_a: kotlin.Int
CALL .reordered2 type=kotlin.Int operator=
CALL .foo type=kotlin.Unit operator=
a: GET_VAR tmp2_a type=kotlin.Int
b: LITERAL Int type=kotlin.Int value='1'