[optimizing] Fold HTypeConversion of constants

While looking into optimizing long shifts on x86, I found that the
compiler wasn't folding HTypeConversion of constants.  Add simple
conversions of constants, taking care of float/double values
with NaNs and small/large values, ensuring Java conversion semantics.

Add checker cases to see that constant folding of HTypeConversion is
done.

Ensure 422-type-conversion type conversion routiness don't get
inlined to avoid compile time folding.

Change-Id: I5a4eb376b64bc4e41bf908af5875bed312efb228
Signed-off-by: Mark Mendell <mark.p.mendell@intel.com>
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index bdbd571..fcfedab 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -34,10 +34,15 @@
 // Binary encoding of 2^31 for type double.
 static int64_t constexpr k2Pow31EncodingForDouble = INT64_C(0x41E0000000000000);
 
+// Minimum value for a primitive integer.
+static int32_t constexpr kPrimIntMin = 0x80000000;
+// Minimum value for a primitive long.
+static int64_t constexpr kPrimLongMin = INT64_C(0x8000000000000000);
+
 // Maximum value for a primitive integer.
 static int32_t constexpr kPrimIntMax = 0x7fffffff;
 // Maximum value for a primitive long.
-static int64_t constexpr kPrimLongMax = 0x7fffffffffffffff;
+static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff);
 
 class Assembler;
 class CodeGenerator;
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 5a1d9b4..20ce110 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -71,6 +71,14 @@
           inst->ReplaceWith(constant);
           inst->GetBlock()->RemoveInstruction(inst);
         }
+      } else if (inst->IsTypeConversion()) {
+        // Constant folding: replace `TypeConversion(a)' with a constant at
+        // compile time if `a' is a constant.
+        HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
+        if (constant != nullptr) {
+          inst->ReplaceWith(constant);
+          inst->GetBlock()->RemoveInstruction(inst);
+        }
       } else if (inst->IsDivZeroCheck()) {
         // We can safely remove the check if the input is a non-null constant.
         HDivZeroCheck* check = inst->AsDivZeroCheck();
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 41adc72..47da9cc 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -16,6 +16,7 @@
 
 #include "nodes.h"
 
+#include "code_generator.h"
 #include "ssa_builder.h"
 #include "base/bit_vector-inl.h"
 #include "utils/growable_array.h"
@@ -794,6 +795,84 @@
   }
 }
 
+HConstant* HTypeConversion::TryStaticEvaluation() const {
+  HGraph* graph = GetBlock()->GetGraph();
+  if (GetInput()->IsIntConstant()) {
+    int32_t value = GetInput()->AsIntConstant()->GetValue();
+    switch (GetResultType()) {
+      case Primitive::kPrimLong:
+        return graph->GetLongConstant(static_cast<int64_t>(value));
+      case Primitive::kPrimFloat:
+        return graph->GetFloatConstant(static_cast<float>(value));
+      case Primitive::kPrimDouble:
+        return graph->GetDoubleConstant(static_cast<double>(value));
+      default:
+        return nullptr;
+    }
+  } else if (GetInput()->IsLongConstant()) {
+    int64_t value = GetInput()->AsLongConstant()->GetValue();
+    switch (GetResultType()) {
+      case Primitive::kPrimInt:
+        return graph->GetIntConstant(static_cast<int32_t>(value));
+      case Primitive::kPrimFloat:
+        return graph->GetFloatConstant(static_cast<float>(value));
+      case Primitive::kPrimDouble:
+        return graph->GetDoubleConstant(static_cast<double>(value));
+      default:
+        return nullptr;
+    }
+  } else if (GetInput()->IsFloatConstant()) {
+    float value = GetInput()->AsFloatConstant()->GetValue();
+    switch (GetResultType()) {
+      case Primitive::kPrimInt:
+        if (std::isnan(value))
+          return graph->GetIntConstant(0);
+        if (value >= kPrimIntMax)
+          return graph->GetIntConstant(kPrimIntMax);
+        if (value <= kPrimIntMin)
+          return graph->GetIntConstant(kPrimIntMin);
+        return graph->GetIntConstant(static_cast<int32_t>(value));
+      case Primitive::kPrimLong:
+        if (std::isnan(value))
+          return graph->GetLongConstant(0);
+        if (value >= kPrimLongMax)
+          return graph->GetLongConstant(kPrimLongMax);
+        if (value <= kPrimLongMin)
+          return graph->GetLongConstant(kPrimLongMin);
+        return graph->GetLongConstant(static_cast<int64_t>(value));
+      case Primitive::kPrimDouble:
+        return graph->GetDoubleConstant(static_cast<double>(value));
+      default:
+        return nullptr;
+    }
+  } else if (GetInput()->IsDoubleConstant()) {
+    double value = GetInput()->AsDoubleConstant()->GetValue();
+    switch (GetResultType()) {
+      case Primitive::kPrimInt:
+        if (std::isnan(value))
+          return graph->GetIntConstant(0);
+        if (value >= kPrimIntMax)
+          return graph->GetIntConstant(kPrimIntMax);
+        if (value <= kPrimLongMin)
+          return graph->GetIntConstant(kPrimIntMin);
+        return graph->GetIntConstant(static_cast<int32_t>(value));
+      case Primitive::kPrimLong:
+        if (std::isnan(value))
+          return graph->GetLongConstant(0);
+        if (value >= kPrimLongMax)
+          return graph->GetLongConstant(kPrimLongMax);
+        if (value <= kPrimLongMin)
+          return graph->GetLongConstant(kPrimLongMin);
+        return graph->GetLongConstant(static_cast<int64_t>(value));
+      case Primitive::kPrimFloat:
+        return graph->GetFloatConstant(static_cast<float>(value));
+      default:
+        return nullptr;
+    }
+  }
+  return nullptr;
+}
+
 HConstant* HUnaryOperation::TryStaticEvaluation() const {
   if (GetInput()->IsIntConstant()) {
     int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 0089f22..cb2e5cc 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2982,6 +2982,10 @@
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
 
+  // Try to statically evaluate the conversion and return a HConstant
+  // containing the result.  If the input cannot be converted, return nullptr.
+  HConstant* TryStaticEvaluation() const;
+
   DECLARE_INSTRUCTION(TypeConversion);
 
  private:
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 7ce2868..447b9b8 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -625,65 +625,67 @@
     assertCharEquals((char)0, $opt$IntToChar(-2147483648));  // -(2^31)
   }
 
+  // A dummy value to defeat inlining of these routines.
+  static boolean doThrow = false;
 
   // These methods produce int-to-long Dex instructions.
-  static long $opt$ByteToLong(byte a) { return (long)a; }
-  static long $opt$ShortToLong(short a) { return (long)a; }
-  static long $opt$IntToLong(int a) { return (long)a; }
-  static long $opt$CharToLong(int a) { return (long)a; }
+  static long $opt$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
+  static long $opt$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
 
   // These methods produce int-to-float Dex instructions.
-  static float $opt$ByteToFloat(byte a) { return (float)a; }
-  static float $opt$ShortToFloat(short a) { return (float)a; }
-  static float $opt$IntToFloat(int a) { return (float)a; }
-  static float $opt$CharToFloat(char a) { return (float)a; }
+  static float $opt$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; }
+  static float $opt$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; }
 
   // These methods produce int-to-double Dex instructions.
-  static double $opt$ByteToDouble(byte a) { return (double)a; }
-  static double $opt$ShortToDouble(short a) { return (double)a; }
-  static double $opt$IntToDouble(int a) { return (double)a; }
-  static double $opt$CharToDouble(int a) { return (double)a; }
+  static double $opt$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
+  static double $opt$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
 
   // These methods produce long-to-int Dex instructions.
-  static int $opt$LongToInt(long a) { return (int)a; }
-  static int $opt$LongLiteralToInt() { return (int)42L; }
+  static int $opt$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; }
+  static int $opt$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; }
 
   // This method produces a long-to-float Dex instruction.
-  static float $opt$LongToFloat(long a) { return (float)a; }
+  static float $opt$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; }
 
   // This method produces a long-to-double Dex instruction.
-  static double $opt$LongToDouble(long a) { return (double)a; }
+  static double $opt$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; }
 
   // This method produces a float-to-int Dex instruction.
-  static int $opt$FloatToInt(float a) { return (int)a; }
+  static int $opt$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; }
 
   // This method produces a float-to-long Dex instruction.
-  static long $opt$FloatToLong(float a){ return (long)a; }
+  static long $opt$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; }
 
   // This method produces a float-to-double Dex instruction.
-  static double $opt$FloatToDouble(float a) { return (double)a; }
+  static double $opt$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; }
 
   // This method produces a double-to-int Dex instruction.
-  static int $opt$DoubleToInt(double a){ return (int)a; }
+  static int $opt$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; }
 
   // This method produces a double-to-long Dex instruction.
-  static long $opt$DoubleToLong(double a){ return (long)a; }
+  static long $opt$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; }
 
   // This method produces a double-to-float Dex instruction.
-  static float $opt$DoubleToFloat(double a) { return (float)a; }
+  static float $opt$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; }
 
   // These methods produce int-to-byte Dex instructions.
-  static byte $opt$ShortToByte(short a) { return (byte)a; }
-  static byte $opt$IntToByte(int a) { return (byte)a; }
-  static byte $opt$CharToByte(char a) { return (byte)a; }
+  static byte $opt$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; }
+  static byte $opt$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; }
+  static byte $opt$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; }
 
   // These methods produce int-to-short Dex instructions.
-  static short $opt$ByteToShort(byte a) { return (short)a; }
-  static short $opt$IntToShort(int a) { return (short)a; }
-  static short $opt$CharToShort(char a) { return (short)a; }
+  static short $opt$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; }
+  static short $opt$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; }
+  static short $opt$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; }
 
   // These methods produce int-to-char Dex instructions.
-  static char $opt$ByteToChar(byte a) { return (char)a; }
-  static char $opt$ShortToChar(short a) { return (char)a; }
-  static char $opt$IntToChar(int a) { return (char)a; }
+  static char $opt$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; }
+  static char $opt$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; }
+  static char $opt$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; }
 }
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index c89ab4d..6699acd 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -34,6 +34,18 @@
     }
   }
 
+  public static void assertFloatEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void assertDoubleEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
   /**
    * Tiny three-register program exercising int constant folding
    * on negation.
@@ -461,6 +473,174 @@
     return arg < Double.NaN;
   }
 
+  // CHECK-START: int Main.ReturnInt33() constant_folding (before)
+  // CHECK-DAG:     [[Const33:j\d+]]  LongConstant 33
+  // CHECK-DAG:     [[Convert:i\d+]]  TypeConversion [[Const33]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: int Main.ReturnInt33() constant_folding (after)
+  // CHECK-DAG:     [[Const33:i\d+]]  IntConstant 33
+  // CHECK-DAG:                       Return [ [[Const33]] ]
+
+  public static int ReturnInt33() {
+    long imm = 33L;
+    return (int) imm;
+  }
+
+  // CHECK-START: int Main.ReturnIntMax() constant_folding (before)
+  // CHECK-DAG:     [[ConstMax:f\d+]] FloatConstant 1e+34
+  // CHECK-DAG:     [[Convert:i\d+]]  TypeConversion [[ConstMax]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: int Main.ReturnIntMax() constant_folding (after)
+  // CHECK-DAG:     [[ConstMax:i\d+]] IntConstant 2147483647
+  // CHECK-DAG:                       Return [ [[ConstMax]] ]
+
+  public static int ReturnIntMax() {
+    float imm = 1.0e34f;
+    return (int) imm;
+  }
+
+  // CHECK-START: int Main.ReturnInt0() constant_folding (before)
+  // CHECK-DAG:     [[ConstNaN:d\d+]] DoubleConstant nan
+  // CHECK-DAG:     [[Convert:i\d+]]  TypeConversion [[ConstNaN]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: int Main.ReturnInt0() constant_folding (after)
+  // CHECK-DAG:     [[Const0:i\d+]]   IntConstant 0
+  // CHECK-DAG:                       Return [ [[Const0]] ]
+
+  public static int ReturnInt0() {
+    double imm = Double.NaN;
+    return (int) imm;
+  }
+
+  // CHECK-START: long Main.ReturnLong33() constant_folding (before)
+  // CHECK-DAG:     [[Const33:i\d+]]  IntConstant 33
+  // CHECK-DAG:     [[Convert:j\d+]]  TypeConversion [[Const33]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: long Main.ReturnLong33() constant_folding (after)
+  // CHECK-DAG:     [[Const33:j\d+]]  LongConstant 33
+  // CHECK-DAG:                       Return [ [[Const33]] ]
+
+  public static long ReturnLong33() {
+    int imm = 33;
+    return (long) imm;
+  }
+
+  // CHECK-START: long Main.ReturnLong34() constant_folding (before)
+  // CHECK-DAG:     [[Const34:f\d+]]  FloatConstant 34
+  // CHECK-DAG:     [[Convert:j\d+]]  TypeConversion [[Const34]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: long Main.ReturnLong34() constant_folding (after)
+  // CHECK-DAG:     [[Const34:j\d+]]  LongConstant 34
+  // CHECK-DAG:                       Return [ [[Const34]] ]
+
+  public static long ReturnLong34() {
+    float imm = 34.0f;
+    return (long) imm;
+  }
+
+  // CHECK-START: long Main.ReturnLong0() constant_folding (before)
+  // CHECK-DAG:     [[ConstNaN:d\d+]] DoubleConstant nan
+  // CHECK-DAG:     [[Convert:j\d+]]  TypeConversion [[ConstNaN]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: long Main.ReturnLong0() constant_folding (after)
+  // CHECK-DAG:     [[Const0:j\d+]]   LongConstant 0
+  // CHECK-DAG:                       Return [ [[Const0]] ]
+
+  public static long ReturnLong0() {
+    double imm = -Double.NaN;
+    return (long) imm;
+  }
+
+  // CHECK-START: float Main.ReturnFloat33() constant_folding (before)
+  // CHECK-DAG:     [[Const33:i\d+]]  IntConstant 33
+  // CHECK-DAG:     [[Convert:f\d+]]  TypeConversion [[Const33]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: float Main.ReturnFloat33() constant_folding (after)
+  // CHECK-DAG:     [[Const33:f\d+]]  FloatConstant 33
+  // CHECK-DAG:                       Return [ [[Const33]] ]
+
+  public static float ReturnFloat33() {
+    int imm = 33;
+    return (float) imm;
+  }
+
+  // CHECK-START: float Main.ReturnFloat34() constant_folding (before)
+  // CHECK-DAG:     [[Const34:j\d+]]  LongConstant 34
+  // CHECK-DAG:     [[Convert:f\d+]]  TypeConversion [[Const34]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: float Main.ReturnFloat34() constant_folding (after)
+  // CHECK-DAG:     [[Const34:f\d+]]  FloatConstant 34
+  // CHECK-DAG:                       Return [ [[Const34]] ]
+
+  public static float ReturnFloat34() {
+    long imm = 34L;
+    return (float) imm;
+  }
+
+  // CHECK-START: float Main.ReturnFloat99P25() constant_folding (before)
+  // CHECK-DAG:     [[Const:d\d+]]    DoubleConstant 99.25
+  // CHECK-DAG:     [[Convert:f\d+]]  TypeConversion [[Const]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: float Main.ReturnFloat99P25() constant_folding (after)
+  // CHECK-DAG:     [[Const:f\d+]]    FloatConstant 99.25
+  // CHECK-DAG:                       Return [ [[Const]] ]
+
+  public static float ReturnFloat99P25() {
+    double imm = 99.25;
+    return (float) imm;
+  }
+
+  // CHECK-START: double Main.ReturnDouble33() constant_folding (before)
+  // CHECK-DAG:     [[Const33:i\d+]]  IntConstant 33
+  // CHECK-DAG:     [[Convert:d\d+]]  TypeConversion [[Const33]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: double Main.ReturnDouble33() constant_folding (after)
+  // CHECK-DAG:     [[Const33:d\d+]]  DoubleConstant 33
+  // CHECK-DAG:                       Return [ [[Const33]] ]
+
+  public static double ReturnDouble33() {
+    int imm = 33;
+    return (double) imm;
+  }
+
+  // CHECK-START: double Main.ReturnDouble34() constant_folding (before)
+  // CHECK-DAG:     [[Const34:j\d+]]  LongConstant 34
+  // CHECK-DAG:     [[Convert:d\d+]]  TypeConversion [[Const34]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: double Main.ReturnDouble34() constant_folding (after)
+  // CHECK-DAG:     [[Const34:d\d+]]  DoubleConstant 34
+  // CHECK-DAG:                       Return [ [[Const34]] ]
+
+  public static double ReturnDouble34() {
+    long imm = 34L;
+    return (double) imm;
+  }
+
+  // CHECK-START: double Main.ReturnDouble99P25() constant_folding (before)
+  // CHECK-DAG:     [[Const:f\d+]]    FloatConstant 99.25
+  // CHECK-DAG:     [[Convert:d\d+]]  TypeConversion [[Const]]
+  // CHECK-DAG:                       Return [ [[Convert]] ]
+
+  // CHECK-START: double Main.ReturnDouble99P25() constant_folding (after)
+  // CHECK-DAG:     [[Const:d\d+]]    DoubleConstant 99.25
+  // CHECK-DAG:                       Return [ [[Const]] ]
+
+  public static double ReturnDouble99P25() {
+    float imm = 99.25f;
+    return (double) imm;
+  }
+
   public static void main(String[] args) {
     assertIntEquals(IntNegation(), -42);
     assertIntEquals(IntAddition1(), 3);
@@ -485,5 +665,17 @@
     assertIntEquals(XorSameInt(arbitrary), 0);
     assertFalse(CmpFloatGreaterThanNaN(arbitrary));
     assertFalse(CmpDoubleLessThanNaN(arbitrary));
+    assertIntEquals(ReturnInt33(), 33);
+    assertIntEquals(ReturnIntMax(), 2147483647);
+    assertIntEquals(ReturnInt0(), 0);
+    assertLongEquals(ReturnLong33(), 33);
+    assertLongEquals(ReturnLong34(), 34);
+    assertLongEquals(ReturnLong0(), 0);
+    assertFloatEquals(ReturnFloat33(), 33);
+    assertFloatEquals(ReturnFloat34(), 34);
+    assertFloatEquals(ReturnFloat99P25(), 99.25f);
+    assertDoubleEquals(ReturnDouble33(), 33);
+    assertDoubleEquals(ReturnDouble34(), 34);
+    assertDoubleEquals(ReturnDouble99P25(), 99.25);
   }
 }