diff options
37 files changed, 636 insertions, 128 deletions
diff --git a/Android.mk b/Android.mk index e7623c6e1c..3467f1d062 100644 --- a/Android.mk +++ b/Android.mk @@ -55,16 +55,16 @@ endif clean-oat-target: adb root adb wait-for-device remount - adb shell sh -c "rm -rf $(ART_TARGET_NATIVETEST_DIR)" - adb shell sh -c "rm -rf $(ART_TARGET_TEST_DIR)" - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*" - adb shell sh -c "rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH)" - adb shell sh -c "rm -rf system/app/$(DEX2OAT_TARGET_ARCH)" + adb shell rm -rf $(ART_TARGET_NATIVETEST_DIR) + adb shell rm -rf $(ART_TARGET_TEST_DIR) + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/* + adb shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH) + adb shell rm -rf system/app/$(DEX2OAT_TARGET_ARCH) ifdef TARGET_2ND_ARCH - adb shell sh -c "rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)" - adb shell sh -c "rm -rf system/app/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)" + adb shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH) + adb shell rm -rf system/app/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH) endif - adb shell sh -c "rm -rf data/run-test/test-*/dalvik-cache/*" + adb shell rm -rf data/run-test/test-*/dalvik-cache/* ifneq ($(art_dont_bother),true) @@ -404,9 +404,9 @@ use-dalvik: use-art-full: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" - adb shell setprop dalvik.vm.dex2oat-filter "" - adb shell setprop dalvik.vm.image-dex2oat-filter "" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* + adb shell setprop dalvik.vm.dex2oat-filter \"\" + adb shell setprop dalvik.vm.image-dex2oat-filter \"\" adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so adb shell start @@ -414,9 +414,9 @@ use-art-full: use-artd-full: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" - adb shell setprop dalvik.vm.dex2oat-filter "" - adb shell setprop dalvik.vm.image-dex2oat-filter "" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* + adb shell setprop dalvik.vm.dex2oat-filter \"\" + adb shell setprop dalvik.vm.image-dex2oat-filter \"\" adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so adb shell start @@ -424,7 +424,7 @@ use-artd-full: use-art-verify-at-runtime: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* adb shell setprop dalvik.vm.dex2oat-filter "verify-at-runtime" adb shell setprop dalvik.vm.image-dex2oat-filter "verify-at-runtime" adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so @@ -434,7 +434,7 @@ use-art-verify-at-runtime: use-art-interpret-only: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* adb shell setprop dalvik.vm.dex2oat-filter "interpret-only" adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only" adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so @@ -444,7 +444,7 @@ use-art-interpret-only: use-artd-interpret-only: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* adb shell setprop dalvik.vm.dex2oat-filter "interpret-only" adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only" adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so @@ -454,7 +454,7 @@ use-artd-interpret-only: use-art-verify-none: adb root adb wait-for-device shell stop - adb shell sh -c "rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*" + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/* adb shell setprop dalvik.vm.dex2oat-filter "verify-none" adb shell setprop dalvik.vm.image-dex2oat-filter "verify-none" adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index d1724ccfcb..3e427a33ee 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -110,10 +110,6 @@ ART_TARGET_CLANG_CFLAGS_x86_64 := ART_TARGET_CLANG_CFLAGS_arm64 += \ -DNVALGRIND -# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed -ART_TARGET_CLANG_CFLAGS_arm64 += \ - -fno-vectorize - # Warn about thread safety violations with clang. art_clang_cflags := -Wthread-safety diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk index e0c0b0c119..2d6b6a3cbf 100644 --- a/build/Android.common_path.mk +++ b/build/Android.common_path.mk @@ -80,7 +80,7 @@ HOST_CORE_IMG_LOCATION := $(HOST_OUT_JAVA_LIBRARIES)/core.art TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art # Jar files for core.art. -TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle +TARGET_CORE_JARS := core-libart conscrypt okhttp bouncycastle HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS)) HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar) diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc index 915fbcda04..6d8a7dab2b 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -58,14 +58,12 @@ inline void GvnDeadCodeElimination::MIRData::RemovePrevChange(int v_reg, MIRData low_def_over_high_word = prev_data->low_def_over_high_word; } else { prev_value = prev_data->prev_value_high; - low_def_over_high_word = - prev_data->prev_value_high.value != kNPos && !prev_data->high_def_over_low_word; + low_def_over_high_word = !prev_data->high_def_over_low_word; } } else { if (prev_data->vreg_def == v_reg) { prev_value_high = prev_data->prev_value; - high_def_over_low_word = - prev_data->prev_value.value != kNPos && !prev_data->low_def_over_high_word; + high_def_over_low_word = !prev_data->low_def_over_high_word; } else { prev_value_high = prev_data->prev_value_high; high_def_over_low_word = prev_data->high_def_over_low_word; @@ -340,8 +338,7 @@ void GvnDeadCodeElimination::VRegChains::RemoveChange(uint16_t change) { DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == data->vreg_def + 1); if (data->vreg_def == v_reg && data->low_def_over_high_word) { vreg_high_words_.SetBit(v_reg); - } else if (data->vreg_def != v_reg && - (data->high_def_over_low_word || data->prev_value_high.value == kNoValue)) { + } else if (data->vreg_def != v_reg && data->high_def_over_low_word) { vreg_high_words_.ClearBit(v_reg); } } else { diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index 4f8127338c..de591d0edb 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -1931,4 +1931,56 @@ TEST_F(GvnDeadCodeEliminationTestDiamond, LongOverlaps1) { } } +TEST_F(GvnDeadCodeEliminationTestSimple, MixedOverlaps1) { + static const MIRDef mirs[] = { + DEF_CONST(3, Instruction::CONST, 0u, 1000u), + DEF_MOVE(3, Instruction::MOVE, 1u, 0u), + DEF_CONST(3, Instruction::CONST, 2u, 2000u), + { 3, Instruction::INT_TO_LONG, 0, 0u, 1, { 2u }, 2, { 3u, 4u} }, + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 5u, 3u), + DEF_CONST(3, Instruction::CONST, 7u, 3000u), + DEF_CONST(3, Instruction::CONST, 8u, 4000u), + }; + + static const int32_t sreg_to_vreg_map[] = { 1, 2, 0, 0, 1, 3, 4, 0, 1 }; + PrepareSRegToVRegMap(sreg_to_vreg_map); + + PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 3, 5 }; + MarkAsWideSRegs(wide_sregs); + PerformGVN_DCE(); + + ASSERT_EQ(arraysize(mirs), value_names_.size()); + static const size_t diff_indexes[] = { 0, 2, 3, 5, 6 }; + ExpectValueNamesNE(diff_indexes); + EXPECT_EQ(value_names_[0], value_names_[1]); + EXPECT_EQ(value_names_[3], value_names_[4]); + + static const bool eliminated[] = { + false, true, false, false, true, false, false, + }; + static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); + for (size_t i = 0; i != arraysize(eliminated); ++i) { + bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); + EXPECT_EQ(eliminated[i], actually_eliminated) << i; + } + // Check renamed registers in CONST. + MIR* cst = &mirs_[0]; + ASSERT_EQ(Instruction::CONST, cst->dalvikInsn.opcode); + ASSERT_EQ(0, cst->ssa_rep->num_uses); + ASSERT_EQ(1, cst->ssa_rep->num_defs); + EXPECT_EQ(1, cst->ssa_rep->defs[0]); + EXPECT_EQ(2u, cst->dalvikInsn.vA); + // Check renamed registers in INT_TO_LONG. + MIR* int_to_long = &mirs_[3]; + ASSERT_EQ(Instruction::INT_TO_LONG, int_to_long->dalvikInsn.opcode); + ASSERT_EQ(1, int_to_long->ssa_rep->num_uses); + EXPECT_EQ(2, int_to_long->ssa_rep->uses[0]); + ASSERT_EQ(2, int_to_long->ssa_rep->num_defs); + EXPECT_EQ(5, int_to_long->ssa_rep->defs[0]); + EXPECT_EQ(6, int_to_long->ssa_rep->defs[1]); + EXPECT_EQ(3u, int_to_long->dalvikInsn.vA); + EXPECT_EQ(0u, int_to_long->dalvikInsn.vB); +} + } // namespace art 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/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 772fa9aa4b..773862710d 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -15,9 +15,11 @@ */ #include <dirent.h> +#include <errno.h> #include <fstream> -#include <sys/types.h> #include <map> +#include <string.h> +#include <sys/types.h> #include "gtest/gtest.h" #include "utils/arm/assembler_thumb2.h" @@ -105,12 +107,14 @@ void dump(std::vector<uint8_t>& code, const char* testname) { // Assemble the .S snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename); - system(cmd); + int cmd_result = system(cmd); + ASSERT_EQ(cmd_result, 0) << strerror(errno); // Remove the $d symbols to prevent the disassembler dumping the instructions // as .word snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename); - system(cmd); + int cmd_result2 = system(cmd); + ASSERT_EQ(cmd_result2, 0) << strerror(errno); // Disassemble. @@ -119,7 +123,8 @@ void dump(std::vector<uint8_t>& code, const char* testname) { if (kPrintResults) { // Print the results only, don't check. This is used to generate new output for inserting // into the .inc file. - system(cmd); + int cmd_result3 = system(cmd); + ASSERT_EQ(cmd_result3, 0) << strerror(errno); } else { // Check the results match the appropriate results in the .inc file. FILE *fp = popen(cmd, "r"); 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/art_field.cc b/runtime/art_field.cc index 2aed440f5e..47d5a76dc7 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -63,6 +63,17 @@ ArtField* ArtField::FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t f FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset) : nullptr; } +ArtField* ArtField::FindStaticFieldWithOffset(mirror::Class* klass, uint32_t field_offset) { + DCHECK(klass != nullptr); + auto* static_fields = klass->GetSFields(); + for (size_t i = 0, count = klass->NumStaticFields(); i < count; ++i) { + if (static_fields[i].GetOffset().Uint32Value() == field_offset) { + return &static_fields[i]; + } + } + return nullptr; +} + mirror::Class* ArtField::ProxyFindSystemClass(const char* descriptor) { DCHECK(GetDeclaringClass()->IsProxyClass()); return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); diff --git a/runtime/art_field.h b/runtime/art_field.h index c0620bf3c3..9d3dbd9e31 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -161,6 +161,9 @@ class ArtField { // Returns an instance field with this offset in the given class or null if not found. static ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns a static field with this offset in the given class or null if not found. + static ArtField* FindStaticFieldWithOffset(mirror::Class* klass, uint32_t field_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 0ae7863382..859de4bd5b 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -289,17 +289,17 @@ void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverit CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U); const char* program_name = ProgramInvocationShortName(); - write(STDERR_FILENO, program_name, strlen(program_name)); - write(STDERR_FILENO, " ", 1); - write(STDERR_FILENO, &log_characters[log_severity], 1); - write(STDERR_FILENO, " ", 1); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, program_name, strlen(program_name))); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1)); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, &log_characters[log_severity], 1)); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1)); // TODO: pid and tid. - write(STDERR_FILENO, file, strlen(file)); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, file, strlen(file))); // TODO: line. UNUSED(line); - write(STDERR_FILENO, "] ", 2); - write(STDERR_FILENO, message, strlen(message)); - write(STDERR_FILENO, "\n", 1); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, "] ", 2)); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, message, strlen(message))); + TEMP_FAILURE_RETRY(write(STDERR_FILENO, "\n", 1)); #endif } diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 0808999e24..b401066f7c 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -283,8 +283,7 @@ void ThrowNegativeArraySizeException(const char* msg) { // NoSuchFieldError void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, - const StringPiece& type, const StringPiece& name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const StringPiece& type, const StringPiece& name) { std::ostringstream msg; std::string temp; msg << "No " << scope << "field " << name << " of type " << type @@ -292,6 +291,13 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, ThrowException("Ljava/lang/NoSuchFieldError;", c, msg.str().c_str()); } +void ThrowNoSuchFieldException(mirror::Class* c, const StringPiece& name) { + std::ostringstream msg; + std::string temp; + msg << "No field " << name << " in class " << c->GetDescriptor(&temp); + ThrowException("Ljava/lang/NoSuchFieldException;", c, msg.str().c_str()); +} + // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, diff --git a/runtime/common_throws.h b/runtime/common_throws.h index df95cf9a9e..49890e21c6 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -149,6 +149,9 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, const StringPiece& type, const StringPiece& name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNoSuchFieldException(mirror::Class* c, const StringPiece& name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, 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/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h index 245e074294..1648aef51f 100644 --- a/runtime/gc/accounting/heap_bitmap.h +++ b/runtime/gc/accounting/heap_bitmap.h @@ -39,9 +39,11 @@ class HeapBitmap { void Clear(const mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); template<typename LargeObjectSetVisitor> bool Set(const mirror::Object* obj, const LargeObjectSetVisitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) ALWAYS_INLINE; template<typename LargeObjectSetVisitor> bool AtomicTestAndSet(const mirror::Object* obj, const LargeObjectSetVisitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) ALWAYS_INLINE; ContinuousSpaceBitmap* GetContinuousSpaceBitmap(const mirror::Object* obj) const; LargeObjectBitmap* GetLargeObjectBitmap(const mirror::Object* obj) const; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 55a8411863..53e56da093 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -368,10 +368,13 @@ bool MarkSweep::HeapReferenceMarkedCallback(mirror::HeapReference<mirror::Object class MarkSweepMarkObjectSlowPath { public: - explicit MarkSweepMarkObjectSlowPath(MarkSweep* mark_sweep) : mark_sweep_(mark_sweep) { + explicit MarkSweepMarkObjectSlowPath(MarkSweep* mark_sweep, Object* holder = nullptr, + MemberOffset offset = MemberOffset(0)) + : mark_sweep_(mark_sweep), holder_(holder), offset_(offset) { } - void operator()(const Object* obj) const ALWAYS_INLINE { + void operator()(const Object* obj) const ALWAYS_INLINE + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (kProfileLargeObjects) { // TODO: Differentiate between marking and testing somehow. ++mark_sweep_->large_object_test_; @@ -384,6 +387,13 @@ class MarkSweepMarkObjectSlowPath { LOG(INTERNAL_FATAL) << "Tried to mark " << obj << " not contained by any spaces"; LOG(INTERNAL_FATAL) << "Attempting see if it's a bad root"; mark_sweep_->VerifyRoots(); + if (holder_ != nullptr) { + ArtField* field = holder_->FindFieldByOffset(offset_); + LOG(INTERNAL_FATAL) << "Field info: holder=" << holder_ + << " holder_type=" << PrettyTypeOf(holder_) + << " offset=" << offset_.Uint32Value() + << " field=" << (field != nullptr ? field->GetName() : "nullptr"); + } PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL); MemMap::DumpMaps(LOG(INTERNAL_FATAL), true); LOG(FATAL) << "Can't mark invalid object"; @@ -392,9 +402,11 @@ class MarkSweepMarkObjectSlowPath { private: MarkSweep* const mark_sweep_; + mirror::Object* const holder_; + MemberOffset offset_; }; -inline void MarkSweep::MarkObjectNonNull(Object* obj) { +inline void MarkSweep::MarkObjectNonNull(Object* obj, Object* holder, MemberOffset offset) { DCHECK(obj != nullptr); if (kUseBakerOrBrooksReadBarrier) { // Verify all the objects have the correct pointer installed. @@ -416,7 +428,7 @@ inline void MarkSweep::MarkObjectNonNull(Object* obj) { if (kCountMarkedObjects) { ++mark_slowpath_count_; } - MarkSweepMarkObjectSlowPath visitor(this); + MarkSweepMarkObjectSlowPath visitor(this, holder, offset); // TODO: We already know that the object is not in the current_space_bitmap_ but MarkBitmap::Set // will check again. if (!mark_bitmap_->Set(obj, visitor)) { @@ -456,9 +468,9 @@ inline bool MarkSweep::MarkObjectParallel(const Object* obj) { } // Used to mark objects when processing the mark stack. If an object is null, it is not marked. -inline void MarkSweep::MarkObject(Object* obj) { +inline void MarkSweep::MarkObject(Object* obj, Object* holder, MemberOffset offset) { if (obj != nullptr) { - MarkObjectNonNull(obj); + MarkObjectNonNull(obj, holder, offset); } else if (kCountMarkedObjects) { ++mark_null_count_; } @@ -1209,7 +1221,7 @@ class MarkObjectVisitor { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current()); } - mark_sweep_->MarkObject(obj->GetFieldObject<mirror::Object>(offset)); + mark_sweep_->MarkObject(obj->GetFieldObject<mirror::Object>(offset), obj, offset); } private: diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 7e1af7b5df..d29d87af1e 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -199,7 +199,8 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Marks an object. - void MarkObject(mirror::Object* obj) + void MarkObject(mirror::Object* obj, mirror::Object* holder = nullptr, + MemberOffset offset = MemberOffset(0)) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -222,7 +223,8 @@ class MarkSweep : public GarbageCollector { static void VerifyImageRootVisitor(mirror::Object* root, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - void MarkObjectNonNull(mirror::Object* obj) + void MarkObjectNonNull(mirror::Object* obj, mirror::Object* holder = nullptr, + MemberOffset offset = MemberOffset(0)) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index d0eb083ec4..efead51800 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -981,7 +981,11 @@ void Hprof::DumpHeapClass(mirror::Class* klass) { // ClassObjects have their static fields appended, so aren't all the same size. // But they're at least this size. __ AddU4(sizeof(mirror::Class)); // instance size - } else if (klass->IsArrayClass() || klass->IsStringClass() || klass->IsPrimitive()) { + } else if (klass->IsStringClass()) { + // Strings are variable length with character data at the end like arrays. + // This outputs the size of an empty string. + __ AddU4(sizeof(mirror::String)); + } else if (klass->IsArrayClass() || klass->IsPrimitive()) { __ AddU4(0); } else { __ AddU4(klass->GetObjectSize()); // instance size @@ -1146,13 +1150,20 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) { // Output native value character array for strings. if (orig_klass->IsStringClass()) { mirror::String* s = obj->AsString(); - __ AddObjectId(reinterpret_cast<mirror::Object*>(s->GetValue())); + mirror::Object* value; + if (s->GetLength() == 0) { + // If string is empty, use an object-aligned address within the string for the value. + value = reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(s) + kObjectAlignment); + } else { + value = reinterpret_cast<mirror::Object*>(s->GetValue()); + } + __ AddObjectId(value); // Patch the instance field length. __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4)); __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); - __ AddObjectId(reinterpret_cast<mirror::Object*>(s->GetValue())); + __ AddObjectId(value); __ AddU4(StackTraceSerialNumber(obj)); __ AddU4(s->GetLength()); __ AddU1(hprof_basic_char); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ae67efbcd1..59d3008fc1 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -616,16 +616,17 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, uint16_t regList = inst->Fetch16(2); uint16_t count = num_ins; size_t arg_index = 0; - if (string_init) { - // Skip the referrer for the new static StringFactory call. - regList >>= 4; - ++arg_index; - } if (count == 5) { AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + 4U, (inst_data >> 8) & 0x0f); --count; - } + } + if (string_init) { + // Skip the referrer for the new static StringFactory call. + regList >>= 4; + ++first_dest_reg; + --count; + } for (; arg_index < count; ++arg_index, regList >>= 4) { AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index f30c93a5af..9006257d00 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -755,6 +755,64 @@ static void UnstartedSecurityGetSecurityPropertiesReader( result->SetL(h_obj.Get()); } +// This allows reading the new style of String objects during compilation. +static void UnstartedStringGetCharsNoCheck( + Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jint start = shadow_frame->GetVReg(arg_offset + 1); + jint end = shadow_frame->GetVReg(arg_offset + 2); + jint index = shadow_frame->GetVReg(arg_offset + 4); + mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString(); + if (string == nullptr) { + AbortTransactionOrFail(self, "String.getCharsNoCheck with null object"); + return; + } + StackHandleScope<1> hs(self); + Handle<mirror::CharArray> h_char_array(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset + 3)->AsCharArray())); + string->GetChars(start, end, h_char_array, index); +} + +// This allows reading chars from the new style of String objects during compilation. +static void UnstartedStringCharAt( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jint index = shadow_frame->GetVReg(arg_offset + 1); + mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString(); + if (string == nullptr) { + AbortTransactionOrFail(self, "String.charAt with null object"); + return; + } + result->SetC(string->CharAt(index)); +} + +// This allows creating the new style of String objects during compilation. +static void UnstartedStringFactoryNewStringFromChars( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jint offset = shadow_frame->GetVReg(arg_offset); + jint char_count = shadow_frame->GetVReg(arg_offset + 1); + DCHECK_GE(char_count, 0); + StackHandleScope<1> hs(self); + Handle<mirror::CharArray> h_char_array(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray())); + Runtime* runtime = Runtime::Current(); + gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); + result->SetL(mirror::String::AllocFromCharArray<true>(self, char_count, h_char_array, offset, allocator)); +} + +// This allows creating the new style of String objects during compilation. +static void UnstartedStringFastSubstring( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jint start = shadow_frame->GetVReg(arg_offset + 1); + jint length = shadow_frame->GetVReg(arg_offset + 2); + DCHECK_GE(length, 0); + StackHandleScope<1> hs(self); + Handle<mirror::String> h_string(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsString())); + Runtime* runtime = Runtime::Current(); + gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); + result->SetL(mirror::String::AllocFromString<true>(self, length, h_string, start, allocator)); +} + static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self, mirror::ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, @@ -1079,6 +1137,14 @@ static void UnstartedRuntimeInitializeInvokeHandlers() { &UnstartedMemoryPeekArrayEntry }, { "java.io.Reader java.security.Security.getSecurityPropertiesReader()", &UnstartedSecurityGetSecurityPropertiesReader }, + { "void java.lang.String.getCharsNoCheck(int, int, char[], int)", + &UnstartedStringGetCharsNoCheck }, + { "char java.lang.String.charAt(int)", + &UnstartedStringCharAt }, + { "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])", + &UnstartedStringFactoryNewStringFromChars }, + { "java.lang.String java.lang.String.fastSubstring(int, int)", + &UnstartedStringFastSubstring }, }; for (auto& def : defs) { diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 5dac985299..f9740bbfae 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -244,5 +244,10 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_val UNREACHABLE(); } +ArtField* Object::FindFieldByOffset(MemberOffset offset) { + return IsClass() ? ArtField::FindStaticFieldWithOffset(AsClass(), offset.Uint32Value()) + : ArtField::FindInstanceFieldWithOffset(GetClass(), offset.Uint32Value()); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index f2d879fef9..5afe99f3f8 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -439,6 +439,8 @@ class MANAGED LOCKABLE Object { void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) NO_THREAD_SAFETY_ANALYSIS; + ArtField* FindFieldByOffset(MemberOffset offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Used by object_test. static void SetHashCodeSeed(uint32_t new_seed); // Generate an identity hash code. Public for object test. diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index a779e97a3b..795a0eadca 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -252,7 +252,7 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam std::string name_str = name_string->ToModifiedUtf8(); // We may have a pending exception if we failed to resolve. if (!soa.Self()->IsExceptionPending()) { - soa.Self()->ThrowNewException("Ljava/lang/NoSuchFieldException;", name_str.c_str()); + ThrowNoSuchFieldException(DecodeClass(soa, javaThis), name_str.c_str()); } return nullptr; } diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 5354fd8486..3b0e6c1062 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -302,7 +302,9 @@ uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { } while (length > 0); // Truncate the file to the new length. - ftruncate(fd, full_length); + if (ftruncate(fd, full_length) == -1) { + LOG(ERROR) << "Failed to truncate profile file " << full_name; + } // Now unlock the file, allowing another process in. err = flock(fd, LOCK_UN); diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java index 59f7001a58..0d8e576086 100644 --- a/test/046-reflect/src/Main.java +++ b/test/046-reflect/src/Main.java @@ -233,6 +233,20 @@ public class Main { field.set(instance, null); /* + * Try getDeclaredField on a non-existant field. + */ + try { + field = target.getDeclaredField("nonExistant"); + System.out.println("ERROR: Expected NoSuchFieldException"); + } catch (NoSuchFieldException nsfe) { + String msg = nsfe.getMessage(); + if (!msg.contains("Target;")) { + System.out.println(" NoSuchFieldException '" + msg + + "' didn't contain class"); + } + } + + /* * Do some stuff with long. */ long longVal; @@ -868,4 +882,4 @@ class OtherClass { System.out.println(e); } } -}
\ No newline at end of file +} 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); } } diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index d9da690e28..2ed4aa7bc3 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -141,7 +141,7 @@ def MatchFiles(checkerFile, c1File): c1Pass = c1File.findPass(testCase.name) if c1Pass is None: Logger.fail("Test case \"" + testCase.name + "\" not found in the C1visualizer output", - testCase.fileName, testCase.lineNo) + testCase.fileName, testCase.startLineNo) Logger.startTest(testCase.name) __matchGroups(testCase, c1Pass) Logger.testPassed() diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 1dd443b2bc..77e800411c 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -21,11 +21,10 @@ fi # Jar containing all the tests. test_jar=out/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar -junit_jar=out/host/linux-x86/framework/junit.jar -if [ ! -f $test_jar -o ! -f $junit_jar ]; then +if [ ! -f $test_jar ]; then echo "Before running, you must build jdwp tests and vogar:" \ - "make junit apache-harmony-jdwp-tests-hostdex vogar vogar.jar" + "make apache-harmony-jdwp-tests-hostdex vogar vogar.jar" exit 1 fi @@ -80,7 +79,6 @@ vogar $vm_command \ --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ --vm-arg -Djpda.settings.debuggeeJavaPath="\"$art_debugee $image $debuggee_args\"" \ --classpath $test_jar \ - --classpath $junit_jar \ --vm-arg -Xcompiler-option --vm-arg --compiler-backend=Optimizing \ --vm-arg -Xcompiler-option --vm-arg --debuggable \ org.apache.harmony.jpda.tests.share.AllTests |