Don't generate DUPX instructions for in-range-literal expressions

Store argument into a local variable instead.
This commit is contained in:
Dmitry Petrov
2017-10-24 11:03:53 +03:00
parent 9b49e9139c
commit 354d54aef6
7 changed files with 170 additions and 146 deletions
@@ -41,5 +41,5 @@ class ComparableRangeLiteralRangeValue(
codegen: ExpressionCodegen,
operatorReference: KtSimpleNameExpression,
resolvedCall: ResolvedCall<out CallableDescriptor>
) = InContinuousRangeOfComparableExpressionGenerator(operatorReference, boundedValue)
) = InContinuousRangeOfComparableExpressionGenerator(operatorReference, boundedValue, codegen.frameMap)
}
@@ -42,7 +42,9 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<o
): InExpressionGenerator {
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
return if (comparisonGenerator != null)
InPrimitiveContinuousRangeExpressionGenerator(operatorReference, getBoundedValue(codegen), comparisonGenerator)
InPrimitiveContinuousRangeExpressionGenerator(
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
)
else
CallBasedInExpressionGenerator(codegen, operatorReference)
}
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.codegen.range.inExpression
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.BranchedValue
import org.jetbrains.kotlin.codegen.Invert
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.range.BoundedValue
import org.jetbrains.kotlin.codegen.range.comparison.ObjectComparisonGenerator
import org.jetbrains.kotlin.lexer.KtTokens
@@ -30,7 +27,8 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class InContinuousRangeOfComparableExpressionGenerator(
operatorReference: KtSimpleNameExpression,
private val boundedValue: BoundedValue
private val boundedValue: BoundedValue,
private val frameMap: FrameMap
) : InExpressionGenerator {
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
private val comparisonGenerator = ObjectComparisonGenerator
@@ -52,84 +50,95 @@ class InContinuousRangeOfComparableExpressionGenerator(
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
// if (arg is in range) goto jumpLabel
val exitLabel1 = Label()
val exitLabel2 = Label()
frameMap.useTmpVar(operandType) { arg1Var ->
val exitLabel1 = Label()
val exitLabel2 = Label()
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
AsmUtil.dupx(v, operandType)
boundedValue.putHighLow(v, operandType)
// On stack: high arg low arg
// if (low bound is NOT satisfied) goto exitLabel1
if (boundedValue.isLowInclusive) {
// arg < low
v.swap()
comparisonGenerator.jumpIfLess(v, exitLabel1)
}
else {
// arg <= low
v.swap()
comparisonGenerator.jumpIfLessOrEqual(v, exitLabel1)
arg1.put(operandType, v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
// On stack: high low arg
// if (low bound is NOT satisfied) goto exitLabel1
if (boundedValue.isLowInclusive) {
// arg < low
v.swap()
comparisonGenerator.jumpIfLess(v, exitLabel1)
}
else {
// arg <= low
v.swap()
comparisonGenerator.jumpIfLessOrEqual(v, exitLabel1)
}
v.load(arg1Var, operandType)
// On stack: high arg
// if (high bound is satisfied) goto jumpLabel
if (boundedValue.isHighInclusive) {
// arg <= high
v.swap()
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
}
else {
// arg < high
v.swap()
comparisonGenerator.jumpIfLess(v, jumpLabel)
}
v.goTo(exitLabel2)
v.mark(exitLabel1)
AsmUtil.pop(v, operandType)
v.mark(exitLabel2)
}
// On stack: high arg
// if (high bound is satisfied) goto jumpLabel
if (boundedValue.isHighInclusive) {
// arg <= high
v.swap()
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
}
else {
// arg < high
v.swap()
comparisonGenerator.jumpIfLess(v, jumpLabel)
}
v.goTo(exitLabel2)
v.mark(exitLabel1)
AsmUtil.pop2(v, operandType)
v.mark(exitLabel2)
}
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
// if (arg is NOT in range) goto jumpLabel
val cmpHighLabel = Label()
frameMap.useTmpVar(operandType) { arg1Var ->
val cmpHighLabel = Label()
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
AsmUtil.dupx(v, operandType)
boundedValue.putHighLow(v, operandType)
// On stack: high arg low arg
// if ([low bound is satisfied]) goto cmpHighLabel
if (boundedValue.isLowInclusive) {
// arg >= low
v.swap()
comparisonGenerator.jumpIfGreaterOrEqual(v, cmpHighLabel)
}
else {
// arg > low
v.swap()
comparisonGenerator.jumpIfGreater(v, cmpHighLabel)
}
arg1.put(operandType, v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
// Low bound is NOT satisfied, clear stack and goto jumpLabel
AsmUtil.pop2(v, operandType)
v.goTo(jumpLabel)
// On stack: high low arg
// if ([low bound is satisfied]) goto cmpHighLabel
if (boundedValue.isLowInclusive) {
// arg >= low
v.swap()
comparisonGenerator.jumpIfGreaterOrEqual(v, cmpHighLabel)
}
else {
// arg > low
v.swap()
comparisonGenerator.jumpIfGreater(v, cmpHighLabel)
}
v.mark(cmpHighLabel)
// On stack: high arg
// if ([high bound is NOT satisfied]) goto jumpLabel
if (boundedValue.isHighInclusive) {
// arg > high
v.swap()
comparisonGenerator.jumpIfGreater(v, jumpLabel)
}
else {
// arg >= high
v.swap()
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
// Low bound is NOT satisfied, clear stack and goto jumpLabel
AsmUtil.pop(v, operandType)
v.goTo(jumpLabel)
v.mark(cmpHighLabel)
v.load(arg1Var, operandType)
// On stack: high arg
// if ([high bound is NOT satisfied]) goto jumpLabel
if (boundedValue.isHighInclusive) {
// arg > high
v.swap()
comparisonGenerator.jumpIfGreater(v, jumpLabel)
}
else {
// arg >= high
v.swap()
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
}
}
}
}
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.codegen.range.inExpression
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.BranchedValue
import org.jetbrains.kotlin.codegen.Invert
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.range.BoundedValue
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
import org.jetbrains.kotlin.lexer.KtTokens
@@ -31,7 +28,8 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class InPrimitiveContinuousRangeExpressionGenerator(
operatorReference: KtSimpleNameExpression,
private val boundedValue: BoundedValue,
private val comparisonGenerator: ComparisonGenerator
private val comparisonGenerator: ComparisonGenerator,
private val frameMap: FrameMap
) : InExpressionGenerator {
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
@@ -51,78 +49,88 @@ class InPrimitiveContinuousRangeExpressionGenerator(
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
// if (arg is in range) goto jumpLabel
frameMap.useTmpVar(operandType) { arg1Var ->
val exitLabel1 = Label()
val exitLabel2 = Label()
val exitLabel1 = Label()
val exitLabel2 = Label()
boundedValue.putHighLow(v, operandType)
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
AsmUtil.dupx(v, operandType)
arg1.put(operandType, v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
// On stack: high arg low arg
// if (low bound is NOT satisfied) goto exitLabel1
if (boundedValue.isLowInclusive) {
// low > arg
comparisonGenerator.jumpIfGreater(v, exitLabel1)
// On stack: high low arg
// if (low bound is NOT satisfied) goto exitLabel1
if (boundedValue.isLowInclusive) {
// low > arg
comparisonGenerator.jumpIfGreater(v, exitLabel1)
}
else {
// low >= arg
comparisonGenerator.jumpIfGreaterOrEqual(v, exitLabel1)
}
v.load(arg1Var, operandType)
// On stack: high arg
// if (high bound is satisfied) goto jumpLabel
if (boundedValue.isHighInclusive) {
// high >= arg
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
}
else {
// high > arg
comparisonGenerator.jumpIfGreater(v, jumpLabel)
}
v.goTo(exitLabel2)
v.mark(exitLabel1)
AsmUtil.pop(v, operandType)
v.mark(exitLabel2)
}
else {
// low >= arg
comparisonGenerator.jumpIfGreaterOrEqual(v, exitLabel1)
}
// On stack: high arg
// if (high bound is satisfied) goto jumpLabel
if (boundedValue.isHighInclusive) {
// high >= arg
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
}
else {
// high > arg
comparisonGenerator.jumpIfGreater(v, jumpLabel)
}
v.goTo(exitLabel2)
v.mark(exitLabel1)
AsmUtil.pop2(v, operandType)
v.mark(exitLabel2)
}
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
// if (arg is NOT in range) goto jumpLabel
val cmpHighLabel = Label()
frameMap.useTmpVar(operandType) { arg1Var ->
val cmpHighLabel = Label()
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
AsmUtil.dupx(v, operandType)
boundedValue.putHighLow(v, operandType)
// On stack: high arg low arg
// if ([low bound is satisfied]) goto cmpHighLabel
if (boundedValue.isLowInclusive) {
// low <= arg
comparisonGenerator.jumpIfLessOrEqual(v, cmpHighLabel)
}
else {
// low < arg
comparisonGenerator.jumpIfLess(v, cmpHighLabel)
arg1.put(operandType, v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
// On stack: high low arg
// if ([low bound is satisfied]) goto cmpHighLabel
if (boundedValue.isLowInclusive) {
// low <= arg
comparisonGenerator.jumpIfLessOrEqual(v, cmpHighLabel)
}
else {
// low < arg
comparisonGenerator.jumpIfLess(v, cmpHighLabel)
}
// Low bound is NOT satisfied, clear stack and goto jumpLabel
AsmUtil.pop(v, operandType)
v.goTo(jumpLabel)
v.mark(cmpHighLabel)
v.load(arg1Var, operandType)
// On stack: high arg
// if ([high bound is NOT satisfied]) goto jumpLabel
if (boundedValue.isHighInclusive) {
// high < arg
comparisonGenerator.jumpIfLess(v, jumpLabel)
}
else {
// high <= arg
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
}
}
// Low bound is NOT satisfied, clear stack and goto jumpLabel
AsmUtil.pop2(v, operandType)
v.goTo(jumpLabel)
v.mark(cmpHighLabel)
// On stack: high arg
// if ([high bound is NOT satisfied]) goto jumpLabel
if (boundedValue.isHighInclusive) {
// high < arg
comparisonGenerator.jumpIfLess(v, jumpLabel)
}
else {
// high <= arg
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
}
}
}
}
@@ -0,0 +1,12 @@
// WITH_RUNTIME
fun test(a: Int) = a in 1 .. 10
fun test(a: Long) = a in 1L .. 10L
fun test(a: Float) = a in 1.0f .. 10.0f
fun test(a: Double) = a in 1.0 .. 10.0
fun test(a: String) = a in "abc" .. "def"
// 0 DUP_X1
// 0 DUP2_X1
// 0 DUP_X2
// 0 DUP2_X2
@@ -1,7 +0,0 @@
// WITH_RUNTIME
fun test(a: Long) = a in 1L .. 10L
// One DUP2_X2 generated for 'in' operator,
// no DUP2_X2 generated for range on stack.
// 1 DUP2_X2
@@ -2006,9 +2006,9 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("noSwap2ForConstLongRangeTo.kt")
public void testNoSwap2ForConstLongRangeTo() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/ranges/noSwap2ForConstLongRangeTo.kt");
@TestMetadata("noDupXForLiteralRangeContains.kt")
public void testNoDupXForLiteralRangeContains() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/ranges/noDupXForLiteralRangeContains.kt");
doTest(fileName);
}
}