diff options
| -rw-r--r-- | compiler/elf_writer.cc | 13 | ||||
| -rw-r--r-- | compiler/elf_writer.h | 4 | ||||
| -rw-r--r-- | compiler/image_writer.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.h | 7 | ||||
| -rw-r--r-- | compiler/optimizing/constant_folding.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 79 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 4 | ||||
| -rw-r--r-- | patchoat/patchoat.cc | 19 | ||||
| -rw-r--r-- | runtime/elf_file.cc | 40 | ||||
| -rw-r--r-- | runtime/elf_file.h | 2 | ||||
| -rw-r--r-- | runtime/elf_file_impl.h | 4 | ||||
| -rw-r--r-- | test/422-type-conversion/src/Main.java | 64 | ||||
| -rw-r--r-- | test/442-checker-constant-folding/src/Main.java | 192 |
13 files changed, 379 insertions, 59 deletions
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc index 47402f38c2..f75638d517 100644 --- a/compiler/elf_writer.cc +++ b/compiler/elf_writer.cc @@ -39,16 +39,17 @@ uintptr_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) { } void ElfWriter::GetOatElfInformation(File* file, - size_t& oat_loaded_size, - size_t& oat_data_offset) { + size_t* oat_loaded_size, + size_t* oat_data_offset) { std::string error_msg; std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, false, false, &error_msg)); CHECK(elf_file.get() != nullptr) << error_msg; - oat_loaded_size = elf_file->GetLoadedSize(); - CHECK_NE(0U, oat_loaded_size); - oat_data_offset = GetOatDataAddress(elf_file.get()); - CHECK_NE(0U, oat_data_offset); + bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg); + CHECK(success) << error_msg; + CHECK_NE(0U, *oat_loaded_size); + *oat_data_offset = GetOatDataAddress(elf_file.get()); + CHECK_NE(0U, *oat_data_offset); } bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) { diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h index 033c1f8c05..8e13b51bbe 100644 --- a/compiler/elf_writer.h +++ b/compiler/elf_writer.h @@ -38,8 +38,8 @@ class ElfWriter { // Looks up information about location of oat file in elf file container. // Used for ImageWriter to perform memory layout. static void GetOatElfInformation(File* file, - size_t& oat_loaded_size, - size_t& oat_data_offset); + size_t* oat_loaded_size, + size_t* oat_data_offset); // Returns runtime oat_data runtime address for an opened ElfFile. static uintptr_t GetOatDataAddress(ElfFile* elf_file); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4dc75091e7..195949bf3c 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -166,7 +166,7 @@ bool ImageWriter::Write(const std::string& image_filename, size_t oat_loaded_size = 0; size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); + ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); Thread::Current()->TransitionFromSuspendedToRunnable(); CreateHeader(oat_loaded_size, oat_data_offset); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index bdbd571133..fcfedab9bb 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 5a1d9b488f..20ce1105ce 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 41adc7223e..47da9cc17c 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<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 0089f22169..cb2e5ccab4 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/patchoat/patchoat.cc b/patchoat/patchoat.cc index 4dc0967bc0..ef84a1717c 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -650,29 +650,34 @@ bool PatchOat::PatchElf() { template <typename ElfFileImpl> bool PatchOat::PatchElf(ElfFileImpl* oat_file) { TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_); + + // Fix up absolute references to locations within the boot image. if (!oat_file->ApplyOatPatchesTo(".text", delta_)) { return false; } + // Update the OatHeader fields referencing the boot image. if (!PatchOatHeader<ElfFileImpl>(oat_file)) { return false; } - bool need_fixup = false; + bool need_boot_oat_fixup = true; for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) { auto hdr = oat_file->GetProgramHeader(i); - if ((hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) || - (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset)) { - need_fixup = true; + if (hdr->p_type == PT_LOAD && hdr->p_vaddr == 0u) { + need_boot_oat_fixup = false; break; } } - if (!need_fixup) { - // This was never passed through ElfFixup so all headers/symbols just have their offset as - // their addr. Therefore we do not need to update these parts. + if (!need_boot_oat_fixup) { + // This is an app oat file that can be loaded at an arbitrary address in memory. + // Boot image references were patched above and there's nothing else to do. return true; } + // This is a boot oat file that's loaded at a particular address and we need + // to patch all absolute addresses, starting with ELF program headers. + t.NewTiming("Fixup Elf Headers"); // Fixup Phdr's oat_file->FixupProgramHeaders(delta_); diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index e909e64e7a..0c5210dc9d 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1080,9 +1080,9 @@ typename ElfTypes::Rela& ElfFileImpl<ElfTypes>::GetRela(Elf_Shdr& section_header // Base on bionic phdr_table_get_load_size template <typename ElfTypes> -size_t ElfFileImpl<ElfTypes>::GetLoadedSize() const { - Elf_Addr min_vaddr = 0xFFFFFFFFu; - Elf_Addr max_vaddr = 0x00000000u; +bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) const { + Elf_Addr min_vaddr = static_cast<Elf_Addr>(-1); + Elf_Addr max_vaddr = 0u; for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) { Elf_Phdr* program_header = GetProgramHeader(i); if (program_header->p_type != PT_LOAD) { @@ -1093,6 +1093,15 @@ size_t ElfFileImpl<ElfTypes>::GetLoadedSize() const { min_vaddr = begin_vaddr; } Elf_Addr end_vaddr = program_header->p_vaddr + program_header->p_memsz; + if (UNLIKELY(begin_vaddr > end_vaddr)) { + std::ostringstream oss; + oss << "Program header #" << i << " has overflow in p_vaddr+p_memsz: 0x" << std::hex + << program_header->p_vaddr << "+0x" << program_header->p_memsz << "=0x" << end_vaddr + << " in ELF file \"" << file_->GetPath() << "\""; + *error_msg = oss.str(); + *size = static_cast<size_t>(-1); + return false; + } if (end_vaddr > max_vaddr) { max_vaddr = end_vaddr; } @@ -1100,8 +1109,18 @@ size_t ElfFileImpl<ElfTypes>::GetLoadedSize() const { min_vaddr = RoundDown(min_vaddr, kPageSize); max_vaddr = RoundUp(max_vaddr, kPageSize); CHECK_LT(min_vaddr, max_vaddr) << file_->GetPath(); - size_t loaded_size = max_vaddr - min_vaddr; - return loaded_size; + Elf_Addr loaded_size = max_vaddr - min_vaddr; + // Check that the loaded_size fits in size_t. + if (UNLIKELY(loaded_size > std::numeric_limits<size_t>::max())) { + std::ostringstream oss; + oss << "Loaded size is 0x" << std::hex << loaded_size << " but maximum size_t is 0x" + << std::numeric_limits<size_t>::max() << " for ELF file \"" << file_->GetPath() << "\""; + *error_msg = oss.str(); + *size = static_cast<size_t>(-1); + return false; + } + *size = loaded_size; + return true; } template <typename ElfTypes> @@ -1164,9 +1183,14 @@ bool ElfFileImpl<ElfTypes>::Load(bool executable, std::string* error_msg) { } std::string reservation_name("ElfFile reservation for "); reservation_name += file_->GetPath(); + size_t loaded_size; + if (!GetLoadedSize(&loaded_size, error_msg)) { + DCHECK(!error_msg->empty()); + return false; + } std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(), reserve_base_override, - GetLoadedSize(), PROT_NONE, false, false, + loaded_size, PROT_NONE, false, false, error_msg)); if (reserve.get() == nullptr) { *error_msg = StringPrintf("Failed to allocate %s: %s", @@ -1915,8 +1939,8 @@ uint64_t ElfFile::FindSymbolAddress(unsigned section_type, DELEGATE_TO_IMPL(FindSymbolAddress, section_type, symbol_name, build_map); } -size_t ElfFile::GetLoadedSize() const { - DELEGATE_TO_IMPL(GetLoadedSize); +bool ElfFile::GetLoadedSize(size_t* size, std::string* error_msg) const { + DELEGATE_TO_IMPL(GetLoadedSize, size, error_msg); } bool ElfFile::Strip(File* file, std::string* error_msg) { diff --git a/runtime/elf_file.h b/runtime/elf_file.h index fe6896dfe7..48cb4b8b2e 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -66,7 +66,7 @@ class ElfFile { const std::string& symbol_name, bool build_map); - size_t GetLoadedSize() const; + bool GetLoadedSize(size_t* size, std::string* error_msg) const; // Strip an ELF file of unneeded debugging information. // Returns true on success, false on failure. diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 80950c6197..3ad096f983 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -106,8 +106,8 @@ class ElfFileImpl { Elf_Word GetRelaNum(Elf_Shdr&) const; Elf_Rela& GetRela(Elf_Shdr&, Elf_Word) const; - // Returns the expected size when the file is loaded at runtime - size_t GetLoadedSize() const; + // Retrieves the expected size when the file is loaded at runtime. Returns true if successful. + bool GetLoadedSize(size_t* size, std::string* error_msg) const; // Load segments into memory based on PT_LOAD program headers. // executable is true at run time, false at compile time. diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 7ce2868283..447b9b8fd2 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 c89ab4dffe..6699acd96e 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); } } |