JVM_IR: make primitive == object slightly less lazy.
Discarding the value used to leave an unused-but-never-destroyed temporary variable. It's best to not separate calls to `enterTemp` and `leaveTemp`. Not sure what kind of test to add though, since this is minor -- if the result of the comparison is discarded, then the entire statement is more or less pointless. #KT-42251 Fixed
This commit is contained in:
Generated
+5
@@ -1765,6 +1765,11 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
|
|||||||
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unusedPrimitiveAndObjectEquals.kt")
|
||||||
|
public void testUnusedPrimitiveAndObjectEquals() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/unusedPrimitiveAndObjectEquals.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
||||||
|
|||||||
+34
-30
@@ -135,49 +135,53 @@ class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PrimitiveToObjectComparison(
|
class PrimitiveToObjectComparison(
|
||||||
val op: IElementType,
|
private val op: IElementType,
|
||||||
private val boxedValue: MaterialValue,
|
private val leftIsPrimitive: Boolean,
|
||||||
private val useNullCheck: Boolean,
|
private val left: MaterialValue,
|
||||||
private val primitiveType: Type,
|
private val right: MaterialValue
|
||||||
private val loadOther: () -> MaterialValue,
|
) : BooleanValue(left.codegen) {
|
||||||
) : BooleanValue(boxedValue.codegen) {
|
private fun checkTypeAndCompare(onWrongType: Label): BooleanValue {
|
||||||
|
|
||||||
override fun jumpIfFalse(target: Label) {
|
|
||||||
val compareLabel = Label()
|
val compareLabel = Label()
|
||||||
|
// If it's the left value that needs unboxing, it should be moved to the top of the stack. `AsmUtil.swap`
|
||||||
|
// is theoretically OK, but in practice breaks peephole optimization passes that unbox longs/doubles,
|
||||||
|
// so just storing in a variable is safer.
|
||||||
|
val tmp = if (leftIsPrimitive) -1 else codegen.frameMap.enterTemp(right.type).also { mv.store(it, right.type) }
|
||||||
mv.dup()
|
mv.dup()
|
||||||
if (useNullCheck) {
|
if (AsmUtil.isBoxedPrimitiveType(if (leftIsPrimitive) right.type else left.type)) {
|
||||||
mv.ifnonnull(compareLabel)
|
mv.ifnonnull(compareLabel)
|
||||||
} else {
|
} else {
|
||||||
mv.instanceOf(AsmUtil.boxType(primitiveType))
|
mv.instanceOf(AsmUtil.boxType(if (leftIsPrimitive) left.type else right.type))
|
||||||
mv.ifne(compareLabel)
|
mv.ifne(compareLabel)
|
||||||
}
|
}
|
||||||
mv.pop()
|
// Type checking of the object failed, values are irrelevant now:
|
||||||
mv.goTo(target)
|
if (leftIsPrimitive) right.discard() // else it's already popped by `mv.store`
|
||||||
|
left.discard()
|
||||||
|
mv.goTo(onWrongType)
|
||||||
mv.mark(compareLabel)
|
mv.mark(compareLabel)
|
||||||
val unboxedValue = boxedValue.materializedAt(primitiveType, boxedValue.irType)
|
// Type checking OK, can unbox and compare:
|
||||||
BooleanComparison(op, unboxedValue, loadOther()).jumpIfFalse(target)
|
return if (leftIsPrimitive) {
|
||||||
|
BooleanComparison(op, left, right.materializedAt(left.type, right.irType))
|
||||||
|
} else {
|
||||||
|
val leftUnboxed = left.materializedAt(right.type, left.irType)
|
||||||
|
mv.load(tmp, right.type)
|
||||||
|
codegen.frameMap.leaveTemp(right.type)
|
||||||
|
BooleanComparison(op, leftUnboxed, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun jumpIfFalse(target: Label) {
|
||||||
|
checkTypeAndCompare(target).jumpIfFalse(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun jumpIfTrue(target: Label) {
|
override fun jumpIfTrue(target: Label) {
|
||||||
val compareLabel = Label()
|
val wrongType = Label()
|
||||||
val endLabel = Label()
|
checkTypeAndCompare(wrongType).jumpIfTrue(target)
|
||||||
mv.dup()
|
mv.mark(wrongType)
|
||||||
if (useNullCheck) {
|
|
||||||
mv.ifnonnull(compareLabel)
|
|
||||||
} else {
|
|
||||||
mv.instanceOf(AsmUtil.boxType(primitiveType))
|
|
||||||
mv.ifne(compareLabel)
|
|
||||||
}
|
|
||||||
mv.pop()
|
|
||||||
mv.goTo(endLabel)
|
|
||||||
mv.mark(compareLabel)
|
|
||||||
val unboxedValue = boxedValue.materializedAt(primitiveType, boxedValue.irType)
|
|
||||||
BooleanComparison(op, unboxedValue, loadOther()).jumpIfTrue(target)
|
|
||||||
mv.mark(endLabel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun discard() {
|
override fun discard() {
|
||||||
boxedValue.discard()
|
right.discard()
|
||||||
|
left.discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.codegen.StackValue
|
|||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.ir.util.isEnumEntry
|
|||||||
import org.jetbrains.kotlin.ir.util.isIntegerConst
|
import org.jetbrains.kotlin.ir.util.isIntegerConst
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.isNullable
|
import org.jetbrains.kotlin.types.isNullable
|
||||||
@@ -83,21 +81,6 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
|
|||||||
val rightType = with(codegen) { b.asmType }
|
val rightType = with(codegen) { b.asmType }
|
||||||
val opToken = expression.origin
|
val opToken = expression.origin
|
||||||
|
|
||||||
fun loadOther(expression: IrExpression, type: Type): () -> MaterialValue {
|
|
||||||
return if (expression.hasNoSideEffects()) {
|
|
||||||
{ expression.accept(codegen, data).materializedAt(type, expression.type) }
|
|
||||||
} else {
|
|
||||||
val aValue = expression.accept(codegen, data).materializedAt(type, expression.type)
|
|
||||||
val local = codegen.frameMap.enterTemp(type)
|
|
||||||
codegen.mv.store(local, type)
|
|
||||||
({
|
|
||||||
codegen.mv.load(local, type)
|
|
||||||
codegen.frameMap.leaveTemp(type)
|
|
||||||
aValue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid boxing for `primitive == object` and `boxed primitive == primitive` where we know
|
// Avoid boxing for `primitive == object` and `boxed primitive == primitive` where we know
|
||||||
// what comparison means. The optimization does not apply to `object == primitive` as equals
|
// what comparison means. The optimization does not apply to `object == primitive` as equals
|
||||||
// could be overridden for the object.
|
// could be overridden for the object.
|
||||||
@@ -105,19 +88,9 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
|
|||||||
((AsmUtil.isIntOrLongPrimitive(leftType) && !AsmUtil.isPrimitive(rightType)) ||
|
((AsmUtil.isIntOrLongPrimitive(leftType) && !AsmUtil.isPrimitive(rightType)) ||
|
||||||
(AsmUtil.isIntOrLongPrimitive(rightType) && AsmUtil.isBoxedPrimitiveType(leftType)))
|
(AsmUtil.isIntOrLongPrimitive(rightType) && AsmUtil.isBoxedPrimitiveType(leftType)))
|
||||||
) {
|
) {
|
||||||
val leftIsPrimitive = AsmUtil.isIntOrLongPrimitive(leftType)
|
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
|
||||||
val primitiveType = if (leftIsPrimitive) leftType else rightType
|
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
|
||||||
val nonPrimitiveType = if (leftIsPrimitive) rightType else leftType
|
return PrimitiveToObjectComparison(operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue)
|
||||||
val useNullCheck = AsmUtil.isBoxedPrimitiveType(nonPrimitiveType)
|
|
||||||
return if (leftIsPrimitive) {
|
|
||||||
val loadOther = loadOther(a, leftType)
|
|
||||||
val boxedValue = b.accept(codegen, data).materializedAt(rightType, b.type)
|
|
||||||
PrimitiveToObjectComparison(operator, boxedValue, useNullCheck, primitiveType, loadOther)
|
|
||||||
} else {
|
|
||||||
val boxedValue = a.accept(codegen, data).materializedAt(leftType, a.type)
|
|
||||||
val loadOther = loadOther(b, rightType)
|
|
||||||
PrimitiveToObjectComparison(operator, boxedValue, useNullCheck, primitiveType, loadOther)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val aIsEnum = a.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
val aIsEnum = a.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
var x = 1
|
||||||
|
|
||||||
|
fun box() {
|
||||||
|
val y: Number = 1
|
||||||
|
x == y
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 equals
|
||||||
|
// JVM_TEMPLATES
|
||||||
|
// 1 ICMP
|
||||||
|
// JVM_IR_TEMPLATES
|
||||||
|
// 0 ICMP
|
||||||
@@ -1810,6 +1810,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unusedPrimitiveAndObjectEquals.kt")
|
||||||
|
public void testUnusedPrimitiveAndObjectEquals() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/unusedPrimitiveAndObjectEquals.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
||||||
|
|||||||
+5
@@ -1765,6 +1765,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
public void testSimpleConstructorNotRedundantNotOptimizable() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundantNotOptimizable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unusedPrimitiveAndObjectEquals.kt")
|
||||||
|
public void testUnusedPrimitiveAndObjectEquals() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/deadCodeElimination/unusedPrimitiveAndObjectEquals.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/defaultArguments")
|
||||||
|
|||||||
Reference in New Issue
Block a user