JVM_IR move increment intrinsic handling to JvmOptimizationLowering

This commit is contained in:
Dmitry Petrov
2021-12-30 18:30:51 +03:00
committed by Space
parent b71179065c
commit 3b928e1780
6 changed files with 179 additions and 147 deletions
@@ -780,80 +780,17 @@ class ExpressionCodegen(
throw AssertionError("Non-mapped local declaration: ${irSymbol.owner.dump()}\n in ${irFunction.dump()}")
}
private fun handlePlusMinus(expression: IrSetValue, value: IrExpression?, isMinus: Boolean): Boolean {
if (value is IrConst<*> && value.kind == IrConstKind.Int) {
@Suppress("UNCHECKED_CAST")
val delta = (value as IrConst<Int>).value
val upperBound = Byte.MAX_VALUE.toInt() + (if (isMinus) 1 else 0)
val lowerBound = Byte.MIN_VALUE.toInt() + (if (isMinus) 1 else 0)
if (delta in lowerBound..upperBound) {
expression.markLineNumber(startOffset = true)
mv.iinc(findLocalIndex(expression.symbol), if (isMinus) -delta else delta)
return true
}
}
return false
}
private fun hasSameLineNumber(element0: IrElement, element1: IrElement): Boolean =
getLineNumberForOffset(element0.startOffset) == getLineNumberForOffset(element1.startOffset)
// Use iinc for all for the set var int special cases where we can.
// Be careful to make sure that debugging behavior does not change and
// only perform the optimization if that can be done without losing
// line number information.
private fun handleIntVariableSpecialCases(expression: IrSetValue): Boolean {
if (expression.symbol.owner.type.isInt()) {
when (expression.origin) {
IrStatementOrigin.PREFIX_INCR, IrStatementOrigin.PREFIX_DECR -> {
expression.markLineNumber(startOffset = true)
mv.iinc(findLocalIndex(expression.symbol), if (expression.origin == IrStatementOrigin.PREFIX_INCR) 1 else -1)
return true
}
IrStatementOrigin.PLUSEQ, IrStatementOrigin.MINUSEQ -> {
val argument = (expression.value as IrCall).getValueArgument(0)!!
if (!hasSameLineNumber(argument, expression)) {
return false
}
return handlePlusMinus(expression, argument, expression.origin is IrStatementOrigin.MINUSEQ)
}
IrStatementOrigin.EQ -> {
val value = expression.value
if (!hasSameLineNumber(value, expression)) {
return false
}
if (value is IrCall) {
val receiver = value.dispatchReceiver ?: return false
val symbol = expression.symbol
if (!hasSameLineNumber(receiver, expression)) {
return false
}
if (value.origin == IrStatementOrigin.PLUS || value.origin == IrStatementOrigin.MINUS) {
val argument = value.getValueArgument(0)!!
if (receiver is IrGetValue && receiver.symbol == symbol && hasSameLineNumber(argument, expression)) {
return handlePlusMinus(expression, argument, value.origin == IrStatementOrigin.MINUS)
}
}
}
}
}
}
return false
}
override fun visitSetValue(expression: IrSetValue, data: BlockInfo): PromisedValue {
if (!handleIntVariableSpecialCases(expression)) {
expression.value.markLineNumber(startOffset = true)
expression.value.accept(this, data).materializeAt(expression.symbol.owner.type)
// We set the value of parameters only for default values. The inliner accepts only
// a very specific bytecode pattern for default arguments and does not tolerate a
// line number on the store. Therefore, if we are storing to a parameter, we do not
// output a line number for the store.
if (expression.symbol !is IrValueParameterSymbol) {
expression.markLineNumber(startOffset = true)
}
mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType)
expression.value.markLineNumber(startOffset = true)
expression.value.accept(this, data).materializeAt(expression.symbol.owner.type)
// We set the value of parameters only for default values. The inliner accepts only
// a very specific bytecode pattern for default arguments and does not tolerate a
// line number on the store. Therefore, if we are storing to a parameter, we do not
// output a line number for the store.
if (expression.symbol !is IrValueParameterSymbol) {
expression.markLineNumber(startOffset = true)
}
mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType)
return unitValue
}
@@ -1414,22 +1351,26 @@ class ExpressionCodegen(
wrapIntoKClass: Boolean,
data: BlockInfo
): PromisedValue {
if (classReference is IrGetClass) {
// TODO transform one sort of access into the other?
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
} else if (classReference is IrClassReference) {
val classType = classReference.classType
val classifier = classType.classifierOrNull
if (classifier is IrTypeParameterSymbol) {
val success = putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS)
assert(success) {
"Non-reified type parameter under ::class should be rejected by type checker: ${classType.render()}"
}
when (classReference) {
is IrGetClass -> {
// TODO transform one sort of access into the other?
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
}
is IrClassReference -> {
val classType = classReference.classType
val classifier = classType.classifierOrNull
if (classifier is IrTypeParameterSymbol) {
val success = putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS)
assert(success) {
"Non-reified type parameter under ::class should be rejected by type checker: ${classType.render()}"
}
}
generateClassInstance(mv, classType, typeMapper)
} else {
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
generateClassInstance(mv, classType, typeMapper)
}
else -> {
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
}
}
if (wrapIntoKClass) {
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2021 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.intrinsics
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.org.objectweb.asm.Type
class IntIncr(private val isPrefix: Boolean) : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
val irGetValue = expression.getValueArgument(0) as? IrGetValue
?: error("IrGetValue expected as valueArgument #0: ${expression.dump()}")
val irDelta = expression.getValueArgument(1) as? IrConst<*>
?: error("IrConst expected as valueArgument #1: ${expression.dump()}")
if (irDelta.kind != IrConstKind.Int)
error("Int const expected: ${irDelta.dump()}")
val delta = IrConstKind.Int.valueOf(irDelta)
if (delta > Byte.MAX_VALUE || delta < Byte.MIN_VALUE)
error("Int const should be in (Byte.MIN_VALUE .. Byte.MAX_VALUE): ${irDelta.dump()}")
val varIndex = codegen.frameMap.getIndex(irGetValue.symbol)
if (varIndex == -1)
error("Unmapped variable: ${irGetValue.render()}")
if (isPrefix) {
codegen.mv.iinc(varIndex, delta)
irGetValue.accept(codegen, data).materialize()
} else {
irGetValue.accept(codegen, data).materialize()
codegen.mv.iinc(varIndex, delta)
}
return MaterialValue(codegen, Type.INT_TYPE, irGetValue.type)
}
}
@@ -90,8 +90,8 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException,
symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic,
symbols.jvmDebuggerInvokeSpecialIntrinsic.toKey()!! to JvmDebuggerInvokeSpecial,
symbols.intPostfixIncr.toKey()!! to PostfixIinc(1),
symbols.intPostfixDecr.toKey()!! to PostfixIinc(-1)
symbols.intPostfixIncrDecr.toKey()!! to IntIncr(isPrefix = false),
symbols.intPrefixIncrDecr.toKey()!! to IntIncr(isPrefix = true)
) +
numberConversionMethods() +
unaryFunForPrimitives("plus", UnaryPlus) +
@@ -1,26 +0,0 @@
/*
* Copyright 2010-2021 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.intrinsics
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.org.objectweb.asm.Type
class PostfixIinc(private val delta: Int) : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
val argument = expression.getValueArgument(0) as? IrGetValue
?: error("IrGetValue expected: ${expression.dump()}")
val varIndex = codegen.frameMap.getIndex(argument.symbol)
if (varIndex == -1)
error("Unmapped variable: ${argument.render()}")
argument.accept(codegen, data).materialize()
codegen.mv.iinc(varIndex, delta)
return MaterialValue(codegen, Type.INT_TYPE, argument.type)
}
}