summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator_arm.cc18
-rw-r--r--compiler/optimizing/code_generator_mips64.cc28
-rw-r--r--compiler/optimizing/code_generator_x86.cc50
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc28
-rw-r--r--compiler/optimizing/instruction_simplifier.cc74
-rw-r--r--test/458-checker-instruction-simplification/src/Main.java318
-rw-r--r--test/551-checker-shifter-operand/src/Main.java16
7 files changed, 505 insertions, 27 deletions
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 005b6c189e..87f52c6f21 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2079,6 +2079,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2097,6 +2099,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2181,6 +2185,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2280,6 +2286,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
+ __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2297,6 +2307,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2398,6 +2412,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
+ __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 71d65e84a6..119084e026 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3943,18 +3943,26 @@ void InstructionCodeGeneratorMIPS64::VisitTypeConversion(HTypeConversion* conver
__ Andi(dst, src, 0xFFFF);
break;
case Primitive::kPrimByte:
- // long is never converted into types narrower than int directly,
- // so SEB and SEH can be used without ever causing unpredictable results
- // on 64-bit inputs
- DCHECK(input_type != Primitive::kPrimLong);
- __ Seb(dst, src);
+ if (input_type == Primitive::kPrimLong) {
+ // Type conversion from long to types narrower than int is a result of code
+ // transformations. To avoid unpredictable results for SEB and SEH, we first
+ // need to sign-extend the low 32-bit value into bits 32 through 63.
+ __ Sll(dst, src, 0);
+ __ Seb(dst, dst);
+ } else {
+ __ Seb(dst, src);
+ }
break;
case Primitive::kPrimShort:
- // long is never converted into types narrower than int directly,
- // so SEB and SEH can be used without ever causing unpredictable results
- // on 64-bit inputs
- DCHECK(input_type != Primitive::kPrimLong);
- __ Seh(dst, src);
+ if (input_type == Primitive::kPrimLong) {
+ // Type conversion from long to types narrower than int is a result of code
+ // transformations. To avoid unpredictable results for SEB and SEH, we first
+ // need to sign-extend the low 32-bit value into bits 32 through 63.
+ __ Sll(dst, src, 0);
+ __ Seh(dst, dst);
+ } else {
+ __ Seh(dst, src);
+ }
break;
case Primitive::kPrimInt:
case Primitive::kPrimLong:
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 3713690335..07edd97c1f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2145,6 +2145,18 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong: {
+ // Type conversion from long to byte is a result of code transformations.
+ HInstruction* input = conversion->InputAt(0);
+ Location input_location = input->IsConstant()
+ ? Location::ConstantLocation(input->AsConstant())
+ : Location::RegisterPairLocation(EAX, EDX);
+ locations->SetInAt(0, input_location);
+ // Make the output overlap to please the register allocator. This greatly simplifies
+ // the validation of the linear scan implementation
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2165,6 +2177,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2242,6 +2256,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2336,6 +2352,16 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movsxb(out.AsRegister<Register>(), in.AsRegisterPairLow<ByteRegister>());
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<int8_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2359,6 +2385,18 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movsxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
+ } else if (in.IsDoubleStackSlot()) {
+ __ movsxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<int16_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2495,6 +2533,18 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movzxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
+ } else if (in.IsDoubleStackSlot()) {
+ __ movzxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<uint16_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 35603aa030..a5c386d327 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2363,6 +2363,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2381,6 +2383,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2458,6 +2462,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2552,6 +2558,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2560,13 +2568,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
// Processing a Dex `int-to-byte' instruction.
if (in.IsRegister()) {
__ movsxb(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movsxb(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<int8_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<int8_t>(Int64FromConstant(in.GetConstant()))));
}
break;
@@ -2578,6 +2585,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2586,13 +2595,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
// Processing a Dex `int-to-short' instruction.
if (in.IsRegister()) {
__ movsxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movsxw(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<int16_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<int16_t>(Int64FromConstant(in.GetConstant()))));
}
break;
@@ -2735,6 +2743,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2743,14 +2753,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
// Processing a Dex `int-to-char' instruction.
if (in.IsRegister()) {
__ movzxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movzxw(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<uint16_t>(
- in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<uint16_t>(Int64FromConstant(in.GetConstant()))));
}
break;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 0029cc3650..98f8009846 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -757,11 +757,79 @@ void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) {
}
}
+static bool IsTypeConversionImplicit(Primitive::Type input_type, Primitive::Type result_type) {
+ // Besides conversion to the same type, widening integral conversions are implicit,
+ // excluding conversions to long and the byte->char conversion where we need to
+ // clear the high 16 bits of the 32-bit sign-extended representation of byte.
+ return result_type == input_type ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimByte) ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimShort) ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimChar) ||
+ (result_type == Primitive::kPrimShort && input_type == Primitive::kPrimByte);
+}
+
+static bool IsTypeConversionLossless(Primitive::Type input_type, Primitive::Type result_type) {
+ // The conversion to a larger type is loss-less with the exception of two cases,
+ // - conversion to char, the only unsigned type, where we may lose some bits, and
+ // - conversion from float to long, the only FP to integral conversion with smaller FP type.
+ // For integral to FP conversions this holds because the FP mantissa is large enough.
+ DCHECK_NE(input_type, result_type);
+ return Primitive::ComponentSize(result_type) > Primitive::ComponentSize(input_type) &&
+ result_type != Primitive::kPrimChar &&
+ !(result_type == Primitive::kPrimLong && input_type == Primitive::kPrimFloat);
+}
+
void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
- if (instruction->GetResultType() == instruction->GetInputType()) {
- // Remove the instruction if it's converting to the same type.
- instruction->ReplaceWith(instruction->GetInput());
+ HInstruction* input = instruction->GetInput();
+ Primitive::Type input_type = input->GetType();
+ Primitive::Type result_type = instruction->GetResultType();
+ if (IsTypeConversionImplicit(input_type, result_type)) {
+ // Remove the implicit conversion; this includes conversion to the same type.
+ instruction->ReplaceWith(input);
instruction->GetBlock()->RemoveInstruction(instruction);
+ RecordSimplification();
+ return;
+ }
+
+ if (input->IsTypeConversion()) {
+ HTypeConversion* input_conversion = input->AsTypeConversion();
+ HInstruction* original_input = input_conversion->GetInput();
+ Primitive::Type original_type = original_input->GetType();
+
+ // When the first conversion is lossless, a direct conversion from the original type
+ // to the final type yields the same result, even for a lossy second conversion, for
+ // example float->double->int or int->double->float.
+ bool is_first_conversion_lossless = IsTypeConversionLossless(original_type, input_type);
+
+ // For integral conversions, see if the first conversion loses only bits that the second
+ // doesn't need, i.e. the final type is no wider than the intermediate. If so, direct
+ // conversion yields the same result, for example long->int->short or int->char->short.
+ bool integral_conversions_with_non_widening_second =
+ Primitive::IsIntegralType(input_type) &&
+ Primitive::IsIntegralType(original_type) &&
+ Primitive::IsIntegralType(result_type) &&
+ Primitive::ComponentSize(result_type) <= Primitive::ComponentSize(input_type);
+
+ if (is_first_conversion_lossless || integral_conversions_with_non_widening_second) {
+ // If the merged conversion is implicit, do the simplification unconditionally.
+ if (IsTypeConversionImplicit(original_type, result_type)) {
+ instruction->ReplaceWith(original_input);
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ if (!input_conversion->HasUses()) {
+ // Don't wait for DCE.
+ input_conversion->GetBlock()->RemoveInstruction(input_conversion);
+ }
+ RecordSimplification();
+ return;
+ }
+ // Otherwise simplify only if the first conversion has no other use.
+ if (input_conversion->HasOnlyOneNonEnvironmentUse()) {
+ input_conversion->ReplaceWith(original_input);
+ input_conversion->GetBlock()->RemoveInstruction(input_conversion);
+ RecordSimplification();
+ return;
+ }
+ }
}
}
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 2f80470cb3..07478b2ac1 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -46,6 +46,12 @@ public class Main {
}
}
+ public static void assertStringEquals(String expected, String result) {
+ if (expected == null ? result != null : !expected.equals(result)) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
/**
* Tiny programs exercising optimizations of arithmetic identities.
*/
@@ -1433,6 +1439,278 @@ public class Main {
return ((d > 42.0) != false) ? 13 : 54;
}
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static int intToDoubleToInt(int value) {
+ // Lossless conversion followed by a conversion back.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: {{d\d+}} TypeConversion [<<Arg>>]
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static String intToDoubleToIntPrint(int value) {
+ // Lossless conversion followed by a conversion back
+ // with another use of the intermediate result.
+ double d = (double) value;
+ int i = (int) d;
+ return "d=" + d + ", i=" + i;
+ }
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static int byteToDoubleToInt(byte value) {
+ // Lossless conversion followed by another conversion, use implicit conversion.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static int floatToDoubleToInt(float value) {
+ // Lossless conversion followed by another conversion.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ public static String floatToDoubleToIntPrint(float value) {
+ // Lossless conversion followed by another conversion with
+ // an extra use of the intermediate result.
+ double d = (double) value;
+ int i = (int) d;
+ return "d=" + d + ", i=" + i;
+ }
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static short byteToDoubleToShort(byte value) {
+ // Originally, this is byte->double->int->short. The first conversion is lossless,
+ // so we merge this with the second one to byte->int which we omit as it's an implicit
+ // conversion. Then we eliminate the resulting byte->short as an implicit conversion.
+ return (short) (double) value;
+ }
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static short charToDoubleToShort(char value) {
+ // Originally, this is char->double->int->short. The first conversion is lossless,
+ // so we merge this with the second one to char->int which we omit as it's an implicit
+ // conversion. Then we are left with the resulting char->short conversion.
+ return (short) (double) value;
+ }
+
+ /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ public static short floatToIntToShort(float value) {
+ // Lossy FP to integral conversion followed by another conversion: no simplification.
+ return (short) value;
+ }
+
+ /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ public static int intToFloatToInt(int value) {
+ // Lossy integral to FP conversion followed another conversion: no simplification.
+ return (int) (float) value;
+ }
+
+ /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ public static double longToIntToDouble(long value) {
+ // Lossy long-to-int conversion followed an integral to FP conversion: no simplification.
+ return (double) (int) value;
+ }
+
+ /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Long>>]
+
+ /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Long>>]
+
+ public static long longToIntToLong(long value) {
+ // Lossy long-to-int conversion followed an int-to-long conversion: no simplification.
+ return (long) (int) value;
+ }
+
+ /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Char>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ public static short shortToCharToShort(short value) {
+ // Integral conversion followed by non-widening integral conversion to original type.
+ return (short) (char) value;
+ }
+
+ /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Long>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ public static int shortToLongToInt(short value) {
+ // Integral conversion followed by non-widening integral conversion, use implicit conversion.
+ return (int) (long) value;
+ }
+
+ /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Char>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ public static byte shortToCharToByte(short value) {
+ // Integral conversion followed by non-widening integral conversion losing bits
+ // from the original type. Simplify to use only one conversion.
+ return (byte) (char) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
+
+ /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
+
+ public static String shortToCharToBytePrint(short value) {
+ // Integral conversion followed by non-widening integral conversion losing bits
+ // from the original type with an extra use of the intermediate result.
+ char c = (char) value;
+ byte b = (byte) c;
+ return "c=" + ((int) c) + ", b=" + ((int) b); // implicit conversions.
+ }
+
public static void main(String[] args) {
int arg = 123456;
@@ -1518,6 +1796,46 @@ public class Main {
assertIntEquals(floatConditionNotEqualOne(43.0f), 13);
assertIntEquals(doubleConditionEqualZero(6.0), 54);
assertIntEquals(doubleConditionEqualZero(43.0), 13);
+
+ assertIntEquals(intToDoubleToInt(1234567), 1234567);
+ assertIntEquals(intToDoubleToInt(Integer.MIN_VALUE), Integer.MIN_VALUE);
+ assertIntEquals(intToDoubleToInt(Integer.MAX_VALUE), Integer.MAX_VALUE);
+ assertStringEquals(intToDoubleToIntPrint(7654321), "d=7654321.0, i=7654321");
+ assertIntEquals(byteToDoubleToInt((byte) 12), 12);
+ assertIntEquals(byteToDoubleToInt(Byte.MIN_VALUE), Byte.MIN_VALUE);
+ assertIntEquals(byteToDoubleToInt(Byte.MAX_VALUE), Byte.MAX_VALUE);
+ assertIntEquals(floatToDoubleToInt(11.3f), 11);
+ assertStringEquals(floatToDoubleToIntPrint(12.25f), "d=12.25, i=12");
+ assertIntEquals(byteToDoubleToShort((byte) 123), 123);
+ assertIntEquals(byteToDoubleToShort(Byte.MIN_VALUE), Byte.MIN_VALUE);
+ assertIntEquals(byteToDoubleToShort(Byte.MAX_VALUE), Byte.MAX_VALUE);
+ assertIntEquals(charToDoubleToShort((char) 1234), 1234);
+ assertIntEquals(charToDoubleToShort(Character.MIN_VALUE), Character.MIN_VALUE);
+ assertIntEquals(charToDoubleToShort(Character.MAX_VALUE), /* sign-extended */ -1);
+ assertIntEquals(floatToIntToShort(12345.75f), 12345);
+ assertIntEquals(floatToIntToShort((float)(Short.MIN_VALUE - 1)), Short.MAX_VALUE);
+ assertIntEquals(floatToIntToShort((float)(Short.MAX_VALUE + 1)), Short.MIN_VALUE);
+ assertIntEquals(intToFloatToInt(-54321), -54321);
+ assertDoubleEquals(longToIntToDouble(0x1234567812345678L), (double) 0x12345678);
+ assertDoubleEquals(longToIntToDouble(Long.MIN_VALUE), 0.0);
+ assertDoubleEquals(longToIntToDouble(Long.MAX_VALUE), -1.0);
+ assertLongEquals(longToIntToLong(0x1234567812345678L), 0x0000000012345678L);
+ assertLongEquals(longToIntToLong(0x1234567887654321L), 0xffffffff87654321L);
+ assertLongEquals(longToIntToLong(Long.MIN_VALUE), 0L);
+ assertLongEquals(longToIntToLong(Long.MAX_VALUE), -1L);
+ assertIntEquals(shortToCharToShort((short) -5678), (short) -5678);
+ assertIntEquals(shortToCharToShort(Short.MIN_VALUE), Short.MIN_VALUE);
+ assertIntEquals(shortToCharToShort(Short.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(shortToLongToInt((short) 5678), 5678);
+ assertIntEquals(shortToLongToInt(Short.MIN_VALUE), Short.MIN_VALUE);
+ assertIntEquals(shortToLongToInt(Short.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(shortToCharToByte((short) 0x1234), 0x34);
+ assertIntEquals(shortToCharToByte((short) 0x12f0), -0x10);
+ assertIntEquals(shortToCharToByte(Short.MIN_VALUE), 0);
+ assertIntEquals(shortToCharToByte(Short.MAX_VALUE), -1);
+ assertStringEquals(shortToCharToBytePrint((short) 1025), "c=1025, b=1");
+ assertStringEquals(shortToCharToBytePrint((short) 1023), "c=1023, b=-1");
+ assertStringEquals(shortToCharToBytePrint((short) -1), "c=65535, b=-1");
}
public static boolean booleanField;
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index decdd1f324..8d73d69db9 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -241,13 +241,14 @@ public class Main {
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
/// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: Arm64DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
public static void $opt$validateExtendByteInt1(int a, byte b) {
assertIntEquals(a + $noinline$byteToChar (b), a + (char)b);
+ // Conversions byte->short and short->int are implicit; nothing to merge.
assertIntEquals(a + $noinline$byteToShort(b), a + (short)b);
}
@@ -266,17 +267,24 @@ public class Main {
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: Arm64DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
- /// CHECK: TypeConversion
/// CHECK-NOT: TypeConversion
public static void $opt$validateExtendByteLong(long a, byte b) {
- // The first two tests have a type conversion.
+ // In each of the following tests, there will be a merge on the LHS.
+
+ // The first test has an explicit byte->char conversion on RHS,
+ // followed by a conversion that is merged with the Add.
assertLongEquals(a + $noinline$byteToChar (b), a + (char)b);
+ // Since conversions byte->short and byte->int are implicit, the RHS
+ // for the two tests below is the same and one is eliminated by GVN.
+ // The other is then merged to a shifter operand instruction.
assertLongEquals(a + $noinline$byteToShort(b), a + (short)b);
- // This test does not because the conversion to `int` is optimized away.
assertLongEquals(a + $noinline$byteToInt (b), a + (int)b);
}