From: Mark Mendell Date: Wed, 6 May 2015 14:55:34 +0000 (-0400) Subject: [optimizing] Fold HTypeConversion of constants X-Git-Tag: android-x86-7.1-r1~889^2~1264^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e82549b14c7def0a45461183964f7e6a34cbb70c;p=android-x86%2Fart.git [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 --- diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index bdbd57113..fcfedab9b 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -34,10 +34,15 @@ static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000); // 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 5a1d9b488..20ce1105c 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -71,6 +71,14 @@ void HConstantFolding::Run() { 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 41adc7223..47da9cc17 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 @@ void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { } } +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(value)); + case Primitive::kPrimFloat: + return graph->GetFloatConstant(static_cast(value)); + case Primitive::kPrimDouble: + return graph->GetDoubleConstant(static_cast(value)); + default: + return nullptr; + } + } else if (GetInput()->IsLongConstant()) { + int64_t value = GetInput()->AsLongConstant()->GetValue(); + switch (GetResultType()) { + case Primitive::kPrimInt: + return graph->GetIntConstant(static_cast(value)); + case Primitive::kPrimFloat: + return graph->GetFloatConstant(static_cast(value)); + case Primitive::kPrimDouble: + return graph->GetDoubleConstant(static_cast(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(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(value)); + case Primitive::kPrimDouble: + return graph->GetDoubleConstant(static_cast(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(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(value)); + case Primitive::kPrimFloat: + return graph->GetFloatConstant(static_cast(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 0089f2216..cb2e5ccab 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2982,6 +2982,10 @@ class HTypeConversion : public HExpression<1> { 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 7ce286828..447b9b8fd 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -625,65 +625,67 @@ public class Main { 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 c89ab4dff..6699acd96 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -34,6 +34,18 @@ public class Main { } } + 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 @@ public class Main { 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 @@ public class Main { 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); } }