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)
}
}
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineScopeResolver
import org.jetbrains.kotlin.backend.jvm.ir.findInlineCallSites
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrFileEntry
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irSetField
@@ -25,6 +27,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.*
@@ -72,10 +75,14 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
}
override fun lower(irFile: IrFile) {
irFile.transformChildren(Transformer(irFile.findInlineCallSites(context)), null)
irFile.transformChildren(Transformer(irFile.fileEntry, irFile.findInlineCallSites(context)), null)
}
private inner class Transformer(private val inlineScopeResolver: IrInlineScopeResolver) : IrElementTransformer<IrDeclaration?> {
private inner class Transformer(
private val fileEntry: IrFileEntry,
private val inlineScopeResolver: IrInlineScopeResolver
) : IrElementTransformer<IrDeclaration?> {
private val dontTouchTemporaryVals = HashSet<IrVariable>()
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclaration?): IrStatement =
@@ -368,39 +375,36 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
}
}
}
private fun IrStatement.isLoopVariable() =
this is IrVariable && origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE
private fun IrContainerExpression.rewritePostfixIncrDecr(): IrCall? {
return when (origin) {
IrStatementOrigin.POSTFIX_INCR, IrStatementOrigin.POSTFIX_DECR -> {
val tmpVar = this.statements[0] as? IrVariable ?: return null
val getIncrVar = tmpVar.initializer as? IrGetValue ?: return null
if (!getIncrVar.type.isInt()) return null
if (origin != IrStatementOrigin.POSTFIX_INCR && origin != IrStatementOrigin.POSTFIX_DECR) return null
val setVar = this.statements[1] as? IrSetValue ?: return null
if (setVar.symbol != getIncrVar.symbol) return null
val setVarValue = setVar.value as? IrCall ?: return null
val calleeName = setVarValue.symbol.owner.name
if (calleeName != OperatorNameConventions.INC && calleeName != OperatorNameConventions.DEC) return null
val calleeArg = setVarValue.dispatchReceiver as? IrGetValue ?: return null
if (calleeArg.symbol != tmpVar.symbol) return null
val tmpVar = this.statements[0] as? IrVariable ?: return null
val getIncrVar = tmpVar.initializer as? IrGetValue ?: return null
if (!getIncrVar.type.isInt()) return null
val getTmpVar = this.statements[2] as? IrGetValue ?: return null
if (getTmpVar.symbol != tmpVar.symbol) return null
val setVar = this.statements[1] as? IrSetValue ?: return null
if (setVar.symbol != getIncrVar.symbol) return null
val setVarValue = setVar.value as? IrCall ?: return null
val calleeName = setVarValue.symbol.owner.name
if (calleeName != OperatorNameConventions.INC && calleeName != OperatorNameConventions.DEC) return null
val calleeArg = setVarValue.dispatchReceiver as? IrGetValue ?: return null
if (calleeArg.symbol != tmpVar.symbol) return null
val intrinsicSymbol =
if (calleeName == OperatorNameConventions.INC)
context.ir.symbols.intPostfixIncr
else
context.ir.symbols.intPostfixDecr
val getTmpVar = this.statements[2] as? IrGetValue ?: return null
if (getTmpVar.symbol != tmpVar.symbol) return null
return IrCallImpl.fromSymbolOwner(this.startOffset, this.endOffset, intrinsicSymbol)
.apply { putValueArgument(0, getIncrVar) }
}
else ->
null
val delta = if (calleeName == OperatorNameConventions.INC)
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, 1)
else
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, -1)
return IrCallImpl.fromSymbolOwner(this.startOffset, this.endOffset, context.ir.symbols.intPostfixIncrDecr).apply {
putValueArgument(0, getIncrVar)
putValueArgument(1, delta)
}
}
@@ -417,6 +421,81 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
expression
}
}
override fun visitSetValue(expression: IrSetValue, data: IrDeclaration?): IrExpression {
expression.transformChildren(this, data)
return rewritePrefixIncrDecr(expression) ?: expression
}
private fun rewritePrefixIncrDecr(expression: IrSetValue): IrExpression? {
if (!expression.symbol.owner.type.isInt())
return null
when (expression.origin) {
IrStatementOrigin.PREFIX_INCR, IrStatementOrigin.PREFIX_DECR -> {
return prefixIncr(expression, if (expression.origin == IrStatementOrigin.PREFIX_INCR) 1 else -1)
}
IrStatementOrigin.PLUSEQ, IrStatementOrigin.MINUSEQ -> {
val argument = (expression.value as IrCall).getValueArgument(0)!!
if (!hasSameLineNumber(argument, expression)) {
return null
}
return rewriteCompoundAssignmentAsPrefixIncrDecr(expression, argument, expression.origin is IrStatementOrigin.MINUSEQ)
}
IrStatementOrigin.EQ -> {
val value = expression.value
if (!hasSameLineNumber(value, expression)) {
return null
}
if (value is IrCall) {
val receiver = value.dispatchReceiver
?: return null
val symbol = expression.symbol
if (!hasSameLineNumber(receiver, expression)) {
return null
}
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 rewriteCompoundAssignmentAsPrefixIncrDecr(
expression, argument, value.origin == IrStatementOrigin.MINUS
)
}
}
}
}
}
return null
}
private fun rewriteCompoundAssignmentAsPrefixIncrDecr(
expression: IrSetValue,
value: IrExpression?,
isMinus: Boolean
): IrExpression? {
if (value is IrConst<*> && value.kind == IrConstKind.Int) {
val delta = IrConstKind.Int.valueOf(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) {
return prefixIncr(expression, if (isMinus) -delta else delta)
}
}
return null
}
private fun prefixIncr(expression: IrSetValue, delta: Int): IrExpression {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
return IrCallImpl.fromSymbolOwner(startOffset, endOffset, context.ir.symbols.intPrefixIncrDecr).apply {
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, expression.symbol))
putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, delta))
}
}
private fun hasSameLineNumber(e1: IrElement, e2: IrElement) =
getLineNumberForOffset(e1.startOffset) == getLineNumberForOffset(e2.startOffset)
private fun getLineNumberForOffset(offset: Int): Int =
fileEntry.getLineNumber(offset) + 1
}
}
@@ -922,17 +922,18 @@ class JvmSymbols(
val remainderUnsignedLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("remainderUnsigned")
val toUnsignedStringLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("toUnsignedString")
val intPostfixIncr = createPostfixIncrDecrFun("<int++>", irBuiltIns.intType)
val intPostfixDecr = createPostfixIncrDecrFun("<int-->", irBuiltIns.intType)
val intPostfixIncrDecr = createIncrDecrFun("<int-postfix-incr-decr>")
val intPrefixIncrDecr = createIncrDecrFun("<int-prefix-incr-decr>")
private fun createPostfixIncrDecrFun(intrinsicName: String, type: IrType): IrSimpleFunctionSymbol =
private fun createIncrDecrFun(intrinsicName: String): IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special(intrinsicName)
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
}.apply {
parent = kotlinJvmInternalPackage
addValueParameter("value", type)
returnType = type
addValueParameter("value", irBuiltIns.intType)
addValueParameter("delta", irBuiltIns.intType)
returnType = irBuiltIns.intType
}.symbol
private fun createJavaPrimitiveClass(fqName: FqName, type: IrType): IrClassSymbol = createClass(fqName) { klass ->