OSDN Git Service

Optimizing: Simplify consecutive type conversions.
authorVladimir Marko <vmarko@google.com>
Fri, 12 Feb 2016 12:06:05 +0000 (12:06 +0000)
committerVladimir Marko <vmarko@google.com>
Mon, 15 Feb 2016 15:06:44 +0000 (15:06 +0000)
Merge two consecutive type conversions to one if the result
of such merged conversion is guaranteed to be the same and
remove all implicit conversions, not just conversions to the
same type. Improve codegens to handle conversions from long
to integral types smaller than int.

This will make it easier to simplify `(byte) (x & 0xffL)` to
`(byte) x` where the conversion from long to byte is done by
two dex instructions, long-to-int and in int-to-byte.

Bug: 23965701
Change-Id: I833f193556671136ad2cd3f5b31cdfbc2d99c19d

compiler/optimizing/code_generator_arm.cc
compiler/optimizing/code_generator_mips64.cc
compiler/optimizing/code_generator_x86.cc
compiler/optimizing/code_generator_x86_64.cc
compiler/optimizing/instruction_simplifier.cc
test/458-checker-instruction-simplification/src/Main.java
test/551-checker-shifter-operand/src/Main.java

index 005b6c1..87f52c6 100644 (file)
@@ -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:
index 71d65e8..119084e 100644 (file)
@@ -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:
index 3713690..07edd97 100644 (file)
@@ -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:
index 35603aa..a5c386d 100644 (file)
@@ -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;
 
index 0029cc3..98f8009 100644 (file)
@@ -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;
+      }
+    }
   }
 }
 
index 2f80470..07478b2 100644 (file)
@@ -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;
index decdd1f..8d73d69 100644 (file)
@@ -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);
   }