Specialize Comparable#compareTo for boxed primitives
Support Comparable#compareTo for boxed primitive in redundant boxing/unboxing analysis, along with CHECKCAST to java.lang.Comparable. Note that we can do that for Float and Double, too, because Float#compareTo(Float) and Double#compareTo(Double) are delegated to Float#compare(float, float) and Double#compare(double, double), respectively. Fuse specialized comparison for integers with conditional jumps if possible (both for Comparable#compareTo and Intrinsics#areEqual). #KT-11959 Fixed
This commit is contained in:
+26
-12
@@ -75,6 +75,10 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
||||
onAreEqual(insn, values[0] as BoxedBasicValue, values[1] as BoxedBasicValue)
|
||||
value
|
||||
}
|
||||
insn.isJavaLangComparableCompareToForSameTypedBoxedValues(values) -> {
|
||||
onCompareTo(insn, values[0] as BoxedBasicValue, values[1] as BoxedBasicValue)
|
||||
value
|
||||
}
|
||||
else -> {
|
||||
// N-ary operation should be a method call or multinewarray.
|
||||
// Arguments for multinewarray could be only numeric,
|
||||
@@ -127,6 +131,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
||||
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
||||
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
|
||||
protected open fun onAreEqual(insn: AbstractInsnNode, value1: BoxedBasicValue, value2: BoxedBasicValue) {}
|
||||
protected open fun onCompareTo(insn: AbstractInsnNode, value1: BoxedBasicValue, value2: BoxedBasicValue) {}
|
||||
protected open fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {}
|
||||
protected open fun onMergeFail(value: BoxedBasicValue) {}
|
||||
protected open fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {}
|
||||
@@ -150,7 +155,7 @@ fun AbstractInsnNode.isPrimitiveUnboxing() =
|
||||
isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isJavaLangClassUnboxing() =
|
||||
fun AbstractInsnNode.isJavaLangClassUnboxing() =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner == "kotlin/jvm/JvmClassMappingKt" &&
|
||||
name == "getJavaClass" &&
|
||||
@@ -185,7 +190,7 @@ private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
|
||||
return desc == Type.getMethodDescriptor(ownerType, AsmUtil.unboxType(ownerType))
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isJavaLangClassBoxing() =
|
||||
fun AbstractInsnNode.isJavaLangClassBoxing() =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner == AsmTypes.REFLECTION &&
|
||||
name == "getOrCreateKotlinClass" &&
|
||||
@@ -210,16 +215,15 @@ fun isProgressionClass(type: Type) =
|
||||
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
||||
|
||||
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||
isAreEqualIntrinsic() && run {
|
||||
if (values.size != 2) return false
|
||||
isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
|
||||
|
||||
val (v1, v2) = values
|
||||
if (v1 !is BoxedBasicValue || v2 !is BoxedBasicValue) return false
|
||||
|
||||
val d1 = v1.descriptor
|
||||
val d2 = v2.descriptor
|
||||
d1.unboxedType == d2.unboxedType
|
||||
}
|
||||
fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean {
|
||||
if (values.size != 2) return false
|
||||
val (v1, v2) = values
|
||||
return v1 is BoxedBasicValue &&
|
||||
v2 is BoxedBasicValue &&
|
||||
v1.descriptor.unboxedType == v2.descriptor.unboxedType
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isAreEqualIntrinsic() =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
@@ -232,4 +236,14 @@ fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
|
||||
!values.any {
|
||||
val unboxedType = getUnboxedType(it.type)
|
||||
unboxedType == Type.DOUBLE_TYPE || unboxedType == Type.FLOAT_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
||||
|
||||
fun AbstractInsnNode.isJavaLangComparableCompareTo() =
|
||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||
name == "compareTo" &&
|
||||
owner == "java/lang/Comparable" &&
|
||||
desc == "(Ljava/lang/Object;)I"
|
||||
}
|
||||
|
||||
+9
@@ -91,6 +91,13 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
||||
descriptor1.addInsn(insn)
|
||||
}
|
||||
|
||||
override fun onCompareTo(insn: AbstractInsnNode, value1: BoxedBasicValue, value2: BoxedBasicValue) {
|
||||
val descriptor1 = value1.descriptor
|
||||
val descriptor2 = value2.descriptor
|
||||
candidatesBoxedValues.merge(descriptor1, descriptor2)
|
||||
descriptor1.addInsn(insn)
|
||||
}
|
||||
|
||||
override fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {
|
||||
markValueAsDirty(value)
|
||||
}
|
||||
@@ -133,6 +140,8 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
||||
true
|
||||
Type.getInternalName(Number::class.java) ->
|
||||
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
|
||||
"java/lang/Comparable" ->
|
||||
true
|
||||
else ->
|
||||
value.type.internalName == targetInternalName
|
||||
}
|
||||
|
||||
+148
-18
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||
@@ -324,16 +325,40 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
adaptAreEqualIntrinsic(node, insn, value);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// fall-through to default
|
||||
else if (BoxingInterpreterKt.isJavaLangClassBoxing(insn) || BoxingInterpreterKt.isJavaLangClassUnboxing(insn)) {
|
||||
node.instructions.remove(insn);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
else {
|
||||
throwCannotAdaptInstruction(insn);
|
||||
}
|
||||
case Opcodes.INVOKEINTERFACE:
|
||||
if (BoxingInterpreterKt.isJavaLangComparableCompareTo(insn)) {
|
||||
adaptJavaLangComparableCompareTo(node, insn, value);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
throwCannotAdaptInstruction(insn);
|
||||
}
|
||||
case Opcodes.CHECKCAST:
|
||||
case Opcodes.INVOKEVIRTUAL:
|
||||
// CHECKCAST or unboxing-method call
|
||||
node.instructions.remove(insn);
|
||||
break;
|
||||
default:
|
||||
throwCannotAdaptInstruction(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsic(@NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedValueDescriptor value) {
|
||||
private static void throwCannotAdaptInstruction(@NotNull AbstractInsnNode insn) {
|
||||
throw new AssertionError("Cannot adapt instruction: " + InlineCodegenUtil.getInsnText(insn));
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsic(
|
||||
@NotNull MethodNode node,
|
||||
@NotNull AbstractInsnNode insn,
|
||||
@NotNull BoxedValueDescriptor value
|
||||
) {
|
||||
Type unboxedType = value.getUnboxedType();
|
||||
|
||||
switch (unboxedType.getSort()) {
|
||||
@@ -355,32 +380,137 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsicForInt(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
LabelNode lNotEqual = new LabelNode(new Label());
|
||||
LabelNode lDone = new LabelNode(new Label());
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.IF_ICMPNE, lNotEqual));
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_1));
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.GOTO, lDone));
|
||||
node.instructions.insertBefore(insn, lNotEqual);
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_0));
|
||||
node.instructions.insertBefore(insn, lDone);
|
||||
|
||||
node.instructions.remove(insn);
|
||||
AbstractInsnNode next = insn.getNext();
|
||||
if (next != null && (next.getOpcode() == Opcodes.IFEQ || next.getOpcode() == Opcodes.IFNE)) {
|
||||
fuseAreEqualWithBranch(node, insn, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ);
|
||||
node.instructions.remove(insn);
|
||||
node.instructions.remove(next);
|
||||
}
|
||||
else {
|
||||
ifEqual1Else0(node, insn, Opcodes.IF_ICMPNE);
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsicForLong(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.LCMP));
|
||||
ifEqual1Else0(node, insn);
|
||||
node.instructions.remove(insn);
|
||||
AbstractInsnNode next = insn.getNext();
|
||||
if (next != null && (next.getOpcode() == Opcodes.IFEQ || next.getOpcode() == Opcodes.IFNE)) {
|
||||
fuseAreEqualWithBranch(node, insn, Opcodes.IFNE, Opcodes.IFEQ);
|
||||
node.instructions.remove(insn);
|
||||
node.instructions.remove(next);
|
||||
}
|
||||
else {
|
||||
ifEqual1Else0(node, insn, Opcodes.IFNE);
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ifEqual1Else0(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
private static void fuseAreEqualWithBranch(
|
||||
@NotNull MethodNode node,
|
||||
@NotNull AbstractInsnNode insn,
|
||||
int ifEqualOpcode,
|
||||
int ifNotEqualOpcode
|
||||
) {
|
||||
AbstractInsnNode next = insn.getNext();
|
||||
assert next instanceof JumpInsnNode : "JumpInsnNode expected: " + next;
|
||||
LabelNode nextLabel = ((JumpInsnNode) next).label;
|
||||
if (next.getOpcode() == Opcodes.IFEQ) {
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(ifEqualOpcode, nextLabel));
|
||||
}
|
||||
else if (next.getOpcode() == Opcodes.IFNE) {
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(ifNotEqualOpcode, nextLabel));
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("IFEQ or IFNE expected: " + InlineCodegenUtil.getInsnOpcodeText(next));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ifEqual1Else0(@NotNull MethodNode node, @NotNull AbstractInsnNode insn, int ifneOpcode) {
|
||||
LabelNode lNotEqual = new LabelNode(new Label());
|
||||
LabelNode lDone = new LabelNode(new Label());
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.IFNE, lNotEqual));
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(ifneOpcode, lNotEqual));
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_1));
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.GOTO, lDone));
|
||||
node.instructions.insertBefore(insn, lNotEqual);
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_0));
|
||||
node.instructions.insertBefore(insn, lDone);
|
||||
}
|
||||
|
||||
private static void adaptJavaLangComparableCompareTo(
|
||||
@NotNull MethodNode node,
|
||||
@NotNull AbstractInsnNode insn,
|
||||
@NotNull BoxedValueDescriptor value
|
||||
) {
|
||||
Type unboxedType = value.getUnboxedType();
|
||||
|
||||
switch (unboxedType.getSort()) {
|
||||
case Type.BOOLEAN:
|
||||
case Type.BYTE:
|
||||
case Type.SHORT:
|
||||
case Type.INT:
|
||||
case Type.CHAR:
|
||||
adaptJavaLangComparableCompareToForInt(node, insn);
|
||||
break;
|
||||
case Type.LONG:
|
||||
adaptJavaLangComparableCompareToForLong(node, insn);
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
adaptJavaLangComparableCompareToForFloat(node, insn);
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
adaptJavaLangComparableCompareToForDouble(node, insn);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unexpected unboxed type kind: " + unboxedType);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptJavaLangComparableCompareToForInt(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
AbstractInsnNode next = insn.getNext();
|
||||
AbstractInsnNode next2 = next == null ? null : next.getNext();
|
||||
if (next != null && next2 != null &&
|
||||
next.getOpcode() == Opcodes.ICONST_0 &&
|
||||
next2.getOpcode() >= Opcodes.IF_ICMPEQ && next2.getOpcode() <= Opcodes.IF_ICMPLE) {
|
||||
// Fuse: compareTo + ICONST_0 + IF_ICMPxx -> IF_ICMPxx
|
||||
node.instructions.remove(insn);
|
||||
node.instructions.remove(next);
|
||||
}
|
||||
else if (next != null &&
|
||||
next.getOpcode() >= Opcodes.IFEQ && next.getOpcode() <= Opcodes.IFLE) {
|
||||
// Fuse: compareTo + IFxx -> IF_ICMPxx
|
||||
LabelNode nextLabel = ((JumpInsnNode) next).label;
|
||||
int ifCmpOpcode = next.getOpcode() - Opcodes.IFEQ + Opcodes.IF_ICMPEQ;
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(ifCmpOpcode, nextLabel));
|
||||
node.instructions.remove(insn);
|
||||
node.instructions.remove(next);
|
||||
}
|
||||
else {
|
||||
// Can't fuse with branching instruction.
|
||||
// Trick: convert I, I on stack to L, L and use LCMP.
|
||||
// This is more compact than explicit branching.
|
||||
// TODO Generate 'java.lang.Integer#compare(int, int)' in targets >= JVM 1.7
|
||||
|
||||
// Initial stack: I1 I2
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.SWAP)); // I2 I1
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.I2L)); // L2 I1
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.DUP2_X1)); // L2 I1 L2
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.POP2)); // I1 L2
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.I2L)); // L1 L2
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.LCMP)); // compare(L1, L2)
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptJavaLangComparableCompareToForLong(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
node.instructions.set(insn, new InsnNode(Opcodes.LCMP));
|
||||
}
|
||||
|
||||
private static void adaptJavaLangComparableCompareToForFloat(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
node.instructions.set(insn, new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Float", "compare", "(FF)I", false));
|
||||
}
|
||||
|
||||
private static void adaptJavaLangComparableCompareToForDouble(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
node.instructions.set(insn, new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Double", "compare", "(DD)I", false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
inline fun ltx(a: Comparable<Any>, b: Any) = a < b
|
||||
inline fun lex(a: Comparable<Any>, b: Any) = a <= b
|
||||
inline fun gex(a: Comparable<Any>, b: Any) = a >= b
|
||||
inline fun gtx(a: Comparable<Any>, b: Any) = a > b
|
||||
|
||||
inline fun lt(a: Any, b: Any) = ltx(a as Comparable<Any>, b)
|
||||
inline fun le(a: Any, b: Any) = lex(a as Comparable<Any>, b)
|
||||
inline fun ge(a: Any, b: Any) = gex(a as Comparable<Any>, b)
|
||||
inline fun gt(a: Any, b: Any) = gtx(a as Comparable<Any>, b)
|
||||
|
||||
val ONE = 1
|
||||
val ONEL = 1L
|
||||
|
||||
fun box(): String {
|
||||
return when {
|
||||
!lt(ONE, 42) -> "Fail 1 LT"
|
||||
lt(42, ONE) -> "Fail 2 LT"
|
||||
|
||||
!le(ONE, 42) -> "Fail 1 LE"
|
||||
le(42, ONE) -> "Fail 2 LE"
|
||||
!le(1, ONE) -> "Fail 3 LE"
|
||||
|
||||
!ge(42, ONE) -> "Fail 1 GE"
|
||||
ge(ONE, 42) -> "Fail 2 GE"
|
||||
!ge(1, ONE) -> "Fail 3 GE"
|
||||
|
||||
gt(ONE, 42) -> "Fail 1 GT"
|
||||
!gt(42, ONE) -> "Fail 2 GT"
|
||||
|
||||
!lt(ONEL, 42L) -> "Fail 1 LT L"
|
||||
lt(42L, ONEL) -> "Fail 2 LT L"
|
||||
|
||||
!le(ONEL, 42L) -> "Fail 1 LE L"
|
||||
le(42L, ONEL) -> "Fail 2 LE L"
|
||||
!le(ONEL, 1L) -> "Fail 3 LE L"
|
||||
|
||||
!ge(42L, ONEL) -> "Fail 1 GE L"
|
||||
ge(ONEL, 42L) -> "Fail 2 GE L"
|
||||
!ge(ONEL, 1L) -> "Fail 3 GE L"
|
||||
|
||||
gt(ONEL, 42L) -> "Fail 1 GT L"
|
||||
!gt(42L, ONEL) -> "Fail 2 GT L"
|
||||
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
inline fun eq(a: Any, b: Any) = a == b
|
||||
inline fun ne(a: Any, b: Any) = a != b
|
||||
|
||||
val ONE = 1
|
||||
val ONEL = 1L
|
||||
|
||||
fun box(): String {
|
||||
return when {
|
||||
eq(ONE, 2) -> "Fail 1"
|
||||
!eq(ONE, 1) -> "Fail 2"
|
||||
!ne(ONE, 2) -> "Fail 3"
|
||||
ne(ONE, 1) -> "Fail 4"
|
||||
|
||||
eq(ONEL, 42L) -> "Fail 1L"
|
||||
!eq(ONEL, 1L) -> "Fail 2L"
|
||||
!ne(ONEL, 42L) -> "Fail 3L"
|
||||
ne(ONEL, 1L) -> "Fail 4L"
|
||||
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
|
||||
inline fun ltx(a: Comparable<Any>, b: Any) = a < b
|
||||
inline fun lex(a: Comparable<Any>, b: Any) = a <= b
|
||||
inline fun gex(a: Comparable<Any>, b: Any) = a >= b
|
||||
inline fun gtx(a: Comparable<Any>, b: Any) = a > b
|
||||
|
||||
inline fun lt(a: Any, b: Any) = ltx(a as Comparable<Any>, b)
|
||||
inline fun le(a: Any, b: Any) = lex(a as Comparable<Any>, b)
|
||||
inline fun ge(a: Any, b: Any) = gex(a as Comparable<Any>, b)
|
||||
inline fun gt(a: Any, b: Any) = gtx(a as Comparable<Any>, b)
|
||||
|
||||
val PLUS0F = 0.0F
|
||||
val MINUS0F = -0.0F
|
||||
val PLUS0D = 0.0
|
||||
val MINUS0D = -0.0
|
||||
|
||||
fun box(): String {
|
||||
return when {
|
||||
!lt(1.0F, 42.0F) -> "Fail 1 LT F"
|
||||
lt(42.0F, 1.0F) -> "Fail 2 LT F"
|
||||
|
||||
!le(1.0F, 42.0F) -> "Fail 1 LE F"
|
||||
le(42.0F, 1.0F) -> "Fail 2 LE F"
|
||||
!le(1.0F, 1.0F) -> "Fail 3 LE F"
|
||||
|
||||
!ge(42.0F, 1.0F) -> "Fail 1 GE F"
|
||||
ge(1.0F, 42.0F) -> "Fail 2 GE F"
|
||||
!ge(1.0F, 1.0F) -> "Fail 3 GE F"
|
||||
|
||||
gt(1.0F, 42.0F) -> "Fail 1 GT F"
|
||||
!gt(42.0F, 1.0F) -> "Fail 2 GT F"
|
||||
|
||||
!lt(1.0, 42.0) -> "Fail 1 LT D"
|
||||
lt(42.0, 1.0) -> "Fail 2 LT D"
|
||||
|
||||
!le(1.0, 42.0) -> "Fail 1 LE D"
|
||||
le(42.0, 1.0) -> "Fail 2 LE D"
|
||||
!le(1.0, 1.0) -> "Fail 3 LE D"
|
||||
|
||||
!ge(42.0, 1.0) -> "Fail 1 GE D"
|
||||
ge(1.0, 42.0) -> "Fail 2 GE D"
|
||||
!ge(1.0, 1.0) -> "Fail 3 GE D"
|
||||
|
||||
gt(1.0, 42.0) -> "Fail 1 GT D"
|
||||
!gt(42.0, 1.0) -> "Fail 2 GT D"
|
||||
|
||||
!lt(MINUS0F, PLUS0F) -> "Fail 1 LT +-0 F"
|
||||
lt(PLUS0F, MINUS0F) -> "Fail 2 LT +-0 F"
|
||||
|
||||
!le(MINUS0F, PLUS0F) -> "Fail 1 LE +-0 F"
|
||||
le(PLUS0F, MINUS0F) -> "Fail 2 LE +-0 F"
|
||||
!le(MINUS0F, MINUS0F) -> "Fail 3 LE +-0 F"
|
||||
!le(PLUS0F, PLUS0F) -> "Fail 3 LE +-0 F"
|
||||
|
||||
ge(MINUS0F, PLUS0F) -> "Fail 1 GE +-0 F"
|
||||
!ge(PLUS0F, MINUS0F) -> "Fail 2 GE +-0 F"
|
||||
!ge(MINUS0F, MINUS0F) -> "Fail 3 GE +-0 F"
|
||||
!ge(PLUS0F, PLUS0F) -> "Fail 3 GE +-0 F"
|
||||
|
||||
gt(MINUS0F, PLUS0F) -> "Fail 1 GT +-0 F"
|
||||
!gt(PLUS0F, MINUS0F) -> "Fail 2 GT +-0 F"
|
||||
|
||||
!lt(MINUS0D, PLUS0D) -> "Fail 1 LT +-0 D"
|
||||
lt(PLUS0D, MINUS0D) -> "Fail 2 LT +-0 D"
|
||||
|
||||
!le(MINUS0D, PLUS0D) -> "Fail 1 LE +-0 D"
|
||||
le(PLUS0D, MINUS0D) -> "Fail 2 LE +-0 D"
|
||||
!le(MINUS0D, MINUS0D) -> "Fail 3 LE +-0 D"
|
||||
!le(PLUS0D, PLUS0D) -> "Fail 3 LE +-0 D"
|
||||
|
||||
ge(MINUS0D, PLUS0D) -> "Fail 1 GE +-0 D"
|
||||
!ge(PLUS0D, MINUS0D) -> "Fail 2 GE +-0 D"
|
||||
!ge(MINUS0D, MINUS0D) -> "Fail 3 GE +-0 D"
|
||||
!ge(PLUS0D, PLUS0D) -> "Fail 3 GE +-0 D"
|
||||
|
||||
gt(MINUS0D, PLUS0D) -> "Fail 1 GT +-0 D"
|
||||
!gt(PLUS0D, MINUS0D) -> "Fail 2 GT +-0 D"
|
||||
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fun box(): String {
|
||||
val a: Any = 1
|
||||
val b: Any = 42
|
||||
val test = (a as Comparable<Any>).compareTo(b)
|
||||
if (test != -1) return "Fail: $test"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
val intList = listOf(1, 2, 3)
|
||||
val longList = listOf(1L, 2L, 3L)
|
||||
|
||||
val intListMin = intList.minBy { it }
|
||||
if (intListMin != 1) return "Fail intListMin=$intListMin"
|
||||
|
||||
val intListMax = intList.maxBy { it }
|
||||
if (intListMax != 3) return "Fail intListMax=$intListMax"
|
||||
|
||||
val longListMin = longList.minBy { it }
|
||||
if (longListMin != 1L) return "Fail longListMin=$longListMin"
|
||||
|
||||
val longListMax = longList.maxBy { it }
|
||||
if (longListMax != 3L) return "Fail longListMax=$longListMax"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// WITH_RUNTIME
|
||||
// FILE: list.kt
|
||||
val intList = listOf(1, 2, 3)
|
||||
val longList = listOf(1L, 2L, 3L)
|
||||
|
||||
// FILE: box.kt
|
||||
fun box(): String {
|
||||
val intListMin = intList.minBy { it } ?: -1
|
||||
if (intListMin != 1) return "Fail intListMin=$intListMin"
|
||||
|
||||
val intListMax = intList.maxBy { it } ?: -1
|
||||
if (intListMax != 3) return "Fail intListMax=$intListMax"
|
||||
|
||||
val longListMin = longList.minBy { it } ?: -1
|
||||
if (longListMin != 1L) return "Fail longListMin=$longListMin"
|
||||
|
||||
val longListMax = longList.maxBy { it } ?: -1
|
||||
if (longListMax != 3L) return "Fail longListMax=$longListMax"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// @BoxKt.class:
|
||||
// -- no boxing
|
||||
// 0 valueOf
|
||||
// -- no compareTo
|
||||
// 0 compareTo
|
||||
// -- comparisons are properly fused with conditional jumps
|
||||
// 0 ICONST_0
|
||||
// 1 IF_ICMPGE
|
||||
// 1 IF_ICMPLE
|
||||
// 4 LCMP
|
||||
// 1 IFGE
|
||||
// 1 IFLE
|
||||
+30
@@ -896,6 +896,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/boxingOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedIntegersCmp.kt")
|
||||
public void testBoxedIntegersCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedIntegersCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedPrimitivesAreEqual.kt")
|
||||
public void testBoxedPrimitivesAreEqual() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedPrimitivesAreEqual.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
public void testCasts() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/casts.kt");
|
||||
@@ -926,6 +944,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intCompareTo.kt")
|
||||
public void testIntCompareTo() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/intCompareTo.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt15871.kt")
|
||||
public void testKt15871() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/kt15871.kt");
|
||||
@@ -962,6 +986,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("maxMinBy.kt")
|
||||
public void testMaxMinBy() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/maxMinBy.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheck.kt")
|
||||
public void testNullCheck() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/nullCheck.kt");
|
||||
|
||||
@@ -896,6 +896,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/boxingOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedIntegersCmp.kt")
|
||||
public void testBoxedIntegersCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedIntegersCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedPrimitivesAreEqual.kt")
|
||||
public void testBoxedPrimitivesAreEqual() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedPrimitivesAreEqual.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
public void testCasts() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/casts.kt");
|
||||
@@ -926,6 +944,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intCompareTo.kt")
|
||||
public void testIntCompareTo() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/intCompareTo.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt15871.kt")
|
||||
public void testKt15871() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/kt15871.kt");
|
||||
@@ -962,6 +986,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("maxMinBy.kt")
|
||||
public void testMaxMinBy() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/maxMinBy.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheck.kt")
|
||||
public void testNullCheck() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/nullCheck.kt");
|
||||
|
||||
@@ -515,6 +515,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("maxMinBy.kt")
|
||||
public void testMaxMinBy() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/maxMinBy.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheck.kt")
|
||||
public void testNullCheck() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/nullCheck.kt");
|
||||
|
||||
@@ -896,6 +896,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/boxingOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedIntegersCmp.kt")
|
||||
public void testBoxedIntegersCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedIntegersCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedPrimitivesAreEqual.kt")
|
||||
public void testBoxedPrimitivesAreEqual() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedPrimitivesAreEqual.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
public void testCasts() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/casts.kt");
|
||||
@@ -926,6 +944,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intCompareTo.kt")
|
||||
public void testIntCompareTo() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/intCompareTo.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt15871.kt")
|
||||
public void testKt15871() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/kt15871.kt");
|
||||
@@ -962,6 +986,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("maxMinBy.kt")
|
||||
public void testMaxMinBy() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/maxMinBy.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheck.kt")
|
||||
public void testNullCheck() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/nullCheck.kt");
|
||||
|
||||
+36
@@ -1076,6 +1076,30 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/boxingOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedIntegersCmp.kt")
|
||||
public void testBoxedIntegersCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedIntegersCmp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedPrimitivesAreEqual.kt")
|
||||
public void testBoxedPrimitivesAreEqual() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedPrimitivesAreEqual.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
public void testCasts() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/casts.kt");
|
||||
@@ -1118,6 +1142,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intCompareTo.kt")
|
||||
public void testIntCompareTo() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/intCompareTo.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt15871.kt")
|
||||
public void testKt15871() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/kt15871.kt");
|
||||
@@ -1160,6 +1190,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("maxMinBy.kt")
|
||||
public void testMaxMinBy() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/maxMinBy.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheck.kt")
|
||||
public void testNullCheck() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/nullCheck.kt");
|
||||
|
||||
Reference in New Issue
Block a user