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.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||
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.InFloatingPointRangeLiteralExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InIntegralContinuousRangeExpressionGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): CallIntrinsicRangeValue(rangeCall) {
|
||||
protected val asmElementType = getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCall.resultingDescriptor)
|
||||
@@ -41,12 +43,22 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<o
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||
): InExpressionGenerator {
|
||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
||||
return if (comparisonGenerator != null)
|
||||
InPrimitiveContinuousRangeExpressionGenerator(
|
||||
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
|
||||
)
|
||||
else
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
val comparedType = comparisonGenerator?.comparedType
|
||||
|
||||
return when {
|
||||
comparisonGenerator == null -> 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
|
||||
|
||||
@@ -25,9 +25,9 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
class SimpleBoundedValue(
|
||||
codegen: ExpressionCodegen,
|
||||
rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||
private val lowBound: StackValue,
|
||||
val lowBound: StackValue,
|
||||
isLowInclusive: Boolean,
|
||||
private val highBound: StackValue,
|
||||
val highBound: StackValue,
|
||||
isHighInclusive: Boolean
|
||||
): AbstractBoundedValue(codegen, rangeCall, isLowInclusive, isHighInclusive) {
|
||||
constructor(
|
||||
|
||||
+4
-4
@@ -22,22 +22,22 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
sealed class FloatingPointComparisonGenerator(override val comparedType: Type): ComparisonGenerator {
|
||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.cmpl(comparedType)
|
||||
v.cmpg(comparedType)
|
||||
v.ifge(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.cmpg(comparedType)
|
||||
v.cmpl(comparedType)
|
||||
v.ifle(label)
|
||||
}
|
||||
|
||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||
v.cmpl(comparedType)
|
||||
v.cmpg(comparedType)
|
||||
v.ifgt(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||
v.cmpg(comparedType)
|
||||
v.cmpl(comparedType)
|
||||
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.commons.InstructionAdapter
|
||||
|
||||
class InPrimitiveContinuousRangeExpressionGenerator(
|
||||
class InIntegralContinuousRangeExpressionGenerator(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
private val boundedValue: BoundedValue,
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testInIntRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||
|
||||
@@ -14575,6 +14575,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
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")
|
||||
public void testInIntRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||
|
||||
@@ -14575,6 +14575,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
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")
|
||||
public void testInIntRange() throws Exception {
|
||||
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.");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testInIntRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inIntRange.kt");
|
||||
|
||||
Reference in New Issue
Block a user