'when' should use intrinsics for '=='

#KT-19029 Fixed Target versions 1.1.5
 #KT-18818 Fixed Target versions 1.1.5
This commit is contained in:
Dmitry Petrov
2017-07-17 14:43:37 +03:00
parent 20cd748377
commit 2ed5a5e368
13 changed files with 281 additions and 51 deletions
@@ -152,14 +152,6 @@ public class AsmUtil {
return isIntPrimitive(type) || type == Type.LONG_TYPE;
}
public static boolean isNumberPrimitiveOrBoolean(Type type) {
return isNumberPrimitive(type) || type.getSort() == Type.BOOLEAN;
}
public static boolean isNumberPrimitive(Type type) {
return isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
}
public static boolean isPrimitive(Type type) {
return type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY;
}
@@ -2840,7 +2840,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
else if (opToken == KtTokens.EQEQ || opToken == KtTokens.EXCLEQ ||
opToken == KtTokens.EQEQEQ || opToken == KtTokens.EXCLEQEQEQ) {
return generateEquals(expression.getLeft(), expression.getRight(), opToken);
return generateEquals(expression.getLeft(), expression.getRight(), opToken, null);
}
else if (opToken == KtTokens.LT || opToken == KtTokens.LTEQ ||
opToken == KtTokens.GT || opToken == KtTokens.GTEQ) {
@@ -2885,44 +2885,76 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.or(gen(expression.getLeft()), gen(expression.getRight()));
}
private StackValue generateEquals(@Nullable KtExpression left, @Nullable KtExpression right, @NotNull IElementType opToken) {
private StackValue genLazyUnlessProvided(@Nullable StackValue pregenerated, @NotNull KtExpression expr, @NotNull Type type) {
return pregenerated != null ? StackValue.coercion(pregenerated, type) : genLazy(expr, type);
}
private StackValue genUnlessProvided(@Nullable StackValue pregenerated, @NotNull KtExpression expr, @NotNull Type type) {
if (pregenerated != null) {
pregenerated.put(type, v);
}
else {
gen(expr, type);
}
return StackValue.onStack(type);
}
private StackValue generateEquals(
@Nullable KtExpression left,
@Nullable KtExpression right,
@NotNull IElementType opToken,
@Nullable StackValue pregeneratedLeft
) {
Type leftType = expressionType(left);
Type rightType = expressionType(right);
if (KtPsiUtil.isNullConstant(left)) {
return genCmpWithNull(right, opToken);
return genCmpWithNull(right, opToken, null);
}
if (KtPsiUtil.isNullConstant(right)) {
return genCmpWithNull(left, opToken);
return genCmpWithNull(left, opToken, pregeneratedLeft);
}
if (isIntZero(left, leftType) && isIntPrimitive(rightType)) {
return genCmpWithZero(right, opToken);
return genCmpWithZero(right, opToken, null);
}
if (isIntZero(right, rightType) && isIntPrimitive(leftType)) {
return genCmpWithZero(left, opToken);
return genCmpWithZero(left, opToken, pregeneratedLeft);
}
if (left instanceof KtSafeQualifiedExpression && isPrimitive(rightType)) {
if (pregeneratedLeft == null && left instanceof KtSafeQualifiedExpression && isPrimitive(rightType)) {
return genCmpSafeCallToPrimitive((KtSafeQualifiedExpression) left, right, rightType, opToken);
}
if (isPrimitive(leftType) && right instanceof KtSafeQualifiedExpression) {
return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken);
return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken, pregeneratedLeft);
}
if (BoxedToPrimitiveEquality.isApplicable(opToken, leftType, rightType)) {
return BoxedToPrimitiveEquality.create(opToken, genLazy(left, leftType), leftType, genLazy(right, rightType), rightType,
myFrameMap);
return BoxedToPrimitiveEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedLeft, left, leftType), leftType,
genLazy(right, rightType), rightType,
myFrameMap
);
}
if (PrimitiveToBoxedEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToBoxedEquality.create(opToken, genLazy(left, leftType), leftType, genLazy(right, rightType), rightType);
return PrimitiveToBoxedEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedLeft, left, leftType), leftType,
genLazy(right, rightType), rightType
);
}
if (PrimitiveToObjectEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToObjectEquality.create(opToken, genLazy(left, leftType), leftType, genLazy(right, rightType), rightType);
return PrimitiveToObjectEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedLeft, left, leftType), leftType,
genLazy(right, rightType), rightType
);
}
@@ -2934,23 +2966,29 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (opToken == KtTokens.EQEQEQ || opToken == KtTokens.EXCLEQEQEQ) {
// TODO: always casting to the type of the left operand in case of primitives looks wrong
Type operandType = isPrimitive(leftType) ? leftType : OBJECT_TYPE;
return StackValue.cmp(opToken, operandType, genLazy(left, leftType), genLazy(right, rightType));
return StackValue.cmp(
opToken,
operandType,
genLazyUnlessProvided(pregeneratedLeft, left, leftType),
genLazy(right, rightType)
);
}
return genEqualsForExpressionsPreferIEEE754Arithmetic(left, right, opToken, leftType, rightType, null);
return genEqualsForExpressionsPreferIEEE754Arithmetic(left, right, opToken, leftType, rightType, pregeneratedLeft);
}
private StackValue genCmpPrimitiveToSafeCall(
@NotNull KtExpression left,
@NotNull Type leftType,
@NotNull KtSafeQualifiedExpression right,
@NotNull IElementType opToken
@NotNull IElementType opToken,
@Nullable StackValue pregeneratedLeft
) {
Label rightIsNull = new Label();
return new PrimitiveToSafeCallEquality(
opToken,
leftType,
genLazy(left, leftType),
genLazyUnlessProvided(pregeneratedLeft, left, leftType),
generateSafeQualifiedExpression(right, rightIsNull),
expressionType(right.getReceiverExpression()),
rightIsNull
@@ -3011,7 +3049,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return genEqualsForExpressionsOnStack(
opToken,
pregeneratedLeft != null ? StackValue.coercion(pregeneratedLeft, leftType) : genLazy(left, leftType),
genLazyUnlessProvided(pregeneratedLeft, left, leftType),
genLazy(right, rightType)
);
}
@@ -3027,12 +3065,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
) {
Type leftType = left754Type.isNullable ? AsmUtil.boxType(left754Type.type) : left754Type.type;
if (pregeneratedLeft != null) {
StackValue.coercion(pregeneratedLeft, leftType).put(leftType, v);
}
else {
gen(left, leftType);
}
genUnlessProvided(pregeneratedLeft, left, leftType);
Type rightType = right754Type.isNullable ? AsmUtil.boxType(right754Type.type) : right754Type.type;
gen(right, rightType);
@@ -3141,12 +3174,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return isIntPrimitive(exprType) && exprValue != null && Integer.valueOf(0).equals(exprValue.getValue());
}
private StackValue genCmpWithZero(KtExpression exp, IElementType opToken) {
return StackValue.compareIntWithZero(gen(exp), (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNE : IFEQ);
private StackValue genCmpWithZero(KtExpression exp, IElementType opToken, @Nullable StackValue pregeneratedExpr) {
return StackValue.compareIntWithZero(
pregeneratedExpr != null ? pregeneratedExpr : gen(exp),
(KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNE : IFEQ
);
}
private StackValue genCmpWithNull(KtExpression exp, IElementType opToken) {
return StackValue.compareWithNull(gen(exp), (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNONNULL : IFNULL);
private StackValue genCmpWithNull(KtExpression exp, IElementType opToken, @Nullable StackValue pregeneratedExpr) {
return StackValue.compareWithNull(
pregeneratedExpr != null ? pregeneratedExpr : gen(exp),
(KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNONNULL : IFNULL
);
}
private StackValue generateElvis(@NotNull KtBinaryExpression expression) {
@@ -4010,22 +4049,7 @@ The "returned" value of try expression with no finally is either the last expres
private StackValue generateExpressionMatch(StackValue expressionToMatch, KtExpression subjectExpression, KtExpression patternExpression) {
if (expressionToMatch != null) {
Type subjectType = expressionToMatch.type;
markStartLineNumber(patternExpression);
KotlinType condJetType = bindingContext.getType(patternExpression);
Type condType;
if (isNumberPrimitiveOrBoolean(subjectType)) {
assert condJetType != null;
condType = asmType(condJetType);
if (!isNumberPrimitiveOrBoolean(condType)) {
subjectType = boxType(subjectType);
}
}
else {
condType = OBJECT_TYPE;
}
return genEqualsForExpressionsPreferIEEE754Arithmetic(subjectExpression, patternExpression, KtTokens.EQEQ, subjectType, condType, expressionToMatch);
return generateEquals(subjectExpression, patternExpression, KtTokens.EQEQ, expressionToMatch);
}
else {
return gen(patternExpression);
@@ -0,0 +1,62 @@
class CInt(val value: Int)
val nCInt3: CInt? = CInt(3)
class CLong(val value: Long)
val nCLong3: CLong? = CLong(3)
var subjectEvaluated: Int = 0
fun <T> subject(x: T): T {
subjectEvaluated++
return x
}
fun doTestInt(i: Int?) =
when (subject(i)) {
null -> "null"
0 -> "zero"
nCInt3?.value -> "three"
42 -> "magic"
else -> "other"
}
fun doTestLong(i: Long?) =
when (subject(i)) {
null -> "null"
0L -> "zero"
nCLong3?.value -> "three"
42L -> "magic"
else -> "other"
}
fun testInt(i: Int?): String {
subjectEvaluated = 0
val result = doTestInt(i)
if (subjectEvaluated != 1) throw AssertionError("Subject evaluated $subjectEvaluated")
return result
}
fun testLong(i: Long?): String {
subjectEvaluated = 0
val result = doTestLong(i)
if (subjectEvaluated != 1) throw AssertionError("Subject evaluated $subjectEvaluated")
return result
}
fun box(): String {
return when {
testInt(null) != "null" -> "Fail testInt null"
testInt(0) != "zero" -> "Fail testInt 0"
testInt(1) != "other" -> "Fail testInt 1"
testInt(3) != "three" -> "Fail testInt 3"
testInt(42) != "magic" -> "Fail testInt 42"
testLong(null) != "null" -> "Fail testLong null"
testLong(0L) != "zero" -> "Fail testLong 0"
testLong(1L) != "other" -> "Fail testLong 1"
testLong(3L) != "three" -> "Fail testLong 3"
testLong(42L) != "magic" -> "Fail testLong 42"
else -> "OK"
}
}
@@ -0,0 +1,19 @@
var subjectEvaluated = 0
fun String.foo() = length.also { ++subjectEvaluated }
fun test(s: String?) =
when (s?.foo()) {
0 -> "zero"
1 -> "one"
2 -> "two"
else -> "other"
}
fun box(): String {
val t = test("12")
if (t != "two") return "Fail: $t"
if (subjectEvaluated != 1) return "Fail: subjectEvaluated=$subjectEvaluated"
return "OK"
}
+14
View File
@@ -0,0 +1,14 @@
// WITH_RUNTIME
fun findUserId(username: String): Long? = null
fun main(args: Array<String>) {
val userId = findUserId("abcd")
when (userId) {
null -> println("User not found")
else -> println("User ID: $userId")
}
}
// 0 areEqual
@@ -0,0 +1,27 @@
// FILE: C.kt
class CInt(val value: Int)
val nCInt3: CInt? = CInt(3)
class CLong(val value: Long)
val nCLong3: CLong? = CLong(3)
// FILE: test.kt
fun testInt(i: Int?) =
when (i) {
0 -> "zero"
42 -> "magic"
else -> "other"
}
fun testLong(i: Long?) =
when (i) {
0L -> "zero"
42L -> "magic"
else -> "other"
}
// @TestKt.class:
// 0 valueOf
// 0 Integer.valueOf
// 0 Long.valueOf
// 0 areEqual
+10
View File
@@ -0,0 +1,10 @@
fun test(a: Any?, b: Any?, c: Any?) {
when (null) {
a -> throw IllegalArgumentException("a is null")
b -> throw IllegalArgumentException("b is null")
c -> throw IllegalArgumentException("c is null")
}
}
// 0 areEqual
// 3 IFNONNULL
+10
View File
@@ -0,0 +1,10 @@
fun test(a: Int, b: Int, c: Int) {
when (0) {
a -> throw IllegalArgumentException("a is 0")
b -> throw IllegalArgumentException("b is 0")
c -> throw IllegalArgumentException("c is 0")
}
}
// 0 IF_ICMPNE
// 3 IFNE
@@ -12559,6 +12559,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("whenNullableBoxed.kt")
public void testWhenNullableBoxed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19349,6 +19355,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("whenSafeCallSubjectEvaluatedOnce.kt")
public void testWhenSafeCallSubjectEvaluatedOnce() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/when/whenSafeCallSubjectEvaluatedOnce.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/when/enumOptimization")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -12559,6 +12559,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("whenNullableBoxed.kt")
public void testWhenNullableBoxed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19349,6 +19355,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("whenSafeCallSubjectEvaluatedOnce.kt")
public void testWhenSafeCallSubjectEvaluatedOnce() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/when/whenSafeCallSubjectEvaluatedOnce.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/when/enumOptimization")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2207,6 +2207,18 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("kt18818.kt")
public void testKt18818() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/kt18818.kt");
doTest(fileName);
}
@TestMetadata("noBoxingInDefaultWhenWithSpecialCases.kt")
public void testNoBoxingInDefaultWhenWithSpecialCases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/noBoxingInDefaultWhenWithSpecialCases.kt");
doTest(fileName);
}
@TestMetadata("qualifiedConstValsInsideWhen.kt")
public void testQualifiedConstValsInsideWhen() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/qualifiedConstValsInsideWhen.kt");
@@ -2224,6 +2236,18 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/simpleConstValsInsideWhen.kt");
doTest(fileName);
}
@TestMetadata("whenNull.kt")
public void testWhenNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/whenNull.kt");
doTest(fileName);
}
@TestMetadata("whenZero.kt")
public void testWhenZero() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/when/whenZero.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/whenEnumOptimization")
@@ -12559,6 +12559,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("whenNullableBoxed.kt")
public void testWhenNullableBoxed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19349,6 +19355,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("whenSafeCallSubjectEvaluatedOnce.kt")
public void testWhenSafeCallSubjectEvaluatedOnce() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/when/whenSafeCallSubjectEvaluatedOnce.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/when/enumOptimization")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -14017,6 +14017,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("whenNullableBoxed.kt")
public void testWhenNullableBoxed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -23513,6 +23519,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("whenSafeCallSubjectEvaluatedOnce.kt")
public void testWhenSafeCallSubjectEvaluatedOnce() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/when/whenSafeCallSubjectEvaluatedOnce.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/when/enumOptimization")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)