JVM_IR move increment intrinsic handling to JvmOptimizationLowering
This commit is contained in:
+27
-86
@@ -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) {
|
||||
|
||||
+37
@@ -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)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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) +
|
||||
|
||||
-26
@@ -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)
|
||||
}
|
||||
}
|
||||
+107
-28
@@ -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 ->
|
||||
|
||||
Reference in New Issue
Block a user