Fix floating point comparison generation for range literals
This commit is contained in:
+19
-7
@@ -19,11 +19,13 @@ package org.jetbrains.kotlin.codegen.range
|
|||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.InPrimitiveContinuousRangeExpressionGenerator
|
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||||
|
import org.jetbrains.kotlin.codegen.range.inExpression.InFloatingPointRangeLiteralExpressionGenerator
|
||||||
|
import org.jetbrains.kotlin.codegen.range.inExpression.InIntegralContinuousRangeExpressionGenerator
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): CallIntrinsicRangeValue(rangeCall) {
|
abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): CallIntrinsicRangeValue(rangeCall) {
|
||||||
protected val asmElementType = getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCall.resultingDescriptor)
|
protected val asmElementType = getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCall.resultingDescriptor)
|
||||||
@@ -41,12 +43,22 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<o
|
|||||||
resolvedCall: ResolvedCall<out CallableDescriptor>
|
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||||
): InExpressionGenerator {
|
): InExpressionGenerator {
|
||||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
||||||
return if (comparisonGenerator != null)
|
val comparedType = comparisonGenerator?.comparedType
|
||||||
InPrimitiveContinuousRangeExpressionGenerator(
|
|
||||||
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
|
return when {
|
||||||
)
|
comparisonGenerator == null -> CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||||
else
|
|
||||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
comparedType == Type.DOUBLE_TYPE || comparedType == Type.FLOAT_TYPE -> {
|
||||||
|
val rangeLiteral = getBoundedValue(codegen) as? SimpleBoundedValue ?:
|
||||||
|
throw AssertionError("Floating point intrinsic range value should be a range literal")
|
||||||
|
InFloatingPointRangeLiteralExpressionGenerator(operatorReference, rangeLiteral, comparisonGenerator, codegen.frameMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
else ->
|
||||||
|
InIntegralContinuousRangeExpressionGenerator(
|
||||||
|
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun getBoundedValue(codegen: ExpressionCodegen): BoundedValue
|
protected abstract fun getBoundedValue(codegen: ExpressionCodegen): BoundedValue
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
|||||||
class SimpleBoundedValue(
|
class SimpleBoundedValue(
|
||||||
codegen: ExpressionCodegen,
|
codegen: ExpressionCodegen,
|
||||||
rangeCall: ResolvedCall<out CallableDescriptor>,
|
rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||||
private val lowBound: StackValue,
|
val lowBound: StackValue,
|
||||||
isLowInclusive: Boolean,
|
isLowInclusive: Boolean,
|
||||||
private val highBound: StackValue,
|
val highBound: StackValue,
|
||||||
isHighInclusive: Boolean
|
isHighInclusive: Boolean
|
||||||
): AbstractBoundedValue(codegen, rangeCall, isLowInclusive, isHighInclusive) {
|
): AbstractBoundedValue(codegen, rangeCall, isLowInclusive, isHighInclusive) {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
+4
-4
@@ -22,22 +22,22 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
|||||||
|
|
||||||
sealed class FloatingPointComparisonGenerator(override val comparedType: Type): ComparisonGenerator {
|
sealed class FloatingPointComparisonGenerator(override val comparedType: Type): ComparisonGenerator {
|
||||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||||
v.cmpl(comparedType)
|
v.cmpg(comparedType)
|
||||||
v.ifge(label)
|
v.ifge(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||||
v.cmpg(comparedType)
|
v.cmpl(comparedType)
|
||||||
v.ifle(label)
|
v.ifle(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||||
v.cmpl(comparedType)
|
v.cmpg(comparedType)
|
||||||
v.ifgt(label)
|
v.ifgt(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||||
v.cmpg(comparedType)
|
v.cmpl(comparedType)
|
||||||
v.iflt(label)
|
v.iflt(label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.range.inExpression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.*
|
||||||
|
import org.jetbrains.kotlin.codegen.range.SimpleBoundedValue
|
||||||
|
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
|
class InFloatingPointRangeLiteralExpressionGenerator(
|
||||||
|
operatorReference: KtSimpleNameExpression,
|
||||||
|
private val rangeLiteral: SimpleBoundedValue,
|
||||||
|
private val comparisonGenerator: ComparisonGenerator,
|
||||||
|
private val frameMap: FrameMap
|
||||||
|
) : InExpressionGenerator {
|
||||||
|
init {
|
||||||
|
assert(rangeLiteral.isLowInclusive && rangeLiteral.isHighInclusive) { "Floating point range literal bounds should be inclusive" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
|
||||||
|
|
||||||
|
override fun generate(argument: StackValue): BranchedValue =
|
||||||
|
gen(argument).let { if (isNotIn) Invert(it) else it }
|
||||||
|
|
||||||
|
private fun gen(argument: StackValue): BranchedValue =
|
||||||
|
object : BranchedValue(argument, null, comparisonGenerator.comparedType, Opcodes.IFEQ) {
|
||||||
|
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||||
|
if (jumpIfFalse) {
|
||||||
|
genJumpIfFalse(v, jumpLabel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
genJumpIfTrue(v, jumpLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
|
||||||
|
// if (arg is in range) goto jumpLabel
|
||||||
|
// =>
|
||||||
|
// if (arg is NOT in range) goto exitLabel
|
||||||
|
// goto jumpLabel
|
||||||
|
// exitLabel:
|
||||||
|
|
||||||
|
frameMap.useTmpVar(operandType) { argVar ->
|
||||||
|
val exitLabel = Label()
|
||||||
|
genJumpIfFalse(v, exitLabel)
|
||||||
|
v.goTo(jumpLabel)
|
||||||
|
v.mark(exitLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
|
||||||
|
// if (arg is NOT in range) goto jumpLabel
|
||||||
|
|
||||||
|
frameMap.useTmpVar(operandType) { argVar ->
|
||||||
|
// Evaluate low and high bounds once (unless they have no side effects)
|
||||||
|
val (lowValue, lowTmpType) = introduceTemporaryIfRequired(v, rangeLiteral.lowBound, operandType)
|
||||||
|
val (highValue, highTmpType) = introduceTemporaryIfRequired(v, rangeLiteral.highBound, operandType)
|
||||||
|
|
||||||
|
val argValue = StackValue.local(argVar, operandType)
|
||||||
|
argValue.store(arg1, v)
|
||||||
|
|
||||||
|
// if (low bound is NOT satisfied) goto jumpLabel
|
||||||
|
// arg < low
|
||||||
|
argValue.put(operandType, v)
|
||||||
|
lowValue.put(operandType, v)
|
||||||
|
comparisonGenerator.jumpIfLess(v, jumpLabel)
|
||||||
|
|
||||||
|
// if (high bound is NOT satisfied) goto jumpLabel
|
||||||
|
// arg > high
|
||||||
|
argValue.put(operandType, v)
|
||||||
|
highValue.put(operandType, v)
|
||||||
|
comparisonGenerator.jumpIfGreater(v, jumpLabel)
|
||||||
|
|
||||||
|
highTmpType?.let { frameMap.leaveTemp(it) }
|
||||||
|
lowTmpType?.let { frameMap.leaveTemp(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun introduceTemporaryIfRequired(v: InstructionAdapter, value: StackValue, type: Type): Pair<StackValue, Type?> {
|
||||||
|
val resultValue: StackValue
|
||||||
|
val resultType: Type?
|
||||||
|
|
||||||
|
if (value.canHaveSideEffects()) {
|
||||||
|
val index = frameMap.enterTemp(type)
|
||||||
|
resultValue = StackValue.local(index, type)
|
||||||
|
resultType = type
|
||||||
|
resultValue.store(value, v)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultValue = value
|
||||||
|
resultType = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(resultValue, resultType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.Label
|
|||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
class InPrimitiveContinuousRangeExpressionGenerator(
|
class InIntegralContinuousRangeExpressionGenerator(
|
||||||
operatorReference: KtSimpleNameExpression,
|
operatorReference: KtSimpleNameExpression,
|
||||||
private val boundedValue: BoundedValue,
|
private val boundedValue: BoundedValue,
|
||||||
private val comparisonGenerator: ComparisonGenerator,
|
private val comparisonGenerator: ComparisonGenerator,
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val NAN = Double.NaN
|
||||||
|
val M_NAN = -Double.NaN
|
||||||
|
|
||||||
|
val range1 = 0.0 .. NAN
|
||||||
|
val range2 = M_NAN .. 0.0
|
||||||
|
val range3 = M_NAN .. NAN
|
||||||
|
|
||||||
|
assertEquals(1.0 in range1, 1.0 in 0.0 .. NAN)
|
||||||
|
assertEquals(-1.0 in range1, -1.0 in 0.0 .. NAN)
|
||||||
|
assertEquals(0.0 in range1, 0.0 in 0.0 .. NAN)
|
||||||
|
assertEquals(NAN in range1, NAN in 0.0 .. NAN)
|
||||||
|
assertEquals(M_NAN in range1, M_NAN in 0.0 .. NAN)
|
||||||
|
|
||||||
|
assertEquals(1.0 !in range1, 1.0 !in 0.0 .. NAN)
|
||||||
|
assertEquals(-1.0 !in range1, -1.0 !in 0.0 .. NAN)
|
||||||
|
assertEquals(0.0 !in range1, 0.0 !in 0.0 .. NAN)
|
||||||
|
assertEquals(NAN !in range1, NAN !in 0.0 .. NAN)
|
||||||
|
assertEquals(M_NAN !in range1, M_NAN !in 0.0 .. NAN)
|
||||||
|
|
||||||
|
assertEquals(1.0 in range2, 1.0 in M_NAN .. 0.0)
|
||||||
|
assertEquals(-1.0 in range2, -1.0 in M_NAN .. 0.0)
|
||||||
|
assertEquals(0.0 in range2, 0.0 in M_NAN .. 0.0)
|
||||||
|
assertEquals(NAN in range2, NAN in M_NAN .. 0.0)
|
||||||
|
assertEquals(M_NAN in range2, M_NAN in M_NAN .. 0.0)
|
||||||
|
|
||||||
|
assertEquals(1.0 !in range2, 1.0 !in M_NAN .. 0.0)
|
||||||
|
assertEquals(-1.0 !in range2, -1.0 !in M_NAN .. 0.0)
|
||||||
|
assertEquals(0.0 !in range2, 0.0 !in M_NAN .. 0.0)
|
||||||
|
assertEquals(NAN !in range2, NAN !in M_NAN .. 0.0)
|
||||||
|
assertEquals(M_NAN !in range2, M_NAN !in M_NAN .. 0.0)
|
||||||
|
|
||||||
|
assertEquals(1.0 in range3, 1.0 in M_NAN .. NAN)
|
||||||
|
assertEquals(-1.0 in range3, -1.0 in M_NAN .. NAN)
|
||||||
|
assertEquals(0.0 in range3, 0.0 in M_NAN .. NAN)
|
||||||
|
assertEquals(NAN in range3, NAN in M_NAN .. NAN)
|
||||||
|
assertEquals(M_NAN in range3, M_NAN in M_NAN .. NAN)
|
||||||
|
|
||||||
|
assertEquals(1.0 !in range3, 1.0 !in M_NAN .. NAN)
|
||||||
|
assertEquals(-1.0 !in range3, -1.0 !in M_NAN .. NAN)
|
||||||
|
assertEquals(0.0 !in range3, 0.0 !in M_NAN .. NAN)
|
||||||
|
assertEquals(NAN !in range3, NAN !in M_NAN .. NAN)
|
||||||
|
assertEquals(M_NAN !in range3, M_NAN !in M_NAN .. NAN)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+6
@@ -14575,6 +14575,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inFloatingPointRangeWithNaNBound.kt")
|
||||||
|
public void testInFloatingPointRangeWithNaNBound() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inFloatingPointRangeWithNaNBound.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inIntRange.kt")
|
@TestMetadata("inIntRange.kt")
|
||||||
public void testInIntRange() throws Exception {
|
public void testInIntRange() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||||
|
|||||||
@@ -14575,6 +14575,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inFloatingPointRangeWithNaNBound.kt")
|
||||||
|
public void testInFloatingPointRangeWithNaNBound() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inFloatingPointRangeWithNaNBound.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inIntRange.kt")
|
@TestMetadata("inIntRange.kt")
|
||||||
public void testInIntRange() throws Exception {
|
public void testInIntRange() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||||
|
|||||||
@@ -14575,6 +14575,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inFloatingPointRangeWithNaNBound.kt")
|
||||||
|
public void testInFloatingPointRangeWithNaNBound() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inFloatingPointRangeWithNaNBound.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inIntRange.kt")
|
@TestMetadata("inIntRange.kt")
|
||||||
public void testInIntRange() throws Exception {
|
public void testInIntRange() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||||
|
|||||||
@@ -15877,6 +15877,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inFloatingPointRangeWithNaNBound.kt")
|
||||||
|
public void testInFloatingPointRangeWithNaNBound() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inFloatingPointRangeWithNaNBound.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inIntRange.kt")
|
@TestMetadata("inIntRange.kt")
|
||||||
public void testInIntRange() throws Exception {
|
public void testInIntRange() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user