diff options
165 files changed, 5599 insertions, 3137 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 a4319c3dc5..6d8a7dab2b 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -20,6 +20,7 @@ #include "base/bit_vector-inl.h" #include "base/macros.h" +#include "base/allocator.h" #include "compiler_enums.h" #include "dataflow_iterator-inl.h" #include "dex_instruction.h" @@ -57,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; @@ -75,6 +74,9 @@ inline void GvnDeadCodeElimination::MIRData::RemovePrevChange(int v_reg, MIRData GvnDeadCodeElimination::VRegChains::VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc) : num_vregs_(num_vregs), vreg_data_(alloc->AllocArray<VRegValue>(num_vregs, kArenaAllocMisc)), + vreg_high_words_(num_vregs, false, Allocator::GetNoopAllocator(), + BitVector::BitsToWords(num_vregs), + alloc->AllocArray<uint32_t>(BitVector::BitsToWords(num_vregs))), mir_data_(alloc->Adapter()) { mir_data_.reserve(100); } @@ -82,6 +84,7 @@ GvnDeadCodeElimination::VRegChains::VRegChains(uint32_t num_vregs, ScopedArenaAl inline void GvnDeadCodeElimination::VRegChains::Reset() { DCHECK(mir_data_.empty()); std::fill_n(vreg_data_, num_vregs_, VRegValue()); + vreg_high_words_.ClearAllBits(); } void GvnDeadCodeElimination::VRegChains::AddMIRWithDef(MIR* mir, int v_reg, bool wide, @@ -93,24 +96,26 @@ void GvnDeadCodeElimination::VRegChains::AddMIRWithDef(MIR* mir, int v_reg, bool data->wide_def = wide; data->vreg_def = v_reg; - if (vreg_data_[v_reg].change != kNPos && - mir_data_[vreg_data_[v_reg].change].vreg_def + 1 == v_reg) { - data->low_def_over_high_word = true; - } - data->prev_value = vreg_data_[v_reg]; DCHECK_LT(static_cast<size_t>(v_reg), num_vregs_); + data->prev_value = vreg_data_[v_reg]; + data->low_def_over_high_word = + (vreg_data_[v_reg].change != kNPos) + ? GetMIRData(vreg_data_[v_reg].change)->vreg_def + 1 == v_reg + : vreg_high_words_.IsBitSet(v_reg); vreg_data_[v_reg].value = new_value; vreg_data_[v_reg].change = pos; + vreg_high_words_.ClearBit(v_reg); if (wide) { - if (vreg_data_[v_reg + 1].change != kNPos && - mir_data_[vreg_data_[v_reg + 1].change].vreg_def == v_reg + 1) { - data->high_def_over_low_word = true; - } - data->prev_value_high = vreg_data_[v_reg + 1]; DCHECK_LT(static_cast<size_t>(v_reg + 1), num_vregs_); + data->prev_value_high = vreg_data_[v_reg + 1]; + data->high_def_over_low_word = + (vreg_data_[v_reg + 1].change != kNPos) + ? GetMIRData(vreg_data_[v_reg + 1].change)->vreg_def == v_reg + 1 + : !vreg_high_words_.IsBitSet(v_reg + 1); vreg_data_[v_reg + 1].value = new_value; vreg_data_[v_reg + 1].change = pos; + vreg_high_words_.SetBit(v_reg + 1); } } @@ -123,9 +128,17 @@ void GvnDeadCodeElimination::VRegChains::RemoveLastMIRData() { if (data->has_def) { DCHECK_EQ(vreg_data_[data->vreg_def].change, NumMIRs() - 1u); vreg_data_[data->vreg_def] = data->prev_value; + DCHECK(!vreg_high_words_.IsBitSet(data->vreg_def)); + if (data->low_def_over_high_word) { + vreg_high_words_.SetBit(data->vreg_def); + } if (data->wide_def) { DCHECK_EQ(vreg_data_[data->vreg_def + 1].change, NumMIRs() - 1u); vreg_data_[data->vreg_def + 1] = data->prev_value_high; + DCHECK(vreg_high_words_.IsBitSet(data->vreg_def + 1)); + if (data->high_def_over_low_word) { + vreg_high_words_.ClearBit(data->vreg_def + 1); + } } } mir_data_.pop_back(); @@ -169,6 +182,7 @@ void GvnDeadCodeElimination::VRegChains::InsertInitialValueHigh(int v_reg, uint1 uint16_t change = vreg_data_[v_reg].change; if (change == kNPos) { vreg_data_[v_reg].value = value; + vreg_high_words_.SetBit(v_reg); } else { while (true) { MIRData* data = &mir_data_[change]; @@ -208,6 +222,7 @@ void GvnDeadCodeElimination::VRegChains::UpdateInitialVRegValue(int v_reg, bool } } vreg_data_[v_reg].value = old_value; + DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. } } else { DCHECK_LT(static_cast<size_t>(v_reg + 1), num_vregs_); @@ -223,6 +238,7 @@ void GvnDeadCodeElimination::VRegChains::UpdateInitialVRegValue(int v_reg, bool old_value = lvn->GetStartingVregValueNumber(v_reg); } vreg_data_[v_reg].value = old_value; + DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. } if (check_high && vreg_data_[v_reg + 1].value == kNoValue) { uint16_t old_value = lvn->GetStartingVregValueNumber(v_reg + 1); @@ -234,6 +250,7 @@ void GvnDeadCodeElimination::VRegChains::UpdateInitialVRegValue(int v_reg, bool } } vreg_data_[v_reg + 1].value = old_value; + DCHECK(!vreg_high_words_.IsBitSet(v_reg + 1)); // Keep marked as low word. } } } @@ -300,6 +317,8 @@ void GvnDeadCodeElimination::VRegChains::ReplaceChange(uint16_t old_change, uint if (next_change == kNPos) { DCHECK_EQ(vreg_data_[v_reg].change, old_change); vreg_data_[v_reg].change = new_change; + DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == old_data->vreg_def + 1); + // No change in vreg_high_words_. } else { DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), old_change); mir_data_[next_change].SetPrevChange(v_reg, new_change); @@ -316,6 +335,12 @@ void GvnDeadCodeElimination::VRegChains::RemoveChange(uint16_t change) { if (next_change == kNPos) { DCHECK_EQ(vreg_data_[v_reg].change, change); vreg_data_[v_reg] = (data->vreg_def == v_reg) ? data->prev_value : data->prev_value_high; + 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) { + vreg_high_words_.ClearBit(v_reg); + } } else { DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), change); mir_data_[next_change].RemovePrevChange(v_reg, data); diff --git a/compiler/dex/gvn_dead_code_elimination.h b/compiler/dex/gvn_dead_code_elimination.h index bc75a01778..06022db501 100644 --- a/compiler/dex/gvn_dead_code_elimination.h +++ b/compiler/dex/gvn_dead_code_elimination.h @@ -121,6 +121,7 @@ class GvnDeadCodeElimination : public DeletableArenaObject<kArenaAllocMisc> { private: const uint32_t num_vregs_; VRegValue* const vreg_data_; + BitVector vreg_high_words_; ScopedArenaVector<MIRData> mir_data_; }; diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index efb32bb39e..de591d0edb 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -1896,4 +1896,91 @@ TEST_F(GvnDeadCodeEliminationTestLoop, IFieldLoopVariable) { EXPECT_EQ(2u, phi->dalvikInsn.vA); } +TEST_F(GvnDeadCodeEliminationTestDiamond, LongOverlaps1) { + static const MIRDef mirs[] = { + DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), + DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 2u, 1000u), + DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 4u, 0u), + DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 2u), + DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 4u), + DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 10u, 6u), + }; + + // The last insn should overlap the first and second. + static const int32_t sreg_to_vreg_map[] = { 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3 }; + PrepareSRegToVRegMap(sreg_to_vreg_map); + + PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 0, 2, 4, 6, 8, 10 }; + MarkAsWideSRegs(wide_sregs); + PerformGVN_DCE(); + + ASSERT_EQ(arraysize(mirs), value_names_.size()); + EXPECT_EQ(value_names_[0], value_names_[1]); + EXPECT_EQ(value_names_[0], value_names_[2]); + EXPECT_EQ(value_names_[0], value_names_[3]); + EXPECT_EQ(value_names_[0], value_names_[4]); + + static const bool eliminated[] = { + false, false, false, false, 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; + } +} + +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/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 6f2cb25911..a06303d23e 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -138,7 +138,8 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset(); // Check handle scope offset is within frame CHECK_LT(handle_scope_offset.Uint32Value(), frame_size); - // TODO: Insert the read barrier for this load. + // Note this LoadRef() already includes the heap poisoning negation. + // Note this LoadRef() does not include read barrier. It will be handled below. __ LoadRef(main_jni_conv->InterproceduralScratchRegister(), mr_conv->MethodRegister(), mirror::ArtMethod::DeclaringClassOffset()); __ VerifyObject(main_jni_conv->InterproceduralScratchRegister(), false); @@ -189,6 +190,49 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, size_t current_out_arg_size = main_out_arg_size; __ IncreaseFrameSize(main_out_arg_size); + // Call the read barrier for the declaring class loaded from the method for a static call. + // Note that we always have outgoing param space available for at least two params. + if (kUseReadBarrier && is_static) { + ThreadOffset<4> read_barrier32 = QUICK_ENTRYPOINT_OFFSET(4, pReadBarrierJni); + ThreadOffset<8> read_barrier64 = QUICK_ENTRYPOINT_OFFSET(8, pReadBarrierJni); + main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size)); + main_jni_conv->Next(); // Skip JNIEnv. + FrameOffset class_handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset(); + main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size)); + // Pass the handle for the class as the first argument. + if (main_jni_conv->IsCurrentParamOnStack()) { + FrameOffset out_off = main_jni_conv->CurrentParamStackOffset(); + __ CreateHandleScopeEntry(out_off, class_handle_scope_offset, + mr_conv->InterproceduralScratchRegister(), + false); + } else { + ManagedRegister out_reg = main_jni_conv->CurrentParamRegister(); + __ CreateHandleScopeEntry(out_reg, class_handle_scope_offset, + ManagedRegister::NoRegister(), false); + } + main_jni_conv->Next(); + // Pass the current thread as the second argument and call. + if (main_jni_conv->IsCurrentParamInRegister()) { + __ GetCurrentThread(main_jni_conv->CurrentParamRegister()); + if (is_64_bit_target) { + __ Call(main_jni_conv->CurrentParamRegister(), Offset(read_barrier64), + main_jni_conv->InterproceduralScratchRegister()); + } else { + __ Call(main_jni_conv->CurrentParamRegister(), Offset(read_barrier32), + main_jni_conv->InterproceduralScratchRegister()); + } + } else { + __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset(), + main_jni_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ CallFromThread64(read_barrier64, main_jni_conv->InterproceduralScratchRegister()); + } else { + __ CallFromThread32(read_barrier32, main_jni_conv->InterproceduralScratchRegister()); + } + } + main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size)); // Reset. + } + // 6. Call into appropriate JniMethodStart passing Thread* so that transition out of Runnable // can occur. The result is the saved JNI local state that is restored by the exit call. We // abuse the JNI calling convention here, that is guaranteed to support passing 2 pointer diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index dbdcc96fc1..a871a82d95 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -176,7 +176,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(28U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(111 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(112 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a902760c09..502ef2f497 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/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index f56e446605..672e55ea0b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -17,6 +17,7 @@ #include "code_generator_arm.h" #include "arch/arm/instruction_set_features_arm.h" +#include "code_generator_utils.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" #include "intrinsics.h" @@ -347,11 +348,11 @@ inline Condition ARMOppositeCondition(IfCondition cond) { } void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { - stream << ArmManagedRegister::FromCoreRegister(Register(reg)); + stream << Register(reg); } void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { - stream << ArmManagedRegister::FromSRegister(SRegister(reg)); + stream << SRegister(reg); } size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { @@ -2185,11 +2186,134 @@ void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { } } +void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = locations->Out().AsRegister<Register>(); + Register dividend = locations->InAt(0).AsRegister<Register>(); + int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + DCHECK(imm == 1 || imm == -1); + + if (instruction->IsRem()) { + __ LoadImmediate(out, 0); + } else { + if (imm == 1) { + __ Mov(out, dividend); + } else { + __ rsb(out, dividend, ShifterOperand(0)); + } + } +} + +void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = locations->Out().AsRegister<Register>(); + Register dividend = locations->InAt(0).AsRegister<Register>(); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t abs_imm = std::abs(imm); + DCHECK(IsPowerOfTwo(abs_imm)); + int ctz_imm = CTZ(abs_imm); + + if (ctz_imm == 1) { + __ Lsr(temp, dividend, 32 - ctz_imm); + } else { + __ Asr(temp, dividend, 31); + __ Lsr(temp, temp, 32 - ctz_imm); + } + __ add(out, temp, ShifterOperand(dividend)); + + if (instruction->IsDiv()) { + __ Asr(out, out, ctz_imm); + if (imm < 0) { + __ rsb(out, out, ShifterOperand(0)); + } + } else { + __ ubfx(out, out, 0, ctz_imm); + __ sub(out, out, ShifterOperand(temp)); + } +} + +void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = locations->Out().AsRegister<Register>(); + Register dividend = locations->InAt(0).AsRegister<Register>(); + Register temp1 = locations->GetTemp(0).AsRegister<Register>(); + Register temp2 = locations->GetTemp(1).AsRegister<Register>(); + int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + + int64_t magic; + int shift; + CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); + + __ LoadImmediate(temp1, magic); + __ smull(temp2, temp1, dividend, temp1); + + if (imm > 0 && magic < 0) { + __ add(temp1, temp1, ShifterOperand(dividend)); + } else if (imm < 0 && magic > 0) { + __ sub(temp1, temp1, ShifterOperand(dividend)); + } + + if (shift != 0) { + __ Asr(temp1, temp1, shift); + } + + if (instruction->IsDiv()) { + __ sub(out, temp1, ShifterOperand(temp1, ASR, 31)); + } else { + __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31)); + // TODO: Strength reduction for mls. + __ LoadImmediate(temp2, imm); + __ mls(out, temp1, temp2, dividend); + } +} + +void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + if (imm == 0) { + // Do not generate anything. DivZeroCheck would prevent any code to be executed. + } else if (imm == 1 || imm == -1) { + DivRemOneOrMinusOne(instruction); + } else if (IsPowerOfTwo(std::abs(imm))) { + DivRemByPowerOfTwo(instruction); + } else { + DCHECK(imm <= -2 || imm >= 2); + GenerateDivRemWithAnyConstant(instruction); + } +} + void LocationsBuilderARM::VisitDiv(HDiv* div) { LocationSummary::CallKind call_kind = LocationSummary::kNoCall; if (div->GetResultType() == Primitive::kPrimLong) { // pLdiv runtime call. call_kind = LocationSummary::kCall; + } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) { + // sdiv will be replaced by other instruction sequence. } else if (div->GetResultType() == Primitive::kPrimInt && !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { // pIdivmod runtime call. @@ -2200,7 +2324,20 @@ void LocationsBuilderARM::VisitDiv(HDiv* div) { switch (div->GetResultType()) { case Primitive::kPrimInt: { - if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (div->InputAt(1)->IsConstant()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue()); + if (abs_imm <= 1) { + // No temp register required. + } else { + locations->AddTemp(Location::RequiresRegister()); + if (!IsPowerOfTwo(abs_imm)) { + locations->AddTemp(Location::RequiresRegister()); + } + } + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); @@ -2244,7 +2381,9 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { switch (div->GetResultType()) { case Primitive::kPrimInt: { - if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (second.IsConstant()) { + GenerateDivRemConstantIntegral(div); + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { __ sdiv(out.AsRegister<Register>(), first.AsRegister<Register>(), second.AsRegister<Register>()); @@ -2296,8 +2435,11 @@ void LocationsBuilderARM::VisitRem(HRem* rem) { // Most remainders are implemented in the runtime. LocationSummary::CallKind call_kind = LocationSummary::kCall; - if (rem->GetResultType() == Primitive::kPrimInt && - codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { + // sdiv will be replaced by other instruction sequence. + call_kind = LocationSummary::kNoCall; + } else if ((rem->GetResultType() == Primitive::kPrimInt) + && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { // Have hardware divide instruction for int, do it with three instructions. call_kind = LocationSummary::kNoCall; } @@ -2306,7 +2448,20 @@ void LocationsBuilderARM::VisitRem(HRem* rem) { switch (type) { case Primitive::kPrimInt: { - if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (rem->InputAt(1)->IsConstant()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue()); + if (abs_imm <= 1) { + // No temp register required. + } else { + locations->AddTemp(Location::RequiresRegister()); + if (!IsPowerOfTwo(abs_imm)) { + locations->AddTemp(Location::RequiresRegister()); + } + } + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); @@ -2363,7 +2518,9 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); switch (type) { case Primitive::kPrimInt: { - if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (second.IsConstant()) { + GenerateDivRemConstantIntegral(rem); + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { Register reg1 = first.AsRegister<Register>(); Register reg2 = second.AsRegister<Register>(); Register temp = locations->GetTemp(0).AsRegister<Register>(); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 1a498e1148..2edbcf8ad7 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -189,6 +189,10 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { Label* true_target, Label* false_target, Label* always_true_target); + void DivRemOneOrMinusOne(HBinaryOperation* instruction); + void DivRemByPowerOfTwo(HBinaryOperation* instruction); + void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction); + void GenerateDivRemConstantIntegral(HBinaryOperation* instruction); ArmAssembler* const assembler_; CodeGeneratorARM* const codegen_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 0222f93da4..f51ea41ddf 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -17,6 +17,7 @@ #include "code_generator_arm64.h" #include "arch/arm64/instruction_set_features_arm64.h" +#include "code_generator_utils.h" #include "common_arm64.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" @@ -691,11 +692,11 @@ size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint } void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const { - stream << Arm64ManagedRegister::FromXRegister(XRegister(reg)); + stream << XRegister(reg); } void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { - stream << Arm64ManagedRegister::FromDRegister(DRegister(reg)); + stream << DRegister(reg); } void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) { @@ -979,14 +980,12 @@ void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset, BlockPoolsScope block_pools(GetVIXLAssembler()); __ Ldr(lr, MemOperand(tr, entry_point_offset)); __ Blr(lr); - if (instruction != nullptr) { - RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || !IsLeafMethod()); - } + RecordPcInfo(instruction, dex_pc, slow_path); + DCHECK(instruction->IsSuspendCheck() + || instruction->IsBoundsCheck() + || instruction->IsNullCheck() + || instruction->IsDivZeroCheck() + || !IsLeafMethod()); } void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, @@ -1605,6 +1604,152 @@ FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) #undef DEFINE_CONDITION_VISITORS #undef FOR_EACH_CONDITION_INSTRUCTION +void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = OutputRegister(instruction); + Register dividend = InputRegisterAt(instruction, 0); + int64_t imm = Int64FromConstant(second.GetConstant()); + DCHECK(imm == 1 || imm == -1); + + if (instruction->IsRem()) { + __ Mov(out, 0); + } else { + if (imm == 1) { + __ Mov(out, dividend); + } else { + __ Neg(out, dividend); + } + } +} + +void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = OutputRegister(instruction); + Register dividend = InputRegisterAt(instruction, 0); + int64_t imm = Int64FromConstant(second.GetConstant()); + int64_t abs_imm = std::abs(imm); + DCHECK(IsPowerOfTwo(abs_imm)); + int ctz_imm = CTZ(abs_imm); + + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); + + if (instruction->IsDiv()) { + __ Add(temp, dividend, abs_imm - 1); + __ Cmp(dividend, 0); + __ Csel(out, temp, dividend, lt); + if (imm > 0) { + __ Asr(out, out, ctz_imm); + } else { + __ Neg(out, Operand(out, ASR, ctz_imm)); + } + } else { + int bits = instruction->GetResultType() == Primitive::kPrimInt ? 32 : 64; + __ Asr(temp, dividend, bits - 1); + __ Lsr(temp, temp, bits - ctz_imm); + __ Add(out, dividend, temp); + __ And(out, out, abs_imm - 1); + __ Sub(out, out, temp); + } +} + +void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + + LocationSummary* locations = instruction->GetLocations(); + Location second = locations->InAt(1); + DCHECK(second.IsConstant()); + + Register out = OutputRegister(instruction); + Register dividend = InputRegisterAt(instruction, 0); + int64_t imm = Int64FromConstant(second.GetConstant()); + + Primitive::Type type = instruction->GetResultType(); + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + int64_t magic; + int shift; + CalculateMagicAndShiftForDivRem(imm, type == Primitive::kPrimLong /* is_long */, &magic, &shift); + + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); + + // temp = get_high(dividend * magic) + __ Mov(temp, magic); + if (type == Primitive::kPrimLong) { + __ Smulh(temp, dividend, temp); + } else { + __ Smull(temp.X(), dividend, temp); + __ Lsr(temp.X(), temp.X(), 32); + } + + if (imm > 0 && magic < 0) { + __ Add(temp, temp, dividend); + } else if (imm < 0 && magic > 0) { + __ Sub(temp, temp, dividend); + } + + if (shift != 0) { + __ Asr(temp, temp, shift); + } + + if (instruction->IsDiv()) { + __ Sub(out, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31)); + } else { + __ Sub(temp, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31)); + // TODO: Strength reduction for msub. + Register temp_imm = temps.AcquireSameSizeAs(out); + __ Mov(temp_imm, imm); + __ Msub(out, temp, temp_imm, dividend); + } +} + +void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + Primitive::Type type = instruction->GetResultType(); + DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong); + + LocationSummary* locations = instruction->GetLocations(); + Register out = OutputRegister(instruction); + Location second = locations->InAt(1); + + if (second.IsConstant()) { + int64_t imm = Int64FromConstant(second.GetConstant()); + + if (imm == 0) { + // Do not generate anything. DivZeroCheck would prevent any code to be executed. + } else if (imm == 1 || imm == -1) { + DivRemOneOrMinusOne(instruction); + } else if (IsPowerOfTwo(std::abs(imm))) { + DivRemByPowerOfTwo(instruction); + } else { + DCHECK(imm <= -2 || imm >= 2); + GenerateDivRemWithAnyConstant(instruction); + } + } else { + Register dividend = InputRegisterAt(instruction, 0); + Register divisor = InputRegisterAt(instruction, 1); + if (instruction->IsDiv()) { + __ Sdiv(out, dividend, divisor); + } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); + __ Sdiv(temp, dividend, divisor); + __ Msub(out, temp, divisor, dividend); + } + } +} + void LocationsBuilderARM64::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); @@ -1612,7 +1757,7 @@ void LocationsBuilderARM64::VisitDiv(HDiv* div) { case Primitive::kPrimInt: case Primitive::kPrimLong: locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1))); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; @@ -1633,7 +1778,7 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { switch (type) { case Primitive::kPrimInt: case Primitive::kPrimLong: - __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1)); + GenerateDivRemIntegral(div); break; case Primitive::kPrimFloat: @@ -2456,7 +2601,7 @@ void LocationsBuilderARM64::VisitRem(HRem* rem) { case Primitive::kPrimInt: case Primitive::kPrimLong: locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1))); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; @@ -2481,14 +2626,7 @@ void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { switch (type) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - UseScratchRegisterScope temps(GetVIXLAssembler()); - Register dividend = InputRegisterAt(rem, 0); - Register divisor = InputRegisterAt(rem, 1); - Register output = OutputRegister(rem); - Register temp = temps.AcquireSameSizeAs(output); - - __ Sdiv(temp, dividend, divisor); - __ Msub(output, temp, divisor, dividend); + GenerateDivRemIntegral(rem); break; } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 8aeea5400f..0dc0918c5e 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -163,6 +163,11 @@ class InstructionCodeGeneratorARM64 : public HGraphVisitor { vixl::Label* true_target, vixl::Label* false_target, vixl::Label* always_true_target); + void DivRemOneOrMinusOne(HBinaryOperation* instruction); + void DivRemByPowerOfTwo(HBinaryOperation* instruction); + void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction); + void GenerateDivRemIntegral(HBinaryOperation* instruction); + Arm64Assembler* const assembler_; CodeGeneratorARM64* const codegen_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2848a48a64..0212da106b 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -340,11 +340,11 @@ inline Condition X86Condition(IfCondition cond) { } void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const { - stream << X86ManagedRegister::FromCpuRegister(Register(reg)); + stream << Register(reg); } void CodeGeneratorX86::DumpFloatingPointRegister(std::ostream& stream, int reg) const { - stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg)); + stream << XmmRegister(reg); } size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e633970279..63d68465d0 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -396,11 +396,11 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo } void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const { - stream << X86_64ManagedRegister::FromCpuRegister(Register(reg)); + stream << Register(reg); } void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { - stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg)); + stream << FloatRegister(reg); } size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index b7a92b5ae5..20ce1105ce 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -28,6 +28,7 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor { void VisitShift(HBinaryOperation* shift); void VisitAnd(HAnd* instruction) OVERRIDE; + void VisitCompare(HCompare* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; void VisitOr(HOr* instruction) OVERRIDE; void VisitRem(HRem* instruction) OVERRIDE; @@ -70,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(); @@ -108,6 +117,26 @@ void InstructionWithAbsorbingInputSimplifier::VisitAnd(HAnd* instruction) { } } +void InstructionWithAbsorbingInputSimplifier::VisitCompare(HCompare* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + if (input_cst != nullptr) { + HInstruction* input_value = instruction->GetLeastConstantLeft(); + if (Primitive::IsFloatingPointType(input_value->GetType()) && + ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->IsNaN()) || + (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->IsNaN()))) { + // Replace code looking like + // CMP{G,L} dst, src, NaN + // with + // CONSTANT +1 (gt bias) + // or + // CONSTANT -1 (lt bias) + instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimInt, + (instruction->IsGtBias() ? 1 : -1))); + instruction->GetBlock()->RemoveInstruction(instruction); + } + } +} + void InstructionWithAbsorbingInputSimplifier::VisitMul(HMul* instruction) { HConstant* input_cst = instruction->GetConstantRight(); Primitive::Type type = instruction->GetType(); diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index cd427c5ed8..b31de98e25 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -17,6 +17,7 @@ #include "dead_code_elimination.h" #include "base/bit_vector-inl.h" +#include "ssa_phi_elimination.h" namespace art { @@ -47,6 +48,12 @@ static void MarkReachableBlocks(HBasicBlock* block, ArenaBitVector* visited) { } } +static void MarkLoopHeadersContaining(const HBasicBlock& block, ArenaBitVector* set) { + for (HLoopInformationOutwardIterator it(block); !it.Done(); it.Advance()) { + set->SetBit(it.Current()->GetHeader()->GetBlockId()); + } +} + void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) { if (stats_ != nullptr) { stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction, @@ -58,18 +65,24 @@ void HDeadCodeElimination::RemoveDeadBlocks() { // Classify blocks as reachable/unreachable. ArenaAllocator* allocator = graph_->GetArena(); ArenaBitVector live_blocks(allocator, graph_->GetBlocks().Size(), false); + ArenaBitVector affected_loops(allocator, graph_->GetBlocks().Size(), false); + MarkReachableBlocks(graph_->GetEntryBlock(), &live_blocks); - // Remove all dead blocks. Process blocks in post-order, because removal needs - // the block's chain of dominators. + // Remove all dead blocks. Iterate in post order because removal needs the + // block's chain of dominators and nested loops need to be updated from the + // inside out. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - if (live_blocks.IsBitSet(block->GetBlockId())) { - // If this block is part of a loop that is being dismantled, we need to - // update its loop information. - block->UpdateLoopInformation(); + int id = block->GetBlockId(); + if (live_blocks.IsBitSet(id)) { + if (affected_loops.IsBitSet(id)) { + DCHECK(block->IsLoopHeader()); + block->GetLoopInformation()->Update(); + } } else { MaybeRecordDeadBlock(block); + MarkLoopHeadersContaining(*block, &affected_loops); block->DisconnectAndDelete(); } } @@ -120,6 +133,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() { void HDeadCodeElimination::Run() { RemoveDeadBlocks(); + SsaRedundantPhiElimination(graph_).Run(); RemoveDeadInstructions(); } diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 0bea0fc1c2..59a57c4345 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -31,13 +31,13 @@ class HDeadCodeElimination : public HOptimization { public: HDeadCodeElimination(HGraph* graph, OptimizingCompilerStats* stats = nullptr, - const char* name = kDeadCodeEliminationPassName) + const char* name = kInitialDeadCodeEliminationPassName) : HOptimization(graph, true, name, stats) {} void Run() OVERRIDE; - static constexpr const char* kDeadCodeEliminationPassName = - "dead_code_elimination"; + static constexpr const char* kInitialDeadCodeEliminationPassName = "dead_code_elimination"; + static constexpr const char* kFinalDeadCodeEliminationPassName = "dead_code_elimination_final"; private: void MaybeRecordDeadBlock(HBasicBlock* block); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 7130127136..7ea1240c5e 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -17,14 +17,63 @@ #include "graph_visualizer.h" #include "code_generator.h" +#include "dead_code_elimination.h" #include "licm.h" #include "nodes.h" #include "optimization.h" #include "register_allocator.h" #include "ssa_liveness_analysis.h" +#include <cctype> +#include <sstream> + namespace art { +static bool HasWhitespace(const char* str) { + DCHECK(str != nullptr); + while (str[0] != 0) { + if (isspace(str[0])) { + return true; + } + str++; + } + return false; +} + +class StringList { + public: + // Create an empty list + StringList() : is_empty_(true) {} + + // Construct StringList from a linked list. List element class T + // must provide methods `GetNext` and `Dump`. + template<class T> + explicit StringList(T* first_entry) : StringList() { + for (T* current = first_entry; current != nullptr; current = current->GetNext()) { + current->Dump(NewEntryStream()); + } + } + + std::ostream& NewEntryStream() { + if (is_empty_) { + is_empty_ = false; + } else { + sstream_ << ","; + } + return sstream_; + } + + private: + bool is_empty_; + std::ostringstream sstream_; + + friend std::ostream& operator<<(std::ostream& os, const StringList& list); +}; + +std::ostream& operator<<(std::ostream& os, const StringList& list) { + return os << "[" << list.sstream_.str() << "]"; +} + /** * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. */ @@ -124,76 +173,84 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_<< std::endl; } - void DumpLocation(Location location) { + void DumpLocation(std::ostream& stream, const Location& location) { if (location.IsRegister()) { - codegen_.DumpCoreRegister(output_, location.reg()); + codegen_.DumpCoreRegister(stream, location.reg()); } else if (location.IsFpuRegister()) { - codegen_.DumpFloatingPointRegister(output_, location.reg()); + codegen_.DumpFloatingPointRegister(stream, location.reg()); } else if (location.IsConstant()) { - output_ << "constant"; + stream << "#"; HConstant* constant = location.GetConstant(); if (constant->IsIntConstant()) { - output_ << " " << constant->AsIntConstant()->GetValue(); + stream << constant->AsIntConstant()->GetValue(); } else if (constant->IsLongConstant()) { - output_ << " " << constant->AsLongConstant()->GetValue(); + stream << constant->AsLongConstant()->GetValue(); } } else if (location.IsInvalid()) { - output_ << "invalid"; + stream << "invalid"; } else if (location.IsStackSlot()) { - output_ << location.GetStackIndex() << "(sp)"; + stream << location.GetStackIndex() << "(sp)"; } else if (location.IsFpuRegisterPair()) { - codegen_.DumpFloatingPointRegister(output_, location.low()); - output_ << " and "; - codegen_.DumpFloatingPointRegister(output_, location.high()); + codegen_.DumpFloatingPointRegister(stream, location.low()); + stream << "|"; + codegen_.DumpFloatingPointRegister(stream, location.high()); } else if (location.IsRegisterPair()) { - codegen_.DumpCoreRegister(output_, location.low()); - output_ << " and "; - codegen_.DumpCoreRegister(output_, location.high()); + codegen_.DumpCoreRegister(stream, location.low()); + stream << "|"; + codegen_.DumpCoreRegister(stream, location.high()); } else if (location.IsUnallocated()) { - output_ << "<U>"; + stream << "unallocated"; } else { DCHECK(location.IsDoubleStackSlot()); - output_ << "2x" << location.GetStackIndex() << "(sp)"; + stream << "2x" << location.GetStackIndex() << "(sp)"; + } + } + + std::ostream& StartAttributeStream(const char* name = nullptr) { + if (name == nullptr) { + output_ << " "; + } else { + DCHECK(!HasWhitespace(name)) << "Checker does not allow spaces in attributes"; + output_ << " " << name << ":"; } + return output_; } void VisitParallelMove(HParallelMove* instruction) OVERRIDE { - output_ << " ("; + StartAttributeStream("liveness") << instruction->GetLifetimePosition(); + StringList moves; for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) { MoveOperands* move = instruction->MoveOperandsAt(i); - DumpLocation(move->GetSource()); - output_ << " -> "; - DumpLocation(move->GetDestination()); - if (i + 1 != e) { - output_ << ", "; - } + std::ostream& str = moves.NewEntryStream(); + DumpLocation(str, move->GetSource()); + str << "->"; + DumpLocation(str, move->GetDestination()); } - output_ << ")"; - output_ << " (liveness: " << instruction->GetLifetimePosition() << ")"; + StartAttributeStream("moves") << moves; } void VisitIntConstant(HIntConstant* instruction) OVERRIDE { - output_ << " " << instruction->GetValue(); + StartAttributeStream() << instruction->GetValue(); } void VisitLongConstant(HLongConstant* instruction) OVERRIDE { - output_ << " " << instruction->GetValue(); + StartAttributeStream() << instruction->GetValue(); } void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE { - output_ << " " << instruction->GetValue(); + StartAttributeStream() << instruction->GetValue(); } void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE { - output_ << " " << instruction->GetValue(); + StartAttributeStream() << instruction->GetValue(); } void VisitPhi(HPhi* phi) OVERRIDE { - output_ << " " << phi->GetRegNumber(); + StartAttributeStream("reg") << phi->GetRegNumber(); } void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE { - output_ << " " << barrier->GetBarrierKind(); + StartAttributeStream("kind") << barrier->GetBarrierKind(); } bool IsPass(const char* name) { @@ -202,64 +259,65 @@ class HGraphVisualizerPrinter : public HGraphVisitor { void PrintInstruction(HInstruction* instruction) { output_ << instruction->DebugName(); - instruction->Accept(this); if (instruction->InputCount() > 0) { - output_ << " [ "; - for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) { - output_ << GetTypeId(inputs.Current()->GetType()) << inputs.Current()->GetId() << " "; + StringList inputs; + for (HInputIterator it(instruction); !it.Done(); it.Advance()) { + inputs.NewEntryStream() << GetTypeId(it.Current()->GetType()) << it.Current()->GetId(); } - output_ << "]"; + StartAttributeStream() << inputs; } + instruction->Accept(this); if (instruction->HasEnvironment()) { - output_ << " (env:"; + StringList envs; for (HEnvironment* environment = instruction->GetEnvironment(); environment != nullptr; environment = environment->GetParent()) { - output_ << " [ "; + StringList vregs; for (size_t i = 0, e = environment->Size(); i < e; ++i) { HInstruction* insn = environment->GetInstructionAt(i); if (insn != nullptr) { - output_ << GetTypeId(insn->GetType()) << insn->GetId() << " "; + vregs.NewEntryStream() << GetTypeId(insn->GetType()) << insn->GetId(); } else { - output_ << " _ "; + vregs.NewEntryStream() << "_"; } } - output_ << "]"; + envs.NewEntryStream() << vregs; } - output_ << ")"; + StartAttributeStream("env") << envs; } if (IsPass(SsaLivenessAnalysis::kLivenessPassName) && is_after_pass_ && instruction->GetLifetimePosition() != kNoLifetime) { - output_ << " (liveness: " << instruction->GetLifetimePosition(); + StartAttributeStream("liveness") << instruction->GetLifetimePosition(); if (instruction->HasLiveInterval()) { - output_ << " "; - const LiveInterval& interval = *instruction->GetLiveInterval(); - interval.Dump(output_); + LiveInterval* interval = instruction->GetLiveInterval(); + StartAttributeStream("ranges") << StringList(interval->GetFirstRange()); + StartAttributeStream("uses") << StringList(interval->GetFirstUse()); + StartAttributeStream("env_uses") << StringList(interval->GetFirstEnvironmentUse()); + StartAttributeStream("is_fixed") << interval->IsFixed(); + StartAttributeStream("is_split") << interval->IsSplit(); + StartAttributeStream("is_low") << interval->IsLowInterval(); + StartAttributeStream("is_high") << interval->IsHighInterval(); } - output_ << ")"; } else if (IsPass(RegisterAllocator::kRegisterAllocatorPassName) && is_after_pass_) { + StartAttributeStream("liveness") << instruction->GetLifetimePosition(); LocationSummary* locations = instruction->GetLocations(); if (locations != nullptr) { - output_ << " ( "; + StringList inputs; for (size_t i = 0; i < instruction->InputCount(); ++i) { - DumpLocation(locations->InAt(i)); - output_ << " "; - } - output_ << ")"; - if (locations->Out().IsValid()) { - output_ << " -> "; - DumpLocation(locations->Out()); + DumpLocation(inputs.NewEntryStream(), locations->InAt(i)); } + std::ostream& attr = StartAttributeStream("locations"); + attr << inputs << "->"; + DumpLocation(attr, locations->Out()); } - output_ << " (liveness: " << instruction->GetLifetimePosition() << ")"; - } else if (IsPass(LICM::kLoopInvariantCodeMotionPassName)) { - output_ << " ( loop_header:"; + } else if (IsPass(LICM::kLoopInvariantCodeMotionPassName) + || IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)) { HLoopInformation* info = instruction->GetBlock()->GetLoopInformation(); if (info == nullptr) { - output_ << "null )"; + StartAttributeStream("loop") << "none"; } else { - output_ << "B" << info->GetHeader()->GetBlockId() << " )"; + StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId(); } } } @@ -279,7 +337,7 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_ << bci << " " << num_uses << " " << GetTypeId(instruction->GetType()) << instruction->GetId() << " "; PrintInstruction(instruction); - output_ << kEndInstructionMarker << std::endl; + output_ << " " << kEndInstructionMarker << std::endl; } } diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 7f7b450003..dccfe9a0ca 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -850,6 +850,94 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +static void GenerateVisitStringIndexOf(HInvoke* invoke, + ArmAssembler* assembler, + CodeGeneratorARM* codegen, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = invoke->GetLocations(); + Register tmp_reg = locations->GetTemp(0).AsRegister<Register>(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, + // or directly dispatch if we have a constant. + SlowPathCodeARM* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + std::numeric_limits<uint16_t>::max()) { + // Always needs the slow-path. We could directly dispatch to it, but this case should be + // rare, so for simplicity just put the full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathARM(invoke); + codegen->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + Register char_reg = locations->InAt(1).AsRegister<Register>(); + __ LoadImmediate(tmp_reg, std::numeric_limits<uint16_t>::max()); + __ cmp(char_reg, ShifterOperand(tmp_reg)); + slow_path = new (allocator) IntrinsicSlowPathARM(invoke); + codegen->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), HI); + } + + if (start_at_zero) { + DCHECK_EQ(tmp_reg, R2); + // Start-index = 0. + __ LoadImmediate(tmp_reg, 0); + } + + __ LoadFromOffset(kLoadWord, LR, TR, + QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value()); + __ blx(LR); + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's + // best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(R0)); + + // Need a temp for slow-path codepoint compare, and need to send start-index=0. + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) { + GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's + // best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(R0)); + + // Need a temp for slow-path codepoint compare. + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} + void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -951,8 +1039,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index ca3de99092..2c4fab0465 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -993,6 +993,91 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +static void GenerateVisitStringIndexOf(HInvoke* invoke, + vixl::MacroAssembler* masm, + CodeGeneratorARM64* codegen, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = invoke->GetLocations(); + Register tmp_reg = WRegisterFrom(locations->GetTemp(0)); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, + // or directly dispatch if we have a constant. + SlowPathCodeARM64* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) { + // Always needs the slow-path. We could directly dispatch to it, but this case should be + // rare, so for simplicity just put the full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathARM64(invoke); + codegen->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + Register char_reg = WRegisterFrom(locations->InAt(1)); + __ Mov(tmp_reg, 0xFFFF); + __ Cmp(char_reg, Operand(tmp_reg)); + slow_path = new (allocator) IntrinsicSlowPathARM64(invoke); + codegen->AddSlowPath(slow_path); + __ B(hi, slow_path->GetEntryLabel()); + } + + if (start_at_zero) { + // Start-index = 0. + __ Mov(tmp_reg, 0); + } + + __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value())); + __ Blr(lr); + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's + // best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); + + // Need a temp for slow-path codepoint compare, and need to send start_index=0. + locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2))); +} + +void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) { + GenerateVisitStringIndexOf(invoke, GetVIXLAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's + // best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); + + // Need a temp for slow-path codepoint compare. + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateVisitStringIndexOf(invoke, GetVIXLAssembler(), codegen_, GetAllocator(), false); +} + void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1080,8 +1165,6 @@ void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED } UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 1eef1eff0b..28b7a07cf9 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -16,6 +16,8 @@ #include "intrinsics_x86.h" +#include <limits> + #include "arch/x86/instruction_set_features_x86.h" #include "code_generator_x86.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -124,11 +126,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorX86* codegen) { // restored! class IntrinsicSlowPathX86 : public SlowPathCodeX86 { public: - explicit IntrinsicSlowPathX86(HInvoke* invoke, Register temp) - : invoke_(invoke) { - // The temporary register has to be EAX for x86 invokes. - DCHECK_EQ(temp, EAX); - } + explicit IntrinsicSlowPathX86(HInvoke* invoke) + : invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorX86* codegen = down_cast<CodeGeneratorX86*>(codegen_in); @@ -880,8 +879,6 @@ void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); - // Needs to be EAX for the invoke. - locations->AddTemp(Location::RegisterLocation(EAX)); } void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { @@ -901,8 +898,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { // TODO: For simplicity, the index parameter is requested in a register, so different from Quick // we will not optimize the code for constants (which would save a register). - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( - invoke, locations->GetTemp(0).AsRegister<Register>()); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); codegen_->AddSlowPath(slow_path); X86Assembler* assembler = GetAssembler(); @@ -926,8 +922,6 @@ void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) { locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetOut(Location::RegisterLocation(EAX)); - // Needs to be EAX for the invoke. - locations->AddTemp(Location::RegisterLocation(EAX)); } void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { @@ -939,8 +933,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { Register argument = locations->InAt(1).AsRegister<Register>(); __ testl(argument, argument); - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( - invoke, locations->GetTemp(0).AsRegister<Register>()); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); codegen_->AddSlowPath(slow_path); __ j(kEqual, slow_path->GetEntryLabel()); @@ -948,6 +941,158 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +static void CreateStringIndexOfLocations(HInvoke* invoke, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = new (allocator) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + // The data needs to be in EDI for scasw. So request that the string is there, anyways. + locations->SetInAt(0, Location::RegisterLocation(EDI)); + // If we look for a constant char, we'll still have to copy it into EAX. So just request the + // allocator to do that, anyways. We can still do the constant check by checking the parameter + // of the instruction explicitly. + // Note: This works as we don't clobber EAX anywhere. + locations->SetInAt(1, Location::RegisterLocation(EAX)); + if (!start_at_zero) { + locations->SetInAt(2, Location::RequiresRegister()); // The starting index. + } + // As we clobber EDI during execution anyways, also use it as the output. + locations->SetOut(Location::SameAsFirstInput()); + + // repne scasw uses ECX as the counter. + locations->AddTemp(Location::RegisterLocation(ECX)); + // Need another temporary to be able to compute the result. + locations->AddTemp(Location::RequiresRegister()); +} + +static void GenerateStringIndexOf(HInvoke* invoke, + X86Assembler* assembler, + CodeGeneratorX86* codegen, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + Register string_obj = locations->InAt(0).AsRegister<Register>(); + Register search_value = locations->InAt(1).AsRegister<Register>(); + Register counter = locations->GetTemp(0).AsRegister<Register>(); + Register string_length = locations->GetTemp(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + // Check our assumptions for registers. + DCHECK_EQ(string_obj, EDI); + DCHECK_EQ(search_value, EAX); + DCHECK_EQ(counter, ECX); + DCHECK_EQ(out, EDI); + + // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, + // or directly dispatch if we have a constant. + SlowPathCodeX86* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + std::numeric_limits<uint16_t>::max()) { + // Always needs the slow-path. We could directly dispatch to it, but this case should be + // rare, so for simplicity just put the full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathX86(invoke); + codegen->AddSlowPath(slow_path); + __ jmp(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max())); + slow_path = new (allocator) IntrinsicSlowPathX86(invoke); + codegen->AddSlowPath(slow_path); + __ j(kAbove, slow_path->GetEntryLabel()); + } + + // From here down, we know that we are looking for a char that fits in 16 bits. + // Location of reference to data array within the String object. + int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + // Location of count within the String object. + int32_t count_offset = mirror::String::CountOffset().Int32Value(); + + // Load string length, i.e., the count field of the string. + __ movl(string_length, Address(string_obj, count_offset)); + + // Do a zero-length check. + // TODO: Support jecxz. + Label not_found_label; + __ testl(string_length, string_length); + __ j(kEqual, ¬_found_label); + + if (start_at_zero) { + // Number of chars to scan is the same as the string length. + __ movl(counter, string_length); + + // Move to the start of the string. + __ addl(string_obj, Immediate(value_offset)); + } else { + Register start_index = locations->InAt(2).AsRegister<Register>(); + + // Do a start_index check. + __ cmpl(start_index, string_length); + __ j(kGreaterEqual, ¬_found_label); + + // Ensure we have a start index >= 0; + __ xorl(counter, counter); + __ cmpl(start_index, Immediate(0)); + __ cmovl(kGreater, counter, start_index); + + // Move to the start of the string: string_obj + value_offset + 2 * start_index. + __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); + + // Now update ecx (the repne scasw work counter). We have string.length - start_index left to + // compare. + __ negl(counter); + __ leal(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0)); + } + + // Everything is set up for repne scasw: + // * Comparison address in EDI. + // * Counter in ECX. + __ repne_scasw(); + + // Did we find a match? + __ j(kNotEqual, ¬_found_label); + + // Yes, we matched. Compute the index of the result. + __ subl(string_length, counter); + __ leal(out, Address(string_length, -1)); + + Label done; + __ jmp(&done); + + // Failed to match; return -1. + __ Bind(¬_found_label); + __ movl(out, Immediate(-1)); + + // And join up at the end. + __ Bind(&done); + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +void IntrinsicLocationsBuilderX86::VisitStringIndexOf(HInvoke* invoke) { + CreateStringIndexOfLocations(invoke, arena_, true); +} + +void IntrinsicCodeGeneratorX86::VisitStringIndexOf(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderX86::VisitStringIndexOfAfter(HInvoke* invoke) { + CreateStringIndexOfLocations(invoke, arena_, false); +} + +void IntrinsicCodeGeneratorX86::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} + void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -958,8 +1103,6 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); locations->SetOut(Location::RegisterLocation(EAX)); - // Needs to be EAX for the invoke. - locations->AddTemp(Location::RegisterLocation(EAX)); } void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { @@ -968,8 +1111,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { Register byte_array = locations->InAt(0).AsRegister<Register>(); __ testl(byte_array, byte_array); - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( - invoke, locations->GetTemp(0).AsRegister<Register>()); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); codegen_->AddSlowPath(slow_path); __ j(kEqual, slow_path->GetEntryLabel()); @@ -1003,8 +1145,6 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invok InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(Location::RegisterLocation(EAX)); - // Needs to be EAX for the invoke. - locations->AddTemp(Location::RegisterLocation(EAX)); } void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) { @@ -1013,8 +1153,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) Register string_to_copy = locations->InAt(0).AsRegister<Register>(); __ testl(string_to_copy, string_to_copy); - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( - invoke, locations->GetTemp(0).AsRegister<Register>()); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); codegen_->AddSlowPath(slow_path); __ j(kEqual, slow_path->GetEntryLabel()); @@ -1584,8 +1723,6 @@ void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 3dbcd4c548..0efa714a23 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -16,6 +16,8 @@ #include "intrinsics_x86_64.h" +#include <limits> + #include "arch/x86_64/instruction_set_features_x86_64.h" #include "code_generator_x86_64.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -858,6 +860,157 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +static void CreateStringIndexOfLocations(HInvoke* invoke, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = new (allocator) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + // The data needs to be in RDI for scasw. So request that the string is there, anyways. + locations->SetInAt(0, Location::RegisterLocation(RDI)); + // If we look for a constant char, we'll still have to copy it into RAX. So just request the + // allocator to do that, anyways. We can still do the constant check by checking the parameter + // of the instruction explicitly. + // Note: This works as we don't clobber RAX anywhere. + locations->SetInAt(1, Location::RegisterLocation(RAX)); + if (!start_at_zero) { + locations->SetInAt(2, Location::RequiresRegister()); // The starting index. + } + // As we clobber RDI during execution anyways, also use it as the output. + locations->SetOut(Location::SameAsFirstInput()); + + // repne scasw uses RCX as the counter. + locations->AddTemp(Location::RegisterLocation(RCX)); + // Need another temporary to be able to compute the result. + locations->AddTemp(Location::RequiresRegister()); +} + +static void GenerateStringIndexOf(HInvoke* invoke, + X86_64Assembler* assembler, + CodeGeneratorX86_64* codegen, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + CpuRegister string_obj = locations->InAt(0).AsRegister<CpuRegister>(); + CpuRegister search_value = locations->InAt(1).AsRegister<CpuRegister>(); + CpuRegister counter = locations->GetTemp(0).AsRegister<CpuRegister>(); + CpuRegister string_length = locations->GetTemp(1).AsRegister<CpuRegister>(); + CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + + // Check our assumptions for registers. + DCHECK_EQ(string_obj.AsRegister(), RDI); + DCHECK_EQ(search_value.AsRegister(), RAX); + DCHECK_EQ(counter.AsRegister(), RCX); + DCHECK_EQ(out.AsRegister(), RDI); + + // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, + // or directly dispatch if we have a constant. + SlowPathCodeX86_64* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + std::numeric_limits<uint16_t>::max()) { + // Always needs the slow-path. We could directly dispatch to it, but this case should be + // rare, so for simplicity just put the full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathX86_64(invoke); + codegen->AddSlowPath(slow_path); + __ jmp(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max())); + slow_path = new (allocator) IntrinsicSlowPathX86_64(invoke); + codegen->AddSlowPath(slow_path); + __ j(kAbove, slow_path->GetEntryLabel()); + } + + // From here down, we know that we are looking for a char that fits in 16 bits. + // Location of reference to data array within the String object. + int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + // Location of count within the String object. + int32_t count_offset = mirror::String::CountOffset().Int32Value(); + + // Load string length, i.e., the count field of the string. + __ movl(string_length, Address(string_obj, count_offset)); + + // Do a length check. + // TODO: Support jecxz. + Label not_found_label; + __ testl(string_length, string_length); + __ j(kEqual, ¬_found_label); + + if (start_at_zero) { + // Number of chars to scan is the same as the string length. + __ movl(counter, string_length); + + // Move to the start of the string. + __ addq(string_obj, Immediate(value_offset)); + } else { + CpuRegister start_index = locations->InAt(2).AsRegister<CpuRegister>(); + + // Do a start_index check. + __ cmpl(start_index, string_length); + __ j(kGreaterEqual, ¬_found_label); + + // Ensure we have a start index >= 0; + __ xorl(counter, counter); + __ cmpl(start_index, Immediate(0)); + __ cmov(kGreater, counter, start_index, false); // 32-bit copy is enough. + + // Move to the start of the string: string_obj + value_offset + 2 * start_index. + __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); + + // Now update ecx, the work counter: it's gonna be string.length - start_index. + __ negq(counter); // Needs to be 64-bit negation, as the address computation is 64-bit. + __ leaq(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0)); + } + + // Everything is set up for repne scasw: + // * Comparison address in RDI. + // * Counter in ECX. + __ repne_scasw(); + + // Did we find a match? + __ j(kNotEqual, ¬_found_label); + + // Yes, we matched. Compute the index of the result. + __ subl(string_length, counter); + __ leal(out, Address(string_length, -1)); + + Label done; + __ jmp(&done); + + // Failed to match; return -1. + __ Bind(¬_found_label); + __ movl(out, Immediate(-1)); + + // And join up at the end. + __ Bind(&done); + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +void IntrinsicLocationsBuilderX86_64::VisitStringIndexOf(HInvoke* invoke) { + CreateStringIndexOfLocations(invoke, arena_, true); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringIndexOf(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringIndexOfAfter(HInvoke* invoke) { + CreateStringIndexOfLocations(invoke, arena_, false); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} + void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1434,8 +1587,6 @@ void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE } UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index b9e58c7032..47da9cc17c 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -16,7 +16,9 @@ #include "nodes.h" +#include "code_generator.h" #include "ssa_builder.h" +#include "base/bit_vector-inl.h" #include "utils/growable_array.h" #include "scoped_thread_state_change.h" @@ -346,6 +348,7 @@ void HLoopInformation::PopulateRecursive(HBasicBlock* block) { } bool HLoopInformation::Populate() { + DCHECK_EQ(blocks_.NumSetBits(), 0u) << "Loop information has already been populated"; for (size_t i = 0, e = GetBackEdges().Size(); i < e; ++i) { HBasicBlock* back_edge = GetBackEdges().Get(i); DCHECK(back_edge->GetDominator() != nullptr); @@ -365,6 +368,39 @@ bool HLoopInformation::Populate() { return true; } +void HLoopInformation::Update() { + HGraph* graph = header_->GetGraph(); + for (uint32_t id : blocks_.Indexes()) { + HBasicBlock* block = graph->GetBlocks().Get(id); + // Reset loop information of non-header blocks inside the loop, except + // members of inner nested loops because those should already have been + // updated by their own LoopInformation. + if (block->GetLoopInformation() == this && block != header_) { + block->SetLoopInformation(nullptr); + } + } + blocks_.ClearAllBits(); + + if (back_edges_.IsEmpty()) { + // The loop has been dismantled, delete its suspend check and remove info + // from the header. + DCHECK(HasSuspendCheck()); + header_->RemoveInstruction(suspend_check_); + header_->SetLoopInformation(nullptr); + header_ = nullptr; + suspend_check_ = nullptr; + } else { + if (kIsDebugBuild) { + for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) { + DCHECK(header_->Dominates(back_edges_.Get(i))); + } + } + // This loop still has reachable back edges. Repopulate the list of blocks. + bool populate_successful = Populate(); + DCHECK(populate_successful); + } +} + HBasicBlock* HLoopInformation::GetPreHeader() const { return header_->GetDominator(); } @@ -759,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()); @@ -1049,20 +1163,6 @@ void HBasicBlock::DisconnectAndDelete() { SetGraph(nullptr); } -void HBasicBlock::UpdateLoopInformation() { - // Check if loop information points to a dismantled loop. If so, replace with - // the loop information of a larger loop which contains this block, or nullptr - // otherwise. We iterate in case the larger loop has been destroyed too. - while (IsInLoop() && loop_information_->GetBackEdges().IsEmpty()) { - if (IsLoopHeader()) { - HSuspendCheck* suspend_check = loop_information_->GetSuspendCheck(); - DCHECK_EQ(suspend_check->GetBlock(), this); - RemoveInstruction(suspend_check); - } - loop_information_ = loop_information_->GetPreHeader()->GetLoopInformation(); - } -} - void HBasicBlock::MergeWith(HBasicBlock* other) { DCHECK_EQ(GetGraph(), other->GetGraph()); DCHECK(GetDominatedBlocks().Contains(other)); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 031761e7f7..cb2e5ccab4 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -436,6 +436,12 @@ class HLoopInformation : public ArenaObject<kArenaAllocMisc> { // that is the header dominates the back edge. bool Populate(); + // Reanalyzes the loop by removing loop info from its blocks and re-running + // Populate(). If there are no back edges left, the loop info is completely + // removed as well as its SuspendCheck instruction. It must be run on nested + // inner loops first. + void Update(); + // Returns whether this loop information contains `block`. // Note that this loop information *must* be populated before entering this function. bool Contains(const HBasicBlock& block) const; @@ -705,14 +711,9 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { loop_information_ = info; } - // Checks if the loop information points to a valid loop. If the loop has been - // dismantled (does not have a back edge any more), loop information is - // removed or replaced with the information of the first valid outer loop. - void UpdateLoopInformation(); - bool IsInLoop() const { return loop_information_ != nullptr; } - // Returns wheter this block dominates the blocked passed as parameter. + // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; size_t GetLifetimeStart() const { return lifetime_start_; } @@ -2196,15 +2197,16 @@ class HFloatConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } bool IsMinusOne() const OVERRIDE { - return bit_cast<uint32_t, float>(AsFloatConstant()->GetValue()) == - bit_cast<uint32_t, float>((-1.0f)); + return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f)); } bool IsZero() const OVERRIDE { - return AsFloatConstant()->GetValue() == 0.0f; + return value_ == 0.0f; } bool IsOne() const OVERRIDE { - return bit_cast<uint32_t, float>(AsFloatConstant()->GetValue()) == - bit_cast<uint32_t, float>(1.0f); + return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f); + } + bool IsNaN() const { + return std::isnan(value_); } DECLARE_INSTRUCTION(FloatConstant); @@ -2234,15 +2236,16 @@ class HDoubleConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } bool IsMinusOne() const OVERRIDE { - return bit_cast<uint64_t, double>(AsDoubleConstant()->GetValue()) == - bit_cast<uint64_t, double>((-1.0)); + return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0)); } bool IsZero() const OVERRIDE { - return AsDoubleConstant()->GetValue() == 0.0; + return value_ == 0.0; } bool IsOne() const OVERRIDE { - return bit_cast<uint64_t, double>(AsDoubleConstant()->GetValue()) == - bit_cast<uint64_t, double>(1.0); + return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0); + } + bool IsNaN() const { + return std::isnan(value_); } DECLARE_INSTRUCTION(DoubleConstant); @@ -2979,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/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e993d778d4..8bb5d8ebae 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -320,8 +320,10 @@ static void RunOptimizations(HGraph* graph, const DexCompilationUnit& dex_compilation_unit, PassInfoPrinter* pass_info_printer, StackHandleScopeCollection* handles) { - HDeadCodeElimination dce1(graph, stats); - HDeadCodeElimination dce2(graph, stats, "dead_code_elimination_final"); + HDeadCodeElimination dce1(graph, stats, + HDeadCodeElimination::kInitialDeadCodeEliminationPassName); + HDeadCodeElimination dce2(graph, stats, + HDeadCodeElimination::kFinalDeadCodeEliminationPassName); HConstantFolding fold1(graph); InstructionSimplifier simplify1(graph, stats); HBooleanSimplifier boolean_simplify(graph); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index f53f846326..925099ade6 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -775,7 +775,7 @@ bool RegisterAllocator::TryAllocateFreeReg(LiveInterval* current) { } else if (current->IsLowInterval()) { reg = FindAvailableRegisterPair(free_until, current->GetStart()); } else { - reg = FindAvailableRegister(free_until); + reg = FindAvailableRegister(free_until, current); } } @@ -839,14 +839,52 @@ int RegisterAllocator::FindAvailableRegisterPair(size_t* next_use, size_t starti return reg; } -int RegisterAllocator::FindAvailableRegister(size_t* next_use) const { +bool RegisterAllocator::IsCallerSaveRegister(int reg) const { + return processing_core_registers_ + ? !codegen_->IsCoreCalleeSaveRegister(reg) + : !codegen_->IsFloatingPointCalleeSaveRegister(reg); +} + +int RegisterAllocator::FindAvailableRegister(size_t* next_use, LiveInterval* current) const { + // We special case intervals that do not span a safepoint to try to find a caller-save + // register if one is available. We iterate from 0 to the number of registers, + // so if there are caller-save registers available at the end, we continue the iteration. + bool prefers_caller_save = !current->HasWillCallSafepoint(); int reg = kNoRegister; - // Pick the register that is used the last. for (size_t i = 0; i < number_of_registers_; ++i) { - if (IsBlocked(i)) continue; - if (reg == kNoRegister || next_use[i] > next_use[reg]) { + if (IsBlocked(i)) { + // Register cannot be used. Continue. + continue; + } + + // Best case: we found a register fully available. + if (next_use[i] == kMaxLifetimePosition) { + if (prefers_caller_save && !IsCallerSaveRegister(i)) { + // We can get shorter encodings on some platforms by using + // small register numbers. So only update the candidate if the previous + // one was not available for the whole method. + if (reg == kNoRegister || next_use[reg] != kMaxLifetimePosition) { + reg = i; + } + // Continue the iteration in the hope of finding a caller save register. + continue; + } else { + reg = i; + // We know the register is good enough. Return it. + break; + } + } + + // If we had no register before, take this one as a reference. + if (reg == kNoRegister) { reg = i; - if (next_use[i] == kMaxLifetimePosition) break; + continue; + } + + // Pick the register that is used the last. + if (next_use[i] > next_use[reg]) { + reg = i; + continue; } } return reg; @@ -971,7 +1009,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { || (first_use >= next_use[GetHighForLowRegister(reg)]); } else { DCHECK(!current->IsHighInterval()); - reg = FindAvailableRegister(next_use); + reg = FindAvailableRegister(next_use, current); should_spill = (first_use >= next_use[reg]); } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index dc9c708eea..6d5bfc3f0d 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -140,7 +140,8 @@ class RegisterAllocator { void DumpInterval(std::ostream& stream, LiveInterval* interval) const; void DumpAllIntervals(std::ostream& stream) const; int FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const; - int FindAvailableRegister(size_t* next_use) const; + int FindAvailableRegister(size_t* next_use, LiveInterval* current) const; + bool IsCallerSaveRegister(int reg) const; // Try splitting an active non-pair or unaligned pair interval at the given `position`. // Returns whether it was successful at finding such an interval. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 82c5454bb0..ce4bbd4fc7 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -76,7 +76,7 @@ class LiveRange FINAL : public ArenaObject<kArenaAllocMisc> { } void Dump(std::ostream& stream) const { - stream << "[" << start_ << ", " << end_ << ")"; + stream << start_ << "-" << end_; } LiveRange* Dup(ArenaAllocator* allocator) const { @@ -542,6 +542,15 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return defined_by_; } + bool HasWillCallSafepoint() const { + for (SafepointPosition* safepoint = first_safepoint_; + safepoint != nullptr; + safepoint = safepoint->GetNext()) { + if (safepoint->GetLocations()->WillCall()) return true; + } + return false; + } + SafepointPosition* FindSafepointJustBefore(size_t position) const { for (SafepointPosition* safepoint = first_safepoint_, *previous = nullptr; safepoint != nullptr; diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index c41066027d..eca6f5a30c 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -860,8 +860,6 @@ void ArmExceptionSlowPath::Emit(Assembler* sasm) { // Set up call to Thread::Current()->pDeliverException. __ LoadFromOffset(kLoadWord, R12, TR, QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value()); __ blx(R12); - // Call never returns. - __ bkpt(0); #undef __ } diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index 313f365df6..dee8287e67 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -398,6 +398,8 @@ class ArmAssembler : public Assembler { Condition cond = AL) = 0; virtual void mls(Register rd, Register rn, Register rm, Register ra, Condition cond = AL) = 0; + virtual void smull(Register rd_lo, Register rd_hi, Register rn, Register rm, + Condition cond = AL) = 0; virtual void umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond = AL) = 0; diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index 95796916b4..6e165fc151 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -200,6 +200,13 @@ void Arm32Assembler::mls(Register rd, Register rn, Register rm, Register ra, } +void Arm32Assembler::smull(Register rd_lo, Register rd_hi, Register rn, + Register rm, Condition cond) { + // Assembler registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. + EmitMulOp(cond, B23 | B22, rd_lo, rd_hi, rn, rm); +} + + void Arm32Assembler::umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond) { // Assembler registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index b922d66513..55ec7b46d8 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -90,6 +90,8 @@ class Arm32Assembler FINAL : public ArmAssembler { Condition cond = AL) OVERRIDE; void mls(Register rd, Register rn, Register rm, Register ra, Condition cond = AL) OVERRIDE; + void smull(Register rd_lo, Register rd_hi, Register rn, Register rm, + Condition cond = AL) OVERRIDE; void umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 4a0ae0ba99..efd517b83a 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -293,12 +293,29 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, f(); } + // NOTE: Only support simple test like "aaa=bbb" + bool EvalFilterString(std::string filter) { + if (filter.compare("") == 0) { + return false; + } + + size_t equal_sign_index = filter.find('='); + if (equal_sign_index == std::string::npos) { + EXPECT_TRUE(false) << "Unsupported filter string."; + } + + std::string lhs = filter.substr(0, equal_sign_index); + std::string rhs = filter.substr(equal_sign_index + 1, std::string::npos); + return lhs.compare(rhs) == 0; + } + void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED, - bool without_pc, - std::string fmt, std::ostringstream& oss) { + bool without_pc, std::string fmt, std::string filter, + std::ostringstream& oss) { std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); for (auto reg : registers) { std::string after_reg = fmt; + std::string after_reg_filter = filter; std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); size_t reg_index; @@ -308,14 +325,23 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, after_reg.replace(reg_index, strlen(reg_token), reg_string); } + while ((reg_index = after_reg_filter.find(reg_token)) != std::string::npos) { + after_reg_filter.replace(reg_index, strlen(reg_token), reg_string); + } + if (EvalFilterString(after_reg_filter)) { + continue; + } + ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss); } } void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED, - bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::string filter, + std::ostringstream& oss) { for (const arm::ShifterOperand& shift : GetShiftOperands()) { std::string after_shift = fmt; + std::string after_shift_filter = filter; std::string shift_string = GetShiftString(shift); size_t shift_index; @@ -323,30 +349,48 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); } + while ((shift_index = after_shift_filter.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift_filter.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + if (EvalFilterString(after_shift_filter)) { + continue; + } + ExecuteAndPrint([&] () { f(shift); }, after_shift, oss); } } void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED, - bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::string filter, + std::ostringstream& oss) { for (arm::Condition c : GetConditions()) { std::string after_cond = fmt; + std::string after_cond_filter = filter; size_t cond_index = after_cond.find(COND_TOKEN); if (cond_index != std::string::npos) { after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); } + cond_index = after_cond_filter.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond_filter.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + if (EvalFilterString(after_cond_filter)) { + continue; + } + ExecuteAndPrint([&] () { f(c); }, after_cond, oss); } } template <typename... Args> void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc, - std::string fmt, std::ostringstream& oss) { + std::string fmt, std::string filter, std::ostringstream& oss) { std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); for (auto reg : registers) { std::string after_reg = fmt; + std::string after_reg_filter = filter; std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); size_t reg_index; @@ -356,17 +400,26 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, after_reg.replace(reg_index, strlen(reg_token), reg_string); } + while ((reg_index = after_reg_filter.find(reg_token)) != std::string::npos) { + after_reg_filter.replace(reg_index, strlen(reg_token), reg_string); + } + if (EvalFilterString(after_reg_filter)) { + continue; + } + auto lambda = [&] (Args... args) { f(*reg, args...); }; // NOLINT [readability/braces] [4] TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc, - after_reg, oss); + after_reg, after_reg_filter, oss); } } template <typename... Args> void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth, - bool without_pc, std::string fmt, std::ostringstream& oss) { + bool without_pc, std::string fmt, std::string filter, + std::ostringstream& oss) { for (const arm::ShifterOperand& shift : GetShiftOperands()) { std::string after_shift = fmt; + std::string after_shift_filter = filter; std::string shift_string = GetShiftString(shift); size_t shift_index; @@ -374,26 +427,42 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); } + while ((shift_index = after_shift_filter.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift_filter.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + if (EvalFilterString(after_shift_filter)) { + continue; + } + auto lambda = [&] (Args... args) { f(shift, args...); }; // NOLINT [readability/braces] [4] TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, - after_shift, oss); + after_shift, after_shift_filter, oss); } } template <typename... Args> void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc, - std::string fmt, std::ostringstream& oss) { + std::string fmt, std::string filter, std::ostringstream& oss) { for (arm::Condition c : GetConditions()) { std::string after_cond = fmt; + std::string after_cond_filter = filter; size_t cond_index = after_cond.find(COND_TOKEN); if (cond_index != std::string::npos) { after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); } + cond_index = after_cond_filter.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond_filter.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + if (EvalFilterString(after_cond_filter)) { + continue; + } + auto lambda = [&] (Args... args) { f(c, args...); }; // NOLINT [readability/braces] [4] TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, - after_cond, oss); + after_cond, after_cond_filter, oss); } } @@ -421,13 +490,13 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, template <typename... Args> void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc, - std::string fmt, std::string test_name) { + std::string fmt, std::string test_name, std::string filter) { first_ = false; WarnOnCombinations(CountHelper<Args...>(without_pc)); std::ostringstream oss; - TemplateHelper(f, 0, without_pc, fmt, oss); + TemplateHelper(f, 0, without_pc, fmt, filter, oss); oss << "\n"; // Trailing newline. @@ -436,26 +505,26 @@ class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, template <typename... Args> void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, - std::string test_name) { - GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name); + std::string test_name, std::string filter = "") { + GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name, filter); } template <typename... Args> void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, - std::string test_name) { - GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name); + std::string test_name, std::string filter = "") { + GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name, filter); } template <typename... Args> void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, - std::string test_name) { - GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name); + std::string test_name, std::string filter = "") { + GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name, filter); } template <typename... Args> void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, - std::string test_name) { - GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name); + std::string test_name, std::string filter = "") { + GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name, filter); } private: @@ -565,15 +634,18 @@ TEST_F(AssemblerArm32Test, Mul) { } TEST_F(AssemblerArm32Test, Mla) { - T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul"); + T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mla"); } -/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo. TEST_F(AssemblerArm32Test, Umull) { T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}", - "umull"); + "umull", "{reg1}={reg2}"); // Skip the cases where reg1 == reg2. +} + +TEST_F(AssemblerArm32Test, Smull) { + T5Helper(&arm::Arm32Assembler::smull, true, "smull{cond} {reg1}, {reg2}, {reg3}, {reg4}", + "smull", "{reg1}={reg2}"); // Skip the cases where reg1 == reg2. } -*/ TEST_F(AssemblerArm32Test, Sdiv) { T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv"); @@ -655,9 +727,10 @@ TEST_F(AssemblerArm32Test, Rsc) { T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc"); } -/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3. +/* TODO: Need better filter support. TEST_F(AssemblerArm32Test, Strex) { - RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex"); + T4Helper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex", + "{reg1}={reg2}||{reg1}={reg3}"); // Skip the cases where reg1 == reg2 || reg1 == reg3. } */ diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 3b42f63509..e7cf26eee2 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -238,6 +238,24 @@ void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra, } +void Thumb2Assembler::smull(Register rd_lo, Register rd_hi, Register rn, + Register rm, Condition cond) { + CheckCondition(cond); + + uint32_t op1 = 0U /* 0b000; */; + uint32_t op2 = 0U /* 0b0000 */; + int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | + op1 << 20 | + op2 << 4 | + static_cast<uint32_t>(rd_lo) << 12 | + static_cast<uint32_t>(rd_hi) << 8 | + static_cast<uint32_t>(rn) << 16 | + static_cast<uint32_t>(rm); + + Emit32(encoding); +} + + void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond) { CheckCondition(cond); @@ -740,13 +758,6 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED, return true; } - // Check for MOV with an ROR. - if (opcode == MOV && so.IsRegister() && so.IsShift() && so.GetShift() == ROR) { - if (so.GetImmediate() != 0) { - return true; - } - } - bool rn_is_valid = true; // Check for single operand instructions and ADD/SUB. @@ -792,6 +803,19 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED, } } + // Check for register shift operand. + if (so.IsRegister() && so.IsShift()) { + if (opcode != MOV) { + return true; + } + // Check for MOV with an ROR. + if (so.GetShift() == ROR) { + if (so.GetImmediate() != 0) { + return true; + } + } + } + // The instruction can be encoded in 16 bits. return false; } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index e33c240dbf..17eae8be0c 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -112,6 +112,8 @@ class Thumb2Assembler FINAL : public ArmAssembler { Condition cond = AL) OVERRIDE; void mls(Register rd, Register rn, Register rm, Register ra, Condition cond = AL) OVERRIDE; + void smull(Register rd_lo, Register rd_hi, Register rn, Register rm, + Condition cond = AL) OVERRIDE; void umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index 5f5561a499..733441b889 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -89,23 +89,24 @@ TEST_F(AssemblerThumb2Test, Toolchain) { EXPECT_TRUE(CheckTools()); } +#define __ GetAssembler()-> TEST_F(AssemblerThumb2Test, Sbfx) { - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32); + __ sbfx(arm::R0, arm::R1, 0, 1); + __ sbfx(arm::R0, arm::R1, 0, 8); + __ sbfx(arm::R0, arm::R1, 0, 16); + __ sbfx(arm::R0, arm::R1, 0, 32); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24); + __ sbfx(arm::R0, arm::R1, 8, 1); + __ sbfx(arm::R0, arm::R1, 8, 8); + __ sbfx(arm::R0, arm::R1, 8, 16); + __ sbfx(arm::R0, arm::R1, 8, 24); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16); + __ sbfx(arm::R0, arm::R1, 16, 1); + __ sbfx(arm::R0, arm::R1, 16, 8); + __ sbfx(arm::R0, arm::R1, 16, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1); + __ sbfx(arm::R0, arm::R1, 31, 1); const char* expected = "sbfx r0, r1, #0, #1\n" @@ -127,21 +128,21 @@ TEST_F(AssemblerThumb2Test, Sbfx) { } TEST_F(AssemblerThumb2Test, Ubfx) { - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32); + __ ubfx(arm::R0, arm::R1, 0, 1); + __ ubfx(arm::R0, arm::R1, 0, 8); + __ ubfx(arm::R0, arm::R1, 0, 16); + __ ubfx(arm::R0, arm::R1, 0, 32); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24); + __ ubfx(arm::R0, arm::R1, 8, 1); + __ ubfx(arm::R0, arm::R1, 8, 8); + __ ubfx(arm::R0, arm::R1, 8, 16); + __ ubfx(arm::R0, arm::R1, 8, 24); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16); + __ ubfx(arm::R0, arm::R1, 16, 1); + __ ubfx(arm::R0, arm::R1, 16, 8); + __ ubfx(arm::R0, arm::R1, 16, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1); + __ ubfx(arm::R0, arm::R1, 31, 1); const char* expected = "ubfx r0, r1, #0, #1\n" @@ -163,7 +164,7 @@ TEST_F(AssemblerThumb2Test, Ubfx) { } TEST_F(AssemblerThumb2Test, Vmstat) { - GetAssembler()->vmstat(); + __ vmstat(); const char* expected = "vmrs APSR_nzcv, FPSCR\n"; @@ -171,10 +172,10 @@ TEST_F(AssemblerThumb2Test, Vmstat) { } TEST_F(AssemblerThumb2Test, ldrexd) { - GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0); - GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1); - GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2); - GetAssembler()->ldrexd(arm::R5, arm::R3, arm::R7); + __ ldrexd(arm::R0, arm::R1, arm::R0); + __ ldrexd(arm::R0, arm::R1, arm::R1); + __ ldrexd(arm::R0, arm::R1, arm::R2); + __ ldrexd(arm::R5, arm::R3, arm::R7); const char* expected = "ldrexd r0, r1, [r0]\n" @@ -185,10 +186,10 @@ TEST_F(AssemblerThumb2Test, ldrexd) { } TEST_F(AssemblerThumb2Test, strexd) { - GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0); - GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1); - GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2); - GetAssembler()->strexd(arm::R9, arm::R5, arm::R3, arm::R7); + __ strexd(arm::R9, arm::R0, arm::R1, arm::R0); + __ strexd(arm::R9, arm::R0, arm::R1, arm::R1); + __ strexd(arm::R9, arm::R0, arm::R1, arm::R2); + __ strexd(arm::R9, arm::R5, arm::R3, arm::R7); const char* expected = "strexd r9, r0, r1, [r0]\n" @@ -199,9 +200,9 @@ TEST_F(AssemblerThumb2Test, strexd) { } TEST_F(AssemblerThumb2Test, LdrdStrd) { - GetAssembler()->ldrd(arm::R0, arm::Address(arm::R2, 8)); - GetAssembler()->ldrd(arm::R0, arm::Address(arm::R12)); - GetAssembler()->strd(arm::R0, arm::Address(arm::R2, 8)); + __ ldrd(arm::R0, arm::Address(arm::R2, 8)); + __ ldrd(arm::R0, arm::Address(arm::R12)); + __ strd(arm::R0, arm::Address(arm::R2, 8)); const char* expected = "ldrd r0, r1, [r2, #8]\n" @@ -211,7 +212,6 @@ TEST_F(AssemblerThumb2Test, LdrdStrd) { } TEST_F(AssemblerThumb2Test, eor) { -#define __ GetAssembler()-> __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0)); __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1)); __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0)); @@ -230,23 +230,47 @@ TEST_F(AssemblerThumb2Test, eor) { TEST_F(AssemblerThumb2Test, sub) { __ subs(arm::R1, arm::R0, arm::ShifterOperand(42)); __ sub(arm::R1, arm::R0, arm::ShifterOperand(42)); + __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); + __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); const char* expected = "subs r1, r0, #42\n" - "subw r1, r0, #42\n"; + "subw r1, r0, #42\n" + "subs r1, r0, r2, asr #31\n" + "sub r1, r0, r2, asr #31\n"; DriverStr(expected, "sub"); } TEST_F(AssemblerThumb2Test, add) { __ adds(arm::R1, arm::R0, arm::ShifterOperand(42)); __ add(arm::R1, arm::R0, arm::ShifterOperand(42)); + __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); + __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); const char* expected = "adds r1, r0, #42\n" - "addw r1, r0, #42\n"; + "addw r1, r0, #42\n" + "adds r1, r0, r2, asr #31\n" + "add r1, r0, r2, asr #31\n"; DriverStr(expected, "add"); } +TEST_F(AssemblerThumb2Test, umull) { + __ umull(arm::R0, arm::R1, arm::R2, arm::R3); + + const char* expected = + "umull r0, r1, r2, r3\n"; + DriverStr(expected, "umull"); +} + +TEST_F(AssemblerThumb2Test, smull) { + __ smull(arm::R0, arm::R1, arm::R2, arm::R3); + + const char* expected = + "smull r0, r1, r2, r3\n"; + DriverStr(expected, "smull"); +} + TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) { arm::StoreOperandType type = arm::kStoreWord; int32_t offset = 4092; 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/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index f2541a2113..7e7520066d 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1507,6 +1507,14 @@ void X86Assembler::jmp(Label* label) { } +void X86Assembler::repne_scasw() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0xF2); + EmitUint8(0xAF); +} + + X86Assembler* X86Assembler::lock() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF0); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 946c96de71..136b0cbfdb 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -464,6 +464,8 @@ class X86Assembler FINAL : public Assembler { void jmp(const Address& address); void jmp(Label* label); + void repne_scasw(); + X86Assembler* lock(); void cmpxchgl(const Address& address, Register reg); void cmpxchg8b(const Address& address); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index f326e496d4..aacc57bb0c 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -190,4 +190,10 @@ TEST_F(AssemblerX86Test, FPUIntegerStore) { DriverStr(expected, "FPUIntegerStore"); } +TEST_F(AssemblerX86Test, Repnescasw) { + GetAssembler()->repne_scasw(); + const char* expected = "repne scasw\n"; + DriverStr(expected, "Repnescasw"); +} + } // namespace art diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index c0ca7ef437..feceecac68 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2065,6 +2065,14 @@ void X86_64Assembler::bswapq(CpuRegister dst) { } +void X86_64Assembler::repne_scasw() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0xF2); + EmitUint8(0xAF); +} + + void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) { // TODO: Need to have a code constants table. int64_t constant = bit_cast<int64_t, double>(value); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index f5327a8d02..162714af68 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -601,6 +601,8 @@ class X86_64Assembler FINAL : public Assembler { void bswapl(CpuRegister dst); void bswapq(CpuRegister dst); + void repne_scasw(); + // // Macros for High-level operations. // diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 9e4144ac26..0be4d632fb 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1215,4 +1215,10 @@ TEST_F(AssemblerX86_64Test, MovsxbRegs) { DriverStr(Repeatrb(&x86_64::X86_64Assembler::movsxb, "movsbl %{reg2}, %{reg1}"), "movsxb"); } +TEST_F(AssemblerX86_64Test, Repnescasw) { + GetAssembler()->repne_scasw(); + const char* expected = "repne scasw\n"; + DriverStr(expected, "Repnescasw"); +} + } // namespace art 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/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index f14dfc27ae..cafc868789 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -166,6 +166,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; qpoints->pDeoptimize = art_quick_deoptimize; + + // Read barrier + qpoints->pReadBarrierJni = ReadBarrierJni; } } // namespace art diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc index 4b12f00d0d..8c8f8d51a8 100644 --- a/runtime/arch/arm64/entrypoints_init_arm64.cc +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -159,6 +159,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Deoptimize qpoints->pDeoptimize = art_quick_deoptimize; + + // Read barrier + qpoints->pReadBarrierJni = ReadBarrierJni; }; } // namespace art diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index a980a86135..ff04106f15 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -272,6 +272,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, static_assert(IsDirectEntrypoint(kQuickA64Load), "Non-direct C stub marked direct."); qpoints->pA64Store = QuasiAtomic::Write64; static_assert(IsDirectEntrypoint(kQuickA64Store), "Non-direct C stub marked direct."); + + qpoints->pReadBarrierJni = ReadBarrierJni; + static_assert(!IsDirectEntrypoint(kQuickReadBarrierJni), "Non-direct C stub marked direct."); }; } // namespace art diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc index b328708409..321c27bf50 100644 --- a/runtime/arch/mips64/entrypoints_init_mips64.cc +++ b/runtime/arch/mips64/entrypoints_init_mips64.cc @@ -180,6 +180,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Atomic 64-bit load/store qpoints->pA64Load = QuasiAtomic::Read64; qpoints->pA64Store = QuasiAtomic::Write64; + + // Read barrier + qpoints->pReadBarrierJni = ReadBarrierJni; }; } // namespace art diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index a371632367..737f4d1c5b 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -138,6 +138,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Deoptimize qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_slow_path; + + // Read barrier + qpoints->pReadBarrierJni = ReadBarrierJni; }; } // namespace art diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index 0cddec4102..d0ab9d5d49 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -142,6 +142,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Deoptimize qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_slow_path; + + // Read barrier + qpoints->pReadBarrierJni = ReadBarrierJni; #endif // __APPLE__ }; 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/asm_support.h b/runtime/asm_support.h index de4783a5f8..3e677a4dbe 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -108,7 +108,7 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 145 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 146 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index 65cb02839a..39ce0d2cbe 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -24,11 +24,6 @@ namespace art { -// The number of words necessary to encode bits. -static constexpr uint32_t BitsToWords(uint32_t bits) { - return RoundUp(bits, 32) / 32; -} - // TODO: replace excessive argument defaulting when we are at gcc 4.7 // or later on host with delegating constructor support. Specifically, // starts_bits and storage_size/storage are mutually exclusive. diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index be4d363bf5..6e4367ac9d 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -20,6 +20,8 @@ #include <stdint.h> #include <iterator> +#include "utils.h" + namespace art { class Allocator; @@ -116,6 +118,11 @@ class BitVector { virtual ~BitVector(); + // The number of words necessary to encode bits. + static constexpr uint32_t BitsToWords(uint32_t bits) { + return RoundUp(bits, kWordBits) / kWordBits; + } + // Mark the specified bit as "set". void SetBit(uint32_t idx) { /* 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/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index f272d88807..07cadc48d6 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -107,7 +107,7 @@ bool FdFile::Open(const std::string& path, int flags, mode_t mode) { } int FdFile::Close() { - int result = TEMP_FAILURE_RETRY(close(fd_)); + int result = close(fd_); // Test here, so the file is closed and not leaked. if (kCheckSafeUsage) { diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 5d9cd35c83..d87a563d73 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -29,7 +29,7 @@ namespace art { class CheckReferenceMapVisitor : public StackVisitor { public: explicit CheckReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr) {} + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); 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/debugger.cc b/runtime/debugger.cc index 9b33e50881..852ba49cd2 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -411,7 +411,7 @@ void DebugInvokeReq::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) } void SingleStepControl::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) { - visitor->VisitRootIfNonNull(reinterpret_cast<mirror::Object**>(&method_), root_info); + method_.VisitRootIfNonNull(visitor, root_info); } void SingleStepControl::AddDexPc(uint32_t dex_pc) { @@ -890,8 +890,10 @@ JDWP::JdwpError Dbg::GetOwnedMonitors(JDWP::ObjectId thread_id, std::vector<JDWP::ObjectId>* monitor_vector, std::vector<uint32_t>* stack_depth_vector) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), current_stack_depth(0), - monitors(monitor_vector), stack_depths(stack_depth_vector) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + current_stack_depth(0), + monitors(monitor_vector), + stack_depths(stack_depth_vector) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. @@ -2141,6 +2143,7 @@ JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) { case kWaitingForDebuggerToAttach: case kWaitingForDeoptimization: case kWaitingForGcToComplete: + case kWaitingForGetObjectsAllocated: case kWaitingForJniOnLoad: case kWaitingForMethodTracingStart: case kWaitingForSignalCatcherOutput: @@ -2252,7 +2255,8 @@ void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct CountStackDepthVisitor : public StackVisitor { explicit CountStackDepthVisitor(Thread* thread_in) - : StackVisitor(thread_in, nullptr), depth(0) {} + : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + depth(0) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. @@ -2292,8 +2296,11 @@ JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, size_t start_fram GetFrameVisitor(Thread* thread, size_t start_frame_in, size_t frame_count_in, JDWP::ExpandBuf* buf_in) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr), depth_(0), - start_frame_(start_frame_in), frame_count_(frame_count_in), buf_(buf_in) { + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + depth_(0), + start_frame_(start_frame_in), + frame_count_(frame_count_in), + buf_(buf_in) { expandBufAdd4BE(buf_, frame_count_); } @@ -2410,7 +2417,9 @@ void Dbg::SuspendSelf() { struct GetThisVisitor : public StackVisitor { GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id_in) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_object(nullptr), frame_id(frame_id_in) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_object(nullptr), + frame_id(frame_id_in) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. @@ -2450,7 +2459,9 @@ class FindFrameVisitor FINAL : public StackVisitor { public: FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), frame_id_(frame_id), error_(JDWP::ERR_INVALID_FRAMEID) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + frame_id_(frame_id), + error_(JDWP::ERR_INVALID_FRAMEID) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. @@ -2834,7 +2845,7 @@ class CatchLocationFinder : public StackVisitor { public: CatchLocationFinder(Thread* self, const Handle<mirror::Throwable>& exception, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, context), + : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), self_(self), exception_(exception), handle_scope_(self), @@ -2918,10 +2929,11 @@ void Dbg::PostException(mirror::Throwable* exception_object) { if (!IsDebuggerActive()) { return; } - StackHandleScope<1> handle_scope(Thread::Current()); + Thread* const self = Thread::Current(); + StackHandleScope<1> handle_scope(self); Handle<mirror::Throwable> h_exception(handle_scope.NewHandle(exception_object)); std::unique_ptr<Context> context(Context::Create()); - CatchLocationFinder clf(Thread::Current(), h_exception, context.get()); + CatchLocationFinder clf(self, h_exception, context.get()); clf.WalkStack(/* include_transitions */ false); JDWP::EventLocation exception_throw_location; SetEventLocation(&exception_throw_location, clf.GetThrowMethod(), clf.GetThrowDexPc()); @@ -3582,8 +3594,10 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // is for step-out. struct SingleStepStackVisitor : public StackVisitor { explicit SingleStepStackVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr), stack_depth(0), method(nullptr), line_number(-1) { - } + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + stack_depth(0), + method(nullptr), + line_number(-1) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. @@ -3968,7 +3982,7 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { Handle<mirror::Object> object_result = hs.NewHandle(is_object_result ? result.GetL() : nullptr); Handle<mirror::Throwable> exception = hs.NewHandle(soa.Self()->GetException()); soa.Self()->ClearException(); - pReq->exception = gRegistry->Add(exception.Get()); + pReq->exception = gRegistry->Add(exception); if (pReq->exception != 0) { VLOG(jdwp) << " JDWP invocation returning with exception=" << exception.Get() << " " << exception->Dump(); @@ -4696,7 +4710,9 @@ void Dbg::SetAllocTrackingEnabled(bool enable) { struct AllocRecordStackVisitor : public StackVisitor { AllocRecordStackVisitor(Thread* thread, AllocRecord* record_in) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr), record(record_in), depth(0) {} + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + record(record_in), + depth(0) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. diff --git a/runtime/debugger.h b/runtime/debugger.h index 789a0a4dca..811d345262 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -109,8 +109,8 @@ class SingleStepControl { return stack_depth_; } - mirror::ArtMethod* GetMethod() const { - return method_; + mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return method_.Read(); } const std::set<uint32_t>& GetDexPcs() const { @@ -138,7 +138,7 @@ class SingleStepControl { // set of DEX pcs associated to the source line number where the suspension occurred. // This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step // causes the execution of an instruction in a different method or at a different line number. - mirror::ArtMethod* method_; + GcRoot<mirror::ArtMethod> method_; std::set<uint32_t> dex_pcs_; DISALLOW_COPY_AND_ASSIGN(SingleStepControl); 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/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index db8c0e3c58..b72ce34648 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -32,6 +32,8 @@ class Array; class ArtMethod; class Class; class Object; +template<class MirrorType> +class CompressedReference; } // namespace mirror class Thread; @@ -65,6 +67,10 @@ extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, jobject locked, Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; +extern void ReadBarrierJni(mirror::CompressedReference<mirror::Object>* handle_on_stack, + Thread* self) + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; + } // namespace art #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_H_ diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 035f57a08a..0aca58fb16 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -143,7 +143,9 @@ V(NewStringFromCodePoints, void) \ V(NewStringFromString, void) \ V(NewStringFromStringBuffer, void) \ - V(NewStringFromStringBuilder, void) + V(NewStringFromStringBuilder, void) \ +\ + V(ReadBarrierJni, void, mirror::CompressedReference<mirror::Object>*, Thread*) #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ #undef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ // #define is only for lint. diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index e478d2a840..51817a249d 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -22,6 +22,13 @@ namespace art { +extern void ReadBarrierJni(mirror::CompressedReference<mirror::Object>* handle_on_stack, + Thread* self ATTRIBUTE_UNUSED) { + // Call the read barrier and update the handle. + mirror::Object* to_ref = ReadBarrier::BarrierForRoot(handle_on_stack); + handle_on_stack->Assign(to_ref); +} + // Called on entry to JNI, transition out of Runnable and release share of mutator_lock_. extern uint32_t JniMethodStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1fb45f4d4d..482f656fa6 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -305,8 +305,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuffer, pNewStringFromStringBuilder, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pReadBarrierJni, + sizeof(void*)); - CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pNewStringFromStringBuilder) + CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pReadBarrierJni) + sizeof(void*) == sizeof(QuickEntryPoints), QuickEntryPoints_all); } }; 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/gc/heap.cc b/runtime/gc/heap.cc index 4129d75520..11a0e3c3b8 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1612,10 +1612,19 @@ void Heap::SetTargetHeapUtilization(float target) { } size_t Heap::GetObjectsAllocated() const { + Thread* self = Thread::Current(); + ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated); + auto* tl = Runtime::Current()->GetThreadList(); + // Need SuspendAll here to prevent lock violation if RosAlloc does it during InspectAll. + tl->SuspendAll(__FUNCTION__); size_t total = 0; - for (space::AllocSpace* space : alloc_spaces_) { - total += space->GetObjectsAllocated(); + { + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetObjectsAllocated(); + } } + tl->ResumeAll(); return total; } 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/instrumentation.cc b/runtime/instrumentation.cc index f810bc8289..98e6200bcb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -44,6 +44,11 @@ namespace instrumentation { constexpr bool kVerboseInstrumentation = false; +// Instrumentation works on non-inlined frames by updating returned PCs +// of compiled frames. +static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk = + StackVisitor::StackWalkKind::kSkipInlinedFrames; + static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) { Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); @@ -162,7 +167,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct InstallStackVisitor FINAL : public StackVisitor { InstallStackVisitor(Thread* thread_in, Context* context, uintptr_t instrumentation_exit_pc) - : StackVisitor(thread_in, context), + : StackVisitor(thread_in, context, kInstrumentationStackWalk), instrumentation_stack_(thread_in->GetInstrumentationStack()), instrumentation_exit_pc_(instrumentation_exit_pc), reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0), @@ -303,7 +308,8 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg) struct RestoreStackVisitor FINAL : public StackVisitor { RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc, Instrumentation* instrumentation) - : StackVisitor(thread_in, nullptr), thread_(thread_in), + : StackVisitor(thread_in, nullptr, kInstrumentationStackWalk), + thread_(thread_in), instrumentation_exit_pc_(instrumentation_exit_pc), instrumentation_(instrumentation), instrumentation_stack_(thread_in->GetInstrumentationStack()), @@ -964,7 +970,7 @@ void Instrumentation::ExceptionCaughtEvent(Thread* thread, static void CheckStackDepth(Thread* self, const InstrumentationStackFrame& instrumentation_frame, int delta) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t frame_id = StackVisitor::ComputeNumFrames(self) + delta; + size_t frame_id = StackVisitor::ComputeNumFrames(self, kInstrumentationStackWalk) + delta; if (frame_id != instrumentation_frame.frame_id_) { LOG(ERROR) << "Expected frame_id=" << frame_id << " but found " << instrumentation_frame.frame_id_; @@ -977,7 +983,7 @@ void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object mirror::ArtMethod* method, uintptr_t lr, bool interpreter_entry) { // We have a callee-save frame meaning this value is guaranteed to never be 0. - size_t frame_id = StackVisitor::ComputeNumFrames(self); + size_t frame_id = StackVisitor::ComputeNumFrames(self, kInstrumentationStackWalk); std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack(); if (kVerboseInstrumentation) { LOG(INFO) << "Entering " << PrettyMethod(method) << " from PC " << reinterpret_cast<void*>(lr); 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..317106bb16 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -755,6 +755,114 @@ 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; + } + DCHECK_GE(start, 0); + DCHECK_GE(end, string->GetLength()); + StackHandleScope<1> hs(self); + Handle<mirror::CharArray> h_char_array(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset + 3)->AsCharArray())); + DCHECK_LE(index, h_char_array->GetLength()); + DCHECK_LE(end - start, h_char_array->GetLength() - index); + 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 setting chars from the new style of String objects during compilation. +static void UnstartedStringSetCharAt( + Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jint index = shadow_frame->GetVReg(arg_offset + 1); + jchar c = shadow_frame->GetVReg(arg_offset + 2); + mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString(); + if (string == nullptr) { + AbortTransactionOrFail(self, "String.setCharAt with null object"); + return; + } + string->SetCharAt(index, c); +} + +// 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 UnstartedStringFactoryNewStringFromString( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::String* to_copy = shadow_frame->GetVRegReference(arg_offset)->AsString(); + if (to_copy == nullptr) { + AbortTransactionOrFail(self, "StringFactory.newStringFromString with null object"); + return; + } + StackHandleScope<1> hs(self); + Handle<mirror::String> h_string(hs.NewHandle(to_copy)); + Runtime* runtime = Runtime::Current(); + gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); + result->SetL(mirror::String::AllocFromString<true>(self, h_string->GetLength(), h_string, 0, + 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(start, 0); + DCHECK_GE(length, 0); + StackHandleScope<1> hs(self); + Handle<mirror::String> h_string(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsString())); + DCHECK_LE(start, h_string->GetLength()); + DCHECK_LE(start + length, h_string->GetLength()); + Runtime* runtime = Runtime::Current(); + gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); + result->SetL(mirror::String::AllocFromString<true>(self, length, h_string, start, allocator)); +} + +// This allows getting the char array for new style of String objects during compilation. +static void UnstartedStringToCharArray( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString(); + if (string == nullptr) { + AbortTransactionOrFail(self, "String.charAt with null object"); + return; + } + result->SetL(string->ToCharArray(self)); +} + static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self, mirror::ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, @@ -1079,6 +1187,20 @@ 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 }, + { "void java.lang.String.setCharAt(int, char)", + &UnstartedStringSetCharAt }, + { "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])", + &UnstartedStringFactoryNewStringFromChars }, + { "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)", + &UnstartedStringFactoryNewStringFromString }, + { "java.lang.String java.lang.String.fastSubstring(int, int)", + &UnstartedStringFastSubstring }, + { "char[] java.lang.String.toCharArray()", + &UnstartedStringToCharArray }, }; for (auto& def : defs) { @@ -1162,6 +1284,8 @@ void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item, std::string name(PrettyMethod(shadow_frame->GetMethod())); const auto& iter = invoke_handlers_.find(name); if (iter != invoke_handlers_.end()) { + // Clear out the result in case it's not zeroed out. + result->SetL(0); (*iter->second)(self, shadow_frame, result, arg_offset); } else { // Not special, continue with regular interpreter execution. @@ -1175,6 +1299,8 @@ void UnstartedRuntimeJni(Thread* self, mirror::ArtMethod* method, mirror::Object std::string name(PrettyMethod(method)); const auto& iter = jni_handlers_.find(name); if (iter != jni_handlers_.end()) { + // Clear out the result in case it's not zeroed out. + result->SetL(0); (*iter->second)(self, method, receiver, args, result); } else if (Runtime::Current()->IsActiveTransaction()) { AbortTransactionF(self, "Attempt to invoke native method in non-started runtime: %s", diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index ab3f2e4ddd..ff75268daa 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -32,6 +32,8 @@ #include "scoped_thread_state_change.h" #include "thread-inl.h" +#include "handle_scope-inl.h" + /* General notes: @@ -108,20 +110,32 @@ namespace JDWP { * Stuff to compare against when deciding if a mod matches. Only the * values for mods valid for the event being evaluated will be filled in. * The rest will be zeroed. + * Must be allocated on the stack only. This is enforced by removing the + * operator new. */ struct ModBasket { - ModBasket() : pLoc(nullptr), thread(nullptr), locationClass(nullptr), exceptionClass(nullptr), - caught(false), field(nullptr), thisPtr(nullptr) { } - - const EventLocation* pLoc; /* LocationOnly */ - std::string className; /* ClassMatch/ClassExclude */ - Thread* thread; /* ThreadOnly */ - mirror::Class* locationClass; /* ClassOnly */ - mirror::Class* exceptionClass; /* ExceptionOnly */ - bool caught; /* ExceptionOnly */ - ArtField* field; /* FieldOnly */ - mirror::Object* thisPtr; /* InstanceOnly */ + explicit ModBasket(Thread* self) + : hs(self), pLoc(nullptr), thread(self), + locationClass(hs.NewHandle<mirror::Class>(nullptr)), + exceptionClass(hs.NewHandle<mirror::Class>(nullptr)), + caught(false), + field(nullptr), + thisPtr(hs.NewHandle<mirror::Object>(nullptr)) { } + + StackHandleScope<3> hs; + const EventLocation* pLoc; /* LocationOnly */ + std::string className; /* ClassMatch/ClassExclude */ + Thread* const thread; /* ThreadOnly */ + MutableHandle<mirror::Class> locationClass; /* ClassOnly */ + MutableHandle<mirror::Class> exceptionClass; /* ExceptionOnly */ + bool caught; /* ExceptionOnly */ + ArtField* field; /* FieldOnly */ + MutableHandle<mirror::Object> thisPtr; /* InstanceOnly */ /* nothing for StepOnly -- handled differently */ + + private: + DISALLOW_ALLOCATION(); // forbids allocation on the heap. + DISALLOW_IMPLICIT_CONSTRUCTORS(ModBasket); }; static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { @@ -457,7 +471,7 @@ static bool ModsMatch(JdwpEvent* pEvent, const ModBasket& basket) } break; case MK_CLASS_ONLY: - if (!Dbg::MatchType(basket.locationClass, pMod->classOnly.refTypeId)) { + if (!Dbg::MatchType(basket.locationClass.Get(), pMod->classOnly.refTypeId)) { return false; } break; @@ -478,7 +492,7 @@ static bool ModsMatch(JdwpEvent* pEvent, const ModBasket& basket) break; case MK_EXCEPTION_ONLY: if (pMod->exceptionOnly.refTypeId != 0 && - !Dbg::MatchType(basket.exceptionClass, pMod->exceptionOnly.refTypeId)) { + !Dbg::MatchType(basket.exceptionClass.Get(), pMod->exceptionOnly.refTypeId)) { return false; } if ((basket.caught && !pMod->exceptionOnly.caught) || @@ -497,7 +511,7 @@ static bool ModsMatch(JdwpEvent* pEvent, const ModBasket& basket) } break; case MK_INSTANCE_ONLY: - if (!Dbg::MatchInstance(pMod->instanceOnly.objectId, basket.thisPtr)) { + if (!Dbg::MatchInstance(pMod->instanceOnly.objectId, basket.thisPtr.Get())) { return false; } break; @@ -825,12 +839,11 @@ void JdwpState::PostLocationEvent(const EventLocation* pLoc, mirror::Object* thi DCHECK(pLoc->method != nullptr); DCHECK_EQ(pLoc->method->IsStatic(), thisPtr == nullptr); - ModBasket basket; + ModBasket basket(Thread::Current()); basket.pLoc = pLoc; - basket.locationClass = pLoc->method->GetDeclaringClass(); - basket.thisPtr = thisPtr; - basket.thread = Thread::Current(); - basket.className = Dbg::GetClassName(basket.locationClass); + basket.locationClass.Assign(pLoc->method->GetDeclaringClass()); + basket.thisPtr.Assign(thisPtr); + basket.className = Dbg::GetClassName(basket.locationClass.Get()); /* * On rare occasions we may need to execute interpreted code in the VM @@ -924,16 +937,15 @@ void JdwpState::PostFieldEvent(const EventLocation* pLoc, ArtField* field, DCHECK_EQ(fieldValue != nullptr, is_modification); DCHECK_EQ(field->IsStatic(), this_object == nullptr); - ModBasket basket; + ModBasket basket(Thread::Current()); basket.pLoc = pLoc; - basket.locationClass = pLoc->method->GetDeclaringClass(); - basket.thisPtr = this_object; - basket.thread = Thread::Current(); - basket.className = Dbg::GetClassName(basket.locationClass); + basket.locationClass.Assign(pLoc->method->GetDeclaringClass()); + basket.thisPtr.Assign(this_object); + basket.className = Dbg::GetClassName(basket.locationClass.Get()); basket.field = field; if (InvokeInProgress()) { - VLOG(jdwp) << "Not posting field event during invoke"; + VLOG(jdwp) << "Not posting field event during invoke (" << basket.className << ")"; return; } @@ -975,7 +987,7 @@ void JdwpState::PostFieldEvent(const EventLocation* pLoc, ArtField* field, uint8_t tag; { ScopedObjectAccessUnchecked soa(Thread::Current()); - tag = Dbg::TagFromObject(soa, basket.thisPtr); + tag = Dbg::TagFromObject(soa, basket.thisPtr.Get()); } for (const JdwpEvent* pEvent : match_list) { @@ -1028,8 +1040,7 @@ void JdwpState::PostThreadChange(Thread* thread, bool start) { return; } - ModBasket basket; - basket.thread = thread; + ModBasket basket(thread); std::vector<JdwpEvent*> match_list; const JdwpEventKind match_kind = (start) ? EK_THREAD_START : EK_THREAD_DEATH; @@ -1106,18 +1117,15 @@ void JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* VLOG(jdwp) << "Unexpected: exception event with empty throw location"; } - ModBasket basket; + ModBasket basket(Thread::Current()); basket.pLoc = pThrowLoc; if (pThrowLoc->method != nullptr) { - basket.locationClass = pThrowLoc->method->GetDeclaringClass(); - } else { - basket.locationClass = nullptr; + basket.locationClass.Assign(pThrowLoc->method->GetDeclaringClass()); } - basket.thread = Thread::Current(); - basket.className = Dbg::GetClassName(basket.locationClass); - basket.exceptionClass = exception_object->GetClass(); + basket.className = Dbg::GetClassName(basket.locationClass.Get()); + basket.exceptionClass.Assign(exception_object->GetClass()); basket.caught = (pCatchLoc->method != 0); - basket.thisPtr = thisPtr; + basket.thisPtr.Assign(thisPtr); /* don't try to post an exception caused by the debugger */ if (InvokeInProgress()) { @@ -1188,10 +1196,9 @@ void JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* void JdwpState::PostClassPrepare(mirror::Class* klass) { DCHECK(klass != nullptr); - ModBasket basket; - basket.locationClass = klass; - basket.thread = Thread::Current(); - basket.className = Dbg::GetClassName(basket.locationClass); + ModBasket basket(Thread::Current()); + basket.locationClass.Assign(klass); + basket.className = Dbg::GetClassName(basket.locationClass.Get()); /* suppress class prep caused by debugger */ if (InvokeInProgress()) { @@ -1214,7 +1221,7 @@ void JdwpState::PostClassPrepare(mirror::Class* klass) { // debuggers seem to like that. There might be some advantage to honesty, // since the class may not yet be verified. int status = JDWP::CS_VERIFIED | JDWP::CS_PREPARED; - JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass); + JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass.Get()); std::string temp; std::string signature(basket.locationClass->GetDescriptor(&temp)); diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index a42a58f601..2b28f7df5a 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -36,17 +36,45 @@ ObjectRegistry::ObjectRegistry() } JDWP::RefTypeId ObjectRegistry::AddRefType(mirror::Class* c) { - return InternalAdd(c); + return Add(c); } -JDWP::ObjectId ObjectRegistry::Add(mirror::Object* o) { - return InternalAdd(o); +JDWP::RefTypeId ObjectRegistry::AddRefType(Handle<mirror::Class> c_h) { + return Add(c_h); } -JDWP::ObjectId ObjectRegistry::InternalAdd(mirror::Object* o) { +JDWP::ObjectId ObjectRegistry::Add(mirror::Object* o) { if (o == nullptr) { return 0; } + Thread* const self = Thread::Current(); + StackHandleScope<1> hs(self); + return InternalAdd(hs.NewHandle(o)); +} + +// Template instantiations must be declared below. +template<class T> +JDWP::ObjectId ObjectRegistry::Add(Handle<T> obj_h) { + if (obj_h.Get() == nullptr) { + return 0; + } + return InternalAdd(obj_h); +} + +// Explicit template instantiation. +template +SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_) +JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Object> obj_h); + +template +SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_) +JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Throwable> obj_h); + +template<class T> +JDWP::ObjectId ObjectRegistry::InternalAdd(Handle<T> obj_h) { + CHECK(obj_h.Get() != nullptr); Thread* const self = Thread::Current(); self->AssertNoPendingException(); @@ -55,9 +83,6 @@ JDWP::ObjectId ObjectRegistry::InternalAdd(mirror::Object* o) { Locks::thread_list_lock_->AssertNotHeld(self); Locks::thread_suspend_count_lock_->AssertNotHeld(self); - StackHandleScope<1> hs(self); - Handle<mirror::Object> obj_h(hs.NewHandle(o)); - // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock. int32_t identity_hash_code = obj_h->IdentityHashCode(); diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h index 27a4e55f41..4c149cdac7 100644 --- a/runtime/jdwp/object_registry.h +++ b/runtime/jdwp/object_registry.h @@ -23,6 +23,7 @@ #include <map> #include "base/casts.h" +#include "handle.h" #include "jdwp/jdwp.h" #include "safe_map.h" @@ -65,11 +66,23 @@ class ObjectRegistry { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_); + JDWP::RefTypeId AddRefType(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_); + template<class T> + JDWP::ObjectId Add(Handle<T> obj_h) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_); + + JDWP::RefTypeId AddRefType(Handle<mirror::Class> c_h) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_); + template<typename T> T Get(JDWP::ObjectId id, JDWP::JdwpError* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (id == 0) { @@ -98,7 +111,8 @@ class ObjectRegistry { jobject GetJObject(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: - JDWP::ObjectId InternalAdd(mirror::Object* o) + template<class T> + JDWP::ObjectId InternalAdd(Handle<T> obj_h) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_, Locks::thread_list_lock_, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 9bb08a23d2..fd386d7b38 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2099,6 +2099,35 @@ class JNI { return JNI_ERR; } bool is_fast = false; + // Notes about fast JNI calls: + // + // On a normal JNI call, the calling thread usually transitions + // from the kRunnable state to the kNative state. But if the + // called native function needs to access any Java object, it + // will have to transition back to the kRunnable state. + // + // There is a cost to this double transition. For a JNI call + // that should be quick, this cost may dominate the call cost. + // + // On a fast JNI call, the calling thread avoids this double + // transition by not transitioning from kRunnable to kNative and + // stays in the kRunnable state. + // + // There are risks to using a fast JNI call because it can delay + // a response to a thread suspension request which is typically + // used for a GC root scanning, etc. If a fast JNI call takes a + // long time, it could cause longer thread suspension latency + // and GC pauses. + // + // Thus, fast JNI should be used with care. It should be used + // for a JNI call that takes a short amount of time (eg. no + // long-running loop) and does not block (eg. no locks, I/O, + // etc.) + // + // A '!' prefix in the signature in the JNINativeMethod + // indicates that it's a fast JNI call and the runtime omits the + // thread state transition from kRunnable to kNative at the + // entry. if (*sig == '!') { is_fast = true; ++sig; diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 543cf9bae3..9518c9d797 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -511,7 +511,6 @@ QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { if (class_linker->IsQuickGenericJniStub(entry_point)) { // Generic JNI frame. DCHECK(IsNative()); - StackHandleScope<1> hs(Thread::Current()); uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(this) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); QuickMethodFrameInfo callee_info = runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); 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/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 17fbc4f85d..1d7d853431 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -84,7 +84,8 @@ static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass) { struct ClosestUserClassLoaderVisitor : public StackVisitor { explicit ClosestUserClassLoaderVisitor(Thread* thread) - : StackVisitor(thread, nullptr), class_loader(nullptr) {} + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + class_loader(nullptr) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(class_loader == nullptr); 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/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index be7022e281..6569d833c5 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -84,6 +84,7 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha case kWaitingInMainDebuggerLoop: return kJavaWaiting; case kWaitingForDebuggerSuspension: return kJavaWaiting; case kWaitingForDeoptimization: return kJavaWaiting; + case kWaitingForGetObjectsAllocated: return kJavaWaiting; case kWaitingForJniOnLoad: return kJavaWaiting; case kWaitingForSignalCatcherOutput: return kJavaWaiting; case kWaitingInMainSignalCatcherLoop: return kJavaWaiting; diff --git a/runtime/nth_caller_visitor.h b/runtime/nth_caller_visitor.h index 632ccdedc0..d2d7fa8a21 100644 --- a/runtime/nth_caller_visitor.h +++ b/runtime/nth_caller_visitor.h @@ -27,8 +27,11 @@ class Thread; // Walks up the stack 'n' callers, when used with Thread::WalkStack. struct NthCallerVisitor : public StackVisitor { NthCallerVisitor(Thread* thread, size_t n_in, bool include_runtime_and_upcalls = false) - : StackVisitor(thread, nullptr), n(n_in), - include_runtime_and_upcalls_(include_runtime_and_upcalls), count(0), caller(nullptr) {} + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + n(n_in), + include_runtime_and_upcalls_(include_runtime_and_upcalls), + count(0), + caller(nullptr) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 2f67263285..d07c09cd9a 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -96,9 +96,8 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, OatFileAssistant::~OatFileAssistant() { // Clean up the lock file. - if (lock_file_.get() != nullptr) { - lock_file_->Erase(); - TEMP_FAILURE_RETRY(unlink(lock_file_->GetPath().c_str())); + if (flock_.HasFile()) { + TEMP_FAILURE_RETRY(unlink(flock_.GetFile()->GetPath().c_str())); } } @@ -121,7 +120,7 @@ bool OatFileAssistant::IsInBootClassPath() { bool OatFileAssistant::Lock(std::string* error_msg) { CHECK(error_msg != nullptr); - CHECK(lock_file_.get() == nullptr) << "OatFileAssistant::Lock already acquired"; + CHECK(!flock_.HasFile()) << "OatFileAssistant::Lock already acquired"; if (OatFileName() == nullptr) { *error_msg = "Failed to determine lock file"; @@ -129,13 +128,7 @@ bool OatFileAssistant::Lock(std::string* error_msg) { } std::string lock_file_name = *OatFileName() + ".flock"; - lock_file_.reset(OS::CreateEmptyFile(lock_file_name.c_str())); - if (lock_file_.get() == nullptr) { - *error_msg = "Failed to create lock file " + lock_file_name; - return false; - } - - if (!flock_.Init(lock_file_.get(), error_msg)) { + if (!flock_.Init(lock_file_name.c_str(), error_msg)) { TEMP_FAILURE_RETRY(unlink(lock_file_name.c_str())); return false; } @@ -465,7 +458,7 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { - VLOG(oat) << "No image for to check oat relocation against."; + VLOG(oat) << "No image to check oat relocation against."; return false; } diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 3b2e2c45ca..4c0b0e26e6 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -353,7 +353,6 @@ class OatFileAssistant { // To implement Lock(), we lock a dummy file where the oat file would go // (adding ".flock" to the target file name) and retain the lock for the // remaining lifetime of the OatFileAssistant object. - std::unique_ptr<File> lock_file_; ScopedFlock flock_; // In a properly constructed OatFileAssistant object, dex_location_ should diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 3f6b2d2cc6..865fcb063a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -29,6 +29,7 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" +#include "gc/space/image_space.h" #include "mem_map.h" #include "os.h" #include "scoped_thread_state_change.h" @@ -610,10 +611,23 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Things aren't relocated, so it should fall back to interpreted. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); + EXPECT_FALSE(oat_file->IsExecutable()); std::vector<std::unique_ptr<const DexFile>> dex_files; dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str()); EXPECT_EQ(1u, dex_files.size()); + + // Add some extra checks to help diagnose apparently flaky test failures. + Runtime* runtime = Runtime::Current(); + const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); + ASSERT_TRUE(image_space != nullptr); + const ImageHeader& image_header = image_space->GetImageHeader(); + const OatHeader& oat_header = oat_file->GetOatHeader(); + EXPECT_FALSE(oat_file->IsPic()); + EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum()); + EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta()); } // Case: We have a DEX file and a PIC ODEX file, but no OAT file. diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 90a47b38c2..3b0e6c1062 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -58,8 +58,10 @@ class BoundedStackVisitor : public StackVisitor { BoundedStackVisitor(std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack, Thread* thread, uint32_t max_depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr), stack_(stack), max_depth_(max_depth), depth_(0) { - } + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + stack_(stack), + max_depth_(max_depth), + depth_(0) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); @@ -300,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/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 9e79bd20cb..730759a71b 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -46,7 +46,9 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception, QuickExceptionHandler* exception_handler) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, context), self_(self), exception_(exception), + : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + self_(self), + exception_(exception), exception_handler_(exception_handler) { } @@ -160,7 +162,9 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { public: DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, context), self_(self), exception_handler_(exception_handler), + : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + self_(self), + exception_handler_(exception_handler), prev_shadow_frame_(nullptr) { CHECK(!self_->HasDeoptimizationShadowFrame()); } @@ -338,7 +342,7 @@ class InstrumentationStackVisitor : public StackVisitor { public: InstrumentationStackVisitor(Thread* self, size_t frame_depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, nullptr), + : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), frame_depth_(frame_depth), instrumentation_frames_to_pop_(0) { CHECK_NE(frame_depth_, kInvalidFrameDepth); @@ -349,7 +353,12 @@ class InstrumentationStackVisitor : public StackVisitor { if (current_frame_depth < frame_depth_) { CHECK(GetMethod() != nullptr); if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) { - ++instrumentation_frames_to_pop_; + if (!IsInInlinedFrame()) { + // We do not count inlined frames, because we do not instrument them. The reason we + // include them in the stack walking is the check against `frame_depth_`, which is + // given to us by a visitor that visits inlined frames. + ++instrumentation_frames_to_pop_; + } } return true; } else { diff --git a/runtime/stack.cc b/runtime/stack.cc index a566886d73..800acaa320 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -85,16 +85,20 @@ bool ManagedStack::ShadowFramesContain(StackReference<mirror::Object>* shadow_fr return false; } -StackVisitor::StackVisitor(Thread* thread, Context* context) - : thread_(thread), cur_shadow_frame_(nullptr), - cur_quick_frame_(nullptr), cur_quick_frame_pc_(0), num_frames_(0), cur_depth_(0), - context_(context) { - DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread; -} - -StackVisitor::StackVisitor(Thread* thread, Context* context, size_t num_frames) - : thread_(thread), cur_shadow_frame_(nullptr), - cur_quick_frame_(nullptr), cur_quick_frame_pc_(0), num_frames_(num_frames), cur_depth_(0), +StackVisitor::StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind) + : StackVisitor(thread, context, walk_kind, 0) {} + +StackVisitor::StackVisitor(Thread* thread, + Context* context, + StackWalkKind walk_kind, + size_t num_frames) + : thread_(thread), + walk_kind_(walk_kind), + cur_shadow_frame_(nullptr), + cur_quick_frame_(nullptr), + cur_quick_frame_pc_(0), + num_frames_(num_frames), + cur_depth_(0), context_(context) { DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread; } @@ -565,10 +569,10 @@ void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) { *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc; } -size_t StackVisitor::ComputeNumFrames(Thread* thread) { +size_t StackVisitor::ComputeNumFrames(Thread* thread, StackWalkKind walk_kind) { struct NumFramesVisitor : public StackVisitor { - explicit NumFramesVisitor(Thread* thread_in) - : StackVisitor(thread_in, nullptr), frames(0) {} + NumFramesVisitor(Thread* thread_in, StackWalkKind walk_kind_in) + : StackVisitor(thread_in, nullptr, walk_kind_in), frames(0) {} bool VisitFrame() OVERRIDE { frames++; @@ -577,16 +581,23 @@ size_t StackVisitor::ComputeNumFrames(Thread* thread) { size_t frames; }; - NumFramesVisitor visitor(thread); + NumFramesVisitor visitor(thread, walk_kind); visitor.WalkStack(true); return visitor.frames; } bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc) { struct HasMoreFramesVisitor : public StackVisitor { - explicit HasMoreFramesVisitor(Thread* thread, size_t num_frames, size_t frame_height) - : StackVisitor(thread, nullptr, num_frames), frame_height_(frame_height), - found_frame_(false), has_more_frames_(false), next_method_(nullptr), next_dex_pc_(0) { + HasMoreFramesVisitor(Thread* thread, + StackWalkKind walk_kind, + size_t num_frames, + size_t frame_height) + : StackVisitor(thread, nullptr, walk_kind, num_frames), + frame_height_(frame_height), + found_frame_(false), + has_more_frames_(false), + next_method_(nullptr), + next_dex_pc_(0) { } bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -610,7 +621,7 @@ bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32 mirror::ArtMethod* next_method_; uint32_t next_dex_pc_; }; - HasMoreFramesVisitor visitor(thread_, GetNumFrames(), GetFrameHeight()); + HasMoreFramesVisitor visitor(thread_, walk_kind_, GetNumFrames(), GetFrameHeight()); visitor.WalkStack(true); *next_method = visitor.next_method_; *next_dex_pc = visitor.next_dex_pc_; @@ -620,7 +631,7 @@ bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32 void StackVisitor::DescribeStack(Thread* thread) { struct DescribeStackVisitor : public StackVisitor { explicit DescribeStackVisitor(Thread* thread_in) - : StackVisitor(thread_in, nullptr) {} + : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { LOG(INFO) << "Frame Id=" << GetFrameId() << " " << DescribeLocation(); diff --git a/runtime/stack.h b/runtime/stack.h index ab8641b4d5..bf6101619d 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -409,8 +409,17 @@ class PACKED(4) ManagedStack { }; class StackVisitor { + public: + // This enum defines a flag to control whether inlined frames are included + // when walking the stack. + enum class StackWalkKind { + kIncludeInlinedFrames, + kSkipInlinedFrames, + }; + protected: - StackVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); public: virtual ~StackVisitor() {} @@ -465,7 +474,7 @@ class StackVisitor { size_t GetNumFrames() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (num_frames_ == 0) { - num_frames_ = ComputeNumFrames(thread_); + num_frames_ = ComputeNumFrames(thread_, walk_kind_); } return num_frames_; } @@ -601,6 +610,10 @@ class StackVisitor { return sizeof(StackReference<mirror::ArtMethod>) + (out_num * sizeof(uint32_t)); } + bool IsInInlinedFrame() const { + return false; + } + uintptr_t GetCurrentQuickFramePc() const { return cur_quick_frame_pc_; } @@ -621,13 +634,14 @@ class StackVisitor { std::string DescribeLocation() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static size_t ComputeNumFrames(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static size_t ComputeNumFrames(Thread* thread, StackWalkKind walk_kind) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: // Private constructor known in the case that num_frames_ has already been computed. - StackVisitor(Thread* thread, Context* context, size_t num_frames) + StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind, size_t num_frames) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsAccessibleRegister(uint32_t reg, bool is_float) const { @@ -690,6 +704,7 @@ class StackVisitor { void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Thread* const thread_; + const StackWalkKind walk_kind_; ShadowFrame* cur_shadow_frame_; StackReference<mirror::ArtMethod>* cur_quick_frame_; uintptr_t cur_quick_frame_pc_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 605a1b5419..148bb6d7d7 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -940,10 +940,14 @@ void Thread::DumpState(std::ostream& os) const { struct StackDumpVisitor : public StackVisitor { StackDumpVisitor(std::ostream& os_in, Thread* thread_in, Context* context, bool can_allocate_in) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread_in, context), os(os_in), thread(thread_in), - can_allocate(can_allocate_in), last_method(nullptr), last_line_number(0), - repetition_count(0), frame_count(0) { - } + : StackVisitor(thread_in, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + os(os_in), + thread(thread_in), + can_allocate(can_allocate_in), + last_method(nullptr), + last_line_number(0), + repetition_count(0), + frame_count(0) {} virtual ~StackDumpVisitor() { if (frame_count == 0) { @@ -1528,7 +1532,7 @@ class CountStackDepthVisitor : public StackVisitor { public: explicit CountStackDepthVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, nullptr), + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), depth_(0), skip_depth_(0), skipping_(true) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -1568,8 +1572,12 @@ template<bool kTransactionActive> class BuildInternalStackTraceVisitor : public StackVisitor { public: explicit BuildInternalStackTraceVisitor(Thread* self, Thread* thread, int skip_depth) - : StackVisitor(thread, nullptr), self_(self), - skip_depth_(skip_depth), count_(0), dex_pc_trace_(nullptr), method_trace_(nullptr) {} + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + self_(self), + skip_depth_(skip_depth), + count_(0), + dex_pc_trace_(nullptr), + method_trace_(nullptr) {} bool Init(int depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2072,6 +2080,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pNewStringFromString) QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer) QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder) + QUICK_ENTRY_POINT_INFO(pReadBarrierJni) #undef QUICK_ENTRY_POINT_INFO os << offset; @@ -2111,7 +2120,10 @@ Context* Thread::GetLongJumpContext() { struct CurrentMethodVisitor FINAL : public StackVisitor { CurrentMethodVisitor(Thread* thread, Context* context, bool abort_on_error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_object_(nullptr), method_(nullptr), dex_pc_(0), + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_object_(nullptr), + method_(nullptr), + dex_pc_(0), abort_on_error_(abort_on_error) {} bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); @@ -2154,7 +2166,10 @@ class ReferenceMapVisitor : public StackVisitor { public: ReferenceMapVisitor(Thread* thread, Context* context, RootVisitor& visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), visitor_(visitor) {} + // We are visiting the references in compiled frames, so we do not need + // to know the inlined frames. + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kSkipInlinedFrames), + visitor_(visitor) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (false) { diff --git a/runtime/thread_state.h b/runtime/thread_state.h index b5479edb80..c7ea7f4381 100644 --- a/runtime/thread_state.h +++ b/runtime/thread_state.h @@ -42,6 +42,7 @@ enum ThreadState { kWaitingForDeoptimization, // WAITING TS_WAIT waiting for deoptimization suspend all kWaitingForMethodTracingStart, // WAITING TS_WAIT waiting for method tracing to start kWaitingForVisitObjects, // WAITING TS_WAIT waiting for visiting objects + kWaitingForGetObjectsAllocated, // WAITING TS_WAIT waiting for getting the number of allocated objects kStarting, // NEW TS_WAIT native thread started, not yet ready to run managed code kNative, // RUNNABLE TS_RUNNING running in a JNI native method kSuspended, // RUNNABLE TS_RUNNING suspended by GC or debugger diff --git a/runtime/trace.cc b/runtime/trace.cc index 3b8feda2cd..76367923c0 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -91,8 +91,9 @@ static constexpr uint8_t kOpNewThread = 2U; class BuildStackTraceVisitor : public StackVisitor { public: - explicit BuildStackTraceVisitor(Thread* thread) : StackVisitor(thread, nullptr), - method_trace_(Trace::AllocStackTrace()) {} + explicit BuildStackTraceVisitor(Thread* thread) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_trace_(Trace::AllocStackTrace()) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); 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/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index abd6cb88d4..4dfa73cbaf 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -262,9 +262,80 @@ public class Main { testIndexOfNull(); + // Same data as above, but stored so it's not a literal in the next test. -2 stands for + // indexOf(I) instead of indexOf(II). + start--; + int[][] searchData = { + { 'a', -2, -1 }, + { 'a', -2, 0 }, + { 'b', -2, 1 }, + { 'c', -2, 2 }, + { 'j', -2, 9 }, + { 'a', -2, 0 }, + { 'b', -2, 38 }, + { 'c', -2, 39 }, + { 'a', 20, -1 }, + { 'a', 0, -1 }, + { 'a', -1, -1 }, + { '/', ++start, -1 }, + { 'a', negIndex[0], -1 }, + { 'a', 0, 0 }, + { 'a', 1, -1 }, + { 'a', 1234, -1 }, + { 'b', 0, 1 }, + { 'b', 1, 1 }, + { 'c', 2, 2 }, + { 'j', 5, 9 }, + { 'j', 9, 9 }, + { 'a', 10, 10 }, + { 'b', 40, -1 }, + }; + testStringIndexOfChars(searchData); + testSurrogateIndexOf(); } + private static void testStringIndexOfChars(int[][] searchData) { + // Use a try-catch to avoid inlining. + try { + testStringIndexOfCharsImpl(searchData); + } catch (Exception e) { + System.out.println("Unexpected exception"); + } + } + + private static void testStringIndexOfCharsImpl(int[][] searchData) { + String str0 = ""; + String str1 = "/"; + String str3 = "abc"; + String str10 = "abcdefghij"; + String str40 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc"; + + Assert.assertEquals(str0.indexOf(searchData[0][0]), searchData[0][2]); + Assert.assertEquals(str3.indexOf(searchData[1][0]), searchData[1][2]); + Assert.assertEquals(str3.indexOf(searchData[2][0]), searchData[2][2]); + Assert.assertEquals(str3.indexOf(searchData[3][0]), searchData[3][2]); + Assert.assertEquals(str10.indexOf(searchData[4][0]), searchData[4][2]); + Assert.assertEquals(str40.indexOf(searchData[5][0]), searchData[5][2]); + Assert.assertEquals(str40.indexOf(searchData[6][0]), searchData[6][2]); + Assert.assertEquals(str40.indexOf(searchData[7][0]), searchData[7][2]); + Assert.assertEquals(str0.indexOf(searchData[8][0], searchData[8][1]), searchData[8][2]); + Assert.assertEquals(str0.indexOf(searchData[9][0], searchData[9][1]), searchData[9][2]); + Assert.assertEquals(str0.indexOf(searchData[10][0], searchData[10][1]), searchData[10][2]); + Assert.assertEquals(str1.indexOf(searchData[11][0], searchData[11][1]), searchData[11][2]); + Assert.assertEquals(str1.indexOf(searchData[12][0], searchData[12][1]), searchData[12][2]); + Assert.assertEquals(str3.indexOf(searchData[13][0], searchData[13][1]), searchData[13][2]); + Assert.assertEquals(str3.indexOf(searchData[14][0], searchData[14][1]), searchData[14][2]); + Assert.assertEquals(str3.indexOf(searchData[15][0], searchData[15][1]), searchData[15][2]); + Assert.assertEquals(str3.indexOf(searchData[16][0], searchData[16][1]), searchData[16][2]); + Assert.assertEquals(str3.indexOf(searchData[17][0], searchData[17][1]), searchData[17][2]); + Assert.assertEquals(str3.indexOf(searchData[18][0], searchData[18][1]), searchData[18][2]); + Assert.assertEquals(str10.indexOf(searchData[19][0], searchData[19][1]), searchData[19][2]); + Assert.assertEquals(str10.indexOf(searchData[20][0], searchData[20][1]), searchData[20][2]); + Assert.assertEquals(str40.indexOf(searchData[21][0], searchData[21][1]), searchData[21][2]); + Assert.assertEquals(str40.indexOf(searchData[22][0], searchData[22][1]), searchData[22][2]); + } + private static void testSurrogateIndexOf() { int supplementaryChar = 0x20b9f; String surrogatePair = "\ud842\udf9f"; @@ -274,6 +345,9 @@ public class Main { Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 2), "hello ".length()); Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 6), 6); Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 7), -1); + + Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar - 0x10000), -1); + Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar | 0x80000000), -1); } private static void testIndexOfNull() { diff --git a/test/127-secondarydex/expected.txt b/test/127-secondarydex/expected.txt index 29a1411ad3..1c8defb6ec 100644 --- a/test/127-secondarydex/expected.txt +++ b/test/127-secondarydex/expected.txt @@ -1,3 +1,4 @@ testSlowPathDirectInvoke Test Got null pointer exception +Test diff --git a/test/127-secondarydex/src/Main.java b/test/127-secondarydex/src/Main.java index c921c5b0c8..0ede8ed2b2 100644 --- a/test/127-secondarydex/src/Main.java +++ b/test/127-secondarydex/src/Main.java @@ -24,6 +24,7 @@ import java.lang.reflect.Method; public class Main { public static void main(String[] args) { testSlowPathDirectInvoke(); + testString(); } public static void testSlowPathDirectInvoke() { @@ -40,4 +41,11 @@ public class Main { System.out.println("Got unexpected exception " + e); } } + + // For string change, test that String.<init> is compiled properly in + // secondary dex. See http://b/20870917 + public static void testString() { + Test t = new Test(); + System.out.println(t.toString()); + } } diff --git a/test/127-secondarydex/src/Test.java b/test/127-secondarydex/src/Test.java index 82cb901374..8547e791c2 100644 --- a/test/127-secondarydex/src/Test.java +++ b/test/127-secondarydex/src/Test.java @@ -22,4 +22,8 @@ public class Test extends Super { private void print() { System.out.println("Test"); } + + public String toString() { + return new String("Test"); + } } 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/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index 631b140683..8894d4e5ce 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -17,9 +17,9 @@ public class Main { // CHECK-START: void Main.InlineVoid() inliner (before) - // CHECK-DAG: [[Const42:i\d+]] IntConstant 42 + // CHECK-DAG: <<Const42:i\d+>> IntConstant 42 // CHECK-DAG: InvokeStaticOrDirect - // CHECK-DAG: InvokeStaticOrDirect [ [[Const42]] ] + // CHECK-DAG: InvokeStaticOrDirect [<<Const42>>] // CHECK-START: void Main.InlineVoid() inliner (after) // CHECK-NOT: InvokeStaticOrDirect @@ -30,94 +30,94 @@ public class Main { } // CHECK-START: int Main.InlineParameter(int) inliner (before) - // CHECK-DAG: [[Param:i\d+]] ParameterValue - // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ] - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Param:i\d+>> ParameterValue + // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>] + // CHECK-DAG: Return [<<Result>>] // CHECK-START: int Main.InlineParameter(int) inliner (after) - // CHECK-DAG: [[Param:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Param]] ] + // CHECK-DAG: <<Param:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Param>>] public static int InlineParameter(int a) { return returnParameter(a); } // CHECK-START: long Main.InlineWideParameter(long) inliner (before) - // CHECK-DAG: [[Param:j\d+]] ParameterValue - // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ] - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Param:j\d+>> ParameterValue + // CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>] + // CHECK-DAG: Return [<<Result>>] // CHECK-START: long Main.InlineWideParameter(long) inliner (after) - // CHECK-DAG: [[Param:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Param]] ] + // CHECK-DAG: <<Param:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Param>>] public static long InlineWideParameter(long a) { return returnWideParameter(a); } // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before) - // CHECK-DAG: [[Param:l\d+]] ParameterValue - // CHECK-DAG: [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ] - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Param:l\d+>> ParameterValue + // CHECK-DAG: <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>] + // CHECK-DAG: Return [<<Result>>] // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after) - // CHECK-DAG: [[Param:l\d+]] ParameterValue - // CHECK-DAG: Return [ [[Param]] ] + // CHECK-DAG: <<Param:l\d+>> ParameterValue + // CHECK-DAG: Return [<<Param>>] public static Object InlineReferenceParameter(Object o) { return returnReferenceParameter(o); } // CHECK-START: int Main.InlineInt() inliner (before) - // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Result>>] // CHECK-START: int Main.InlineInt() inliner (after) - // CHECK-DAG: [[Const4:i\d+]] IntConstant 4 - // CHECK-DAG: Return [ [[Const4]] ] + // CHECK-DAG: <<Const4:i\d+>> IntConstant 4 + // CHECK-DAG: Return [<<Const4>>] public static int InlineInt() { return returnInt(); } // CHECK-START: long Main.InlineWide() inliner (before) - // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Result>>] // CHECK-START: long Main.InlineWide() inliner (after) - // CHECK-DAG: [[Const8:j\d+]] LongConstant 8 - // CHECK-DAG: Return [ [[Const8]] ] + // CHECK-DAG: <<Const8:j\d+>> LongConstant 8 + // CHECK-DAG: Return [<<Const8>>] public static long InlineWide() { return returnWide(); } // CHECK-START: int Main.InlineAdd() inliner (before) - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Result]] ] + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Result>>] // CHECK-START: int Main.InlineAdd() inliner (after) - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Add:i\d+]] Add [ [[Const3]] [[Const5]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Add:i\d+>> Add [<<Const3>>,<<Const5>>] + // CHECK-DAG: Return [<<Add>>] public static int InlineAdd() { return returnAdd(3, 5); } // CHECK-START: int Main.InlineFieldAccess() inliner (before) - // CHECK-DAG: [[After:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[After]] ] + // CHECK-DAG: <<After:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<After>>] // CHECK-START: int Main.InlineFieldAccess() inliner (after) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Before:i\d+]] StaticFieldGet - // CHECK-DAG: [[After:i\d+]] Add [ [[Before]] [[Const1]] ] - // CHECK-DAG: StaticFieldSet [ {{l\d+}} [[After]] ] - // CHECK-DAG: Return [ [[After]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Before:i\d+>> StaticFieldGet + // CHECK-DAG: <<After:i\d+>> Add [<<Before>>,<<Const1>>] + // CHECK-DAG: StaticFieldSet [{{l\d+}},<<After>>] + // CHECK-DAG: Return [<<After>>] // CHECK-START: int Main.InlineFieldAccess() inliner (after) // CHECK-NOT: InvokeStaticOrDirect @@ -127,22 +127,22 @@ public class Main { } // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Add:i\d+]] InvokeStaticOrDirect [ [[Const1]] [[Const3]] ] - // CHECK-DAG: [[Sub:i\d+]] InvokeStaticOrDirect [ [[Const5]] [[Const3]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>] + // CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const3]] ] - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const3]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Add:i\d+>> Add [<<Const1>>,<<Const3>>] + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const5>>,<<Const3>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] + // CHECK-DAG: Return [<<Phi>>] public static int InlineWithControlFlow(boolean cond) { int x, const1, const3, const5; diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 6b21fed66c..c258db9c45 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -16,6 +16,12 @@ public class Main { + public static void assertFalse(boolean condition) { + if (condition) { + throw new Error(); + } + } + public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -28,19 +34,31 @@ 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. */ // CHECK-START: int Main.IntNegation() constant_folding (before) - // CHECK-DAG: [[Const42:i\d+]] IntConstant 42 - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Const42]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Const42>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.IntNegation() constant_folding (after) - // CHECK-DAG: [[ConstN42:i\d+]] IntConstant -42 - // CHECK-DAG: Return [ [[ConstN42]] ] + // CHECK-DAG: <<ConstN42:i\d+>> IntConstant -42 + // CHECK-DAG: Return [<<ConstN42>>] public static int IntNegation() { int x, y; @@ -55,14 +73,14 @@ public class Main { */ // CHECK-START: int Main.IntAddition1() constant_folding (before) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Const2:i\d+]] IntConstant 2 - // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + // CHECK-DAG: <<Add:i\d+>> Add [<<Const1>>,<<Const2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.IntAddition1() constant_folding (after) - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: Return [ [[Const3]] ] + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: Return [<<Const3>>] public static int IntAddition1() { int a, b, c; @@ -78,18 +96,18 @@ public class Main { */ // CHECK-START: int Main.IntAddition2() constant_folding (before) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Const2:i\d+]] IntConstant 2 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Const6:i\d+]] IntConstant 6 - // CHECK-DAG: [[Add1:i\d+]] Add [ [[Const1]] [[Const2]] ] - // CHECK-DAG: [[Add2:i\d+]] Add [ [[Const5]] [[Const6]] ] - // CHECK-DAG: [[Add3:i\d+]] Add [ [[Add1]] [[Add2]] ] - // CHECK-DAG: Return [ [[Add3]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Const6:i\d+>> IntConstant 6 + // CHECK-DAG: <<Add1:i\d+>> Add [<<Const1>>,<<Const2>>] + // CHECK-DAG: <<Add2:i\d+>> Add [<<Const5>>,<<Const6>>] + // CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>] + // CHECK-DAG: Return [<<Add3>>] // CHECK-START: int Main.IntAddition2() constant_folding (after) - // CHECK-DAG: [[Const14:i\d+]] IntConstant 14 - // CHECK-DAG: Return [ [[Const14]] ] + // CHECK-DAG: <<Const14:i\d+>> IntConstant 14 + // CHECK-DAG: Return [<<Const14>>] public static int IntAddition2() { int a, b, c; @@ -109,14 +127,14 @@ public class Main { */ // CHECK-START: int Main.IntSubtraction() constant_folding (before) - // CHECK-DAG: [[Const6:i\d+]] IntConstant 6 - // CHECK-DAG: [[Const2:i\d+]] IntConstant 2 - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const6]] [[Const2]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Const6:i\d+>> IntConstant 6 + // CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const6>>,<<Const2>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.IntSubtraction() constant_folding (after) - // CHECK-DAG: [[Const4:i\d+]] IntConstant 4 - // CHECK-DAG: Return [ [[Const4]] ] + // CHECK-DAG: <<Const4:i\d+>> IntConstant 4 + // CHECK-DAG: Return [<<Const4>>] public static int IntSubtraction() { int a, b, c; @@ -132,14 +150,14 @@ public class Main { */ // CHECK-START: long Main.LongAddition() constant_folding (before) - // CHECK-DAG: [[Const1:j\d+]] LongConstant 1 - // CHECK-DAG: [[Const2:j\d+]] LongConstant 2 - // CHECK-DAG: [[Add:j\d+]] Add [ [[Const1]] [[Const2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Const1:j\d+>> LongConstant 1 + // CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + // CHECK-DAG: <<Add:j\d+>> Add [<<Const1>>,<<Const2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: long Main.LongAddition() constant_folding (after) - // CHECK-DAG: [[Const3:j\d+]] LongConstant 3 - // CHECK-DAG: Return [ [[Const3]] ] + // CHECK-DAG: <<Const3:j\d+>> LongConstant 3 + // CHECK-DAG: Return [<<Const3>>] public static long LongAddition() { long a, b, c; @@ -155,14 +173,14 @@ public class Main { */ // CHECK-START: long Main.LongSubtraction() constant_folding (before) - // CHECK-DAG: [[Const6:j\d+]] LongConstant 6 - // CHECK-DAG: [[Const2:j\d+]] LongConstant 2 - // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const6]] [[Const2]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Const6:j\d+>> LongConstant 6 + // CHECK-DAG: <<Const2:j\d+>> LongConstant 2 + // CHECK-DAG: <<Sub:j\d+>> Sub [<<Const6>>,<<Const2>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: long Main.LongSubtraction() constant_folding (after) - // CHECK-DAG: [[Const4:j\d+]] LongConstant 4 - // CHECK-DAG: Return [ [[Const4]] ] + // CHECK-DAG: <<Const4:j\d+>> LongConstant 4 + // CHECK-DAG: Return [<<Const4>>] public static long LongSubtraction() { long a, b, c; @@ -177,14 +195,14 @@ public class Main { */ // CHECK-START: int Main.StaticCondition() constant_folding (before) - // CHECK-DAG: [[Const7:i\d+]] IntConstant 7 - // CHECK-DAG: [[Const2:i\d+]] IntConstant 2 - // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[Const7]] [[Const2]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Const7:i\d+>> IntConstant 7 + // CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + // CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<Const7>>,<<Const2>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.StaticCondition() constant_folding (after) - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: If [ [[Const1]] ] + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: If [<<Const1>>] public static int StaticCondition() { int a, b, c; @@ -207,18 +225,18 @@ public class Main { */ // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before) - // CHECK-DAG: [[Const2:i\d+]] IntConstant 2 - // CHECK-DAG: [[Const5:i\d+]] IntConstant 5 - // CHECK-DAG: [[Add:i\d+]] Add [ [[Const5]] [[Const2]] ] - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const2]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + // CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + // CHECK-DAG: <<Add:i\d+>> Add [<<Const5>>,<<Const2>>] + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const5>>,<<Const2>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) - // CHECK-DAG: [[Const3:i\d+]] IntConstant 3 - // CHECK-DAG: [[Const7:i\d+]] IntConstant 7 - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const7]] [[Const3]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + // CHECK-DAG: <<Const7:i\d+>> IntConstant 7 + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const7>>,<<Const3>>] + // CHECK-DAG: Return [<<Phi>>] public static int JumpsAndConditionals(boolean cond) { int a, b, c; @@ -236,177 +254,393 @@ public class Main { */ // CHECK-START: int Main.And0(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[And:i\d+]] And [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[And]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<And>>] // CHECK-START: int Main.And0(int) constant_folding (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: And - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static int And0(int arg) { return arg & 0; } // CHECK-START: long Main.Mul0(long) constant_folding (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: long Main.Mul0(long) constant_folding (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 // CHECK-NOT: Mul - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static long Mul0(long arg) { return arg * 0; } // CHECK-START: int Main.OrAllOnes(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1 - // CHECK-DAG: [[Or:i\d+]] Or [ [[Arg]] [[ConstF]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 + // CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<ConstF>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.OrAllOnes(int) constant_folding (after) - // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1 + // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 // CHECK-NOT: Or - // CHECK-DAG: Return [ [[ConstF]] ] + // CHECK-DAG: Return [<<ConstF>>] public static int OrAllOnes(int arg) { return arg | -1; } // CHECK-START: long Main.Rem0(long) constant_folding (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[DivZeroCheck:j\d+]] DivZeroCheck [ [[Arg]] ] - // CHECK-DAG: [[Rem:j\d+]] Rem [ [[Const0]] [[DivZeroCheck]] ] - // CHECK-DAG: Return [ [[Rem]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<DivZeroCheck:j\d+>> DivZeroCheck [<<Arg>>] + // CHECK-DAG: <<Rem:j\d+>> Rem [<<Const0>>,<<DivZeroCheck>>] + // CHECK-DAG: Return [<<Rem>>] // CHECK-START: long Main.Rem0(long) constant_folding (after) - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 // CHECK-NOT: Rem - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static long Rem0(long arg) { return 0 % arg; } // CHECK-START: int Main.Rem1(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Rem:i\d+]] Rem [ [[Arg]] [[Const1]] ] - // CHECK-DAG: Return [ [[Rem]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Rem:i\d+>> Rem [<<Arg>>,<<Const1>>] + // CHECK-DAG: Return [<<Rem>>] // CHECK-START: int Main.Rem1(int) constant_folding (after) - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: Rem - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static int Rem1(int arg) { return arg % 1; } // CHECK-START: long Main.RemN1(long) constant_folding (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[ConstN1:j\d+]] LongConstant -1 - // CHECK-DAG: [[DivZeroCheck:j\d+]] DivZeroCheck [ [[Arg]] ] - // CHECK-DAG: [[Rem:j\d+]] Rem [ [[Arg]] [[DivZeroCheck]] ] - // CHECK-DAG: Return [ [[Rem]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<ConstN1:j\d+>> LongConstant -1 + // CHECK-DAG: <<DivZeroCheck:j\d+>> DivZeroCheck [<<ConstN1>>] + // CHECK-DAG: <<Rem:j\d+>> Rem [<<Arg>>,<<DivZeroCheck>>] + // CHECK-DAG: Return [<<Rem>>] // CHECK-START: long Main.RemN1(long) constant_folding (after) - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 // CHECK-NOT: Rem - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static long RemN1(long arg) { return arg % -1; } // CHECK-START: int Main.Shl0(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Const0]] [[Arg]] ] - // CHECK-DAG: Return [ [[Shl]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Shl:i\d+>> Shl [<<Const0>>,<<Arg>>] + // CHECK-DAG: Return [<<Shl>>] // CHECK-START: int Main.Shl0(int) constant_folding (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: Shl - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static int Shl0(int arg) { return 0 << arg; } // CHECK-START: long Main.Shr0(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[Shr:j\d+]] Shr [ [[Const0]] [[Arg]] ] - // CHECK-DAG: Return [ [[Shr]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<Shr:j\d+>> Shr [<<Const0>>,<<Arg>>] + // CHECK-DAG: Return [<<Shr>>] // CHECK-START: long Main.Shr0(int) constant_folding (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 // CHECK-NOT: Shr - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static long Shr0(int arg) { return (long)0 >> arg; } // CHECK-START: long Main.SubSameLong(long) constant_folding (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg]] [[Arg]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Arg>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: long Main.SubSameLong(long) constant_folding (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 // CHECK-NOT: Sub - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static long SubSameLong(long arg) { return arg - arg; } // CHECK-START: int Main.UShr0(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[UShr:i\d+]] UShr [ [[Const0]] [[Arg]] ] - // CHECK-DAG: Return [ [[UShr]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<UShr:i\d+>> UShr [<<Const0>>,<<Arg>>] + // CHECK-DAG: Return [<<UShr>>] // CHECK-START: int Main.UShr0(int) constant_folding (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: UShr - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static int UShr0(int arg) { return 0 >>> arg; } // CHECK-START: int Main.XorSameInt(int) constant_folding (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[Arg]] ] - // CHECK-DAG: Return [ [[Xor]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Arg>>] + // CHECK-DAG: Return [<<Xor>>] // CHECK-START: int Main.XorSameInt(int) constant_folding (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: Xor - // CHECK-DAG: Return [ [[Const0]] ] + // CHECK-DAG: Return [<<Const0>>] public static int XorSameInt(int arg) { return arg ^ arg; } + // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (before) + // CHECK-DAG: <<Arg:f\d+>> ParameterValue + // CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: IntConstant 1 + // CHECK-DAG: <<Cmp:i\d+>> Compare [<<Arg>>,<<ConstNan>>] + // CHECK-DAG: <<Le:z\d+>> LessThanOrEqual [<<Cmp>>,<<Const0>>] + // CHECK-DAG: If [<<Le>>] + + // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (after) + // CHECK-DAG: ParameterValue + // CHECK-DAG: FloatConstant nan + // CHECK-DAG: IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: If [<<Const1>>] + + // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (after) + // CHECK-NOT: Compare + // CHECK-NOT: LessThanOrEqual + + public static boolean CmpFloatGreaterThanNaN(float arg) { + return arg > Float.NaN; + } + + // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (before) + // CHECK-DAG: <<Arg:d\d+>> ParameterValue + // CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: IntConstant 1 + // CHECK-DAG: <<Cmp:i\d+>> Compare [<<Arg>>,<<ConstNan>>] + // CHECK-DAG: <<Ge:z\d+>> GreaterThanOrEqual [<<Cmp>>,<<Const0>>] + // CHECK-DAG: If [<<Ge>>] + + // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (after) + // CHECK-DAG: ParameterValue + // CHECK-DAG: DoubleConstant nan + // CHECK-DAG: IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: If [<<Const1>>] + + // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (after) + // CHECK-NOT: Compare + // CHECK-NOT: GreaterThanOrEqual + + public static boolean CmpDoubleLessThanNaN(double arg) { + 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); @@ -417,17 +651,31 @@ public class Main { assertIntEquals(StaticCondition(), 5); assertIntEquals(JumpsAndConditionals(true), 7); assertIntEquals(JumpsAndConditionals(false), 3); - int random = 123456; // Chosen randomly. - assertIntEquals(And0(random), 0); - assertLongEquals(Mul0(random), 0); - assertIntEquals(OrAllOnes(random), -1); - assertLongEquals(Rem0(random), 0); - assertIntEquals(Rem1(random), 0); - assertLongEquals(RemN1(random), 0); - assertIntEquals(Shl0(random), 0); - assertLongEquals(Shr0(random), 0); - assertLongEquals(SubSameLong(random), 0); - assertIntEquals(UShr0(random), 0); - assertIntEquals(XorSameInt(random), 0); + int arbitrary = 123456; // Value chosen arbitrarily. + assertIntEquals(And0(arbitrary), 0); + assertLongEquals(Mul0(arbitrary), 0); + assertIntEquals(OrAllOnes(arbitrary), -1); + assertLongEquals(Rem0(arbitrary), 0); + assertIntEquals(Rem1(arbitrary), 0); + assertLongEquals(RemN1(arbitrary), 0); + assertIntEquals(Shl0(arbitrary), 0); + assertLongEquals(Shr0(arbitrary), 0); + assertLongEquals(SubSameLong(arbitrary), 0); + assertIntEquals(UShr0(arbitrary), 0); + 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/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 91ac2edbdc..96918d3e3a 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -17,13 +17,13 @@ public class Main { // CHECK-START: int Main.div() licm (before) - // CHECK-DAG: Div ( loop_header:{{B\d+}} ) + // CHECK-DAG: Div loop:{{B\d+}} // CHECK-START: int Main.div() licm (after) - // CHECK-NOT: Div ( loop_header:{{B\d+}} ) + // CHECK-NOT: Div loop:{{B\d+}} // CHECK-START: int Main.div() licm (after) - // CHECK-DAG: Div ( loop_header:null ) + // CHECK-DAG: Div loop:none public static int div() { int result = 0; @@ -34,13 +34,13 @@ public class Main { } // CHECK-START: int Main.innerDiv() licm (before) - // CHECK-DAG: Div ( loop_header:{{B\d+}} ) + // CHECK-DAG: Div loop:{{B\d+}} // CHECK-START: int Main.innerDiv() licm (after) - // CHECK-NOT: Div ( loop_header:{{B\d+}} ) + // CHECK-NOT: Div loop:{{B\d+}} // CHECK-START: int Main.innerDiv() licm (after) - // CHECK-DAG: Div ( loop_header:null ) + // CHECK-DAG: Div loop:none public static int innerDiv() { int result = 0; @@ -53,10 +53,10 @@ public class Main { } // CHECK-START: int Main.innerDiv2() licm (before) - // CHECK-DAG: Mul ( loop_header:{{B4}} ) + // CHECK-DAG: Mul loop:B4 // CHECK-START: int Main.innerDiv2() licm (after) - // CHECK-DAG: Mul ( loop_header:{{B2}} ) + // CHECK-DAG: Mul loop:B2 public static int innerDiv2() { int result = 0; @@ -72,10 +72,10 @@ public class Main { } // CHECK-START: int Main.innerDiv3(int, int) licm (before) - // CHECK-DAG: Div ( loop_header:{{B\d+}} ) + // CHECK-DAG: Div loop:{{B\d+}} // CHECK-START: int Main.innerDiv3(int, int) licm (after) - // CHECK-DAG: Div ( loop_header:{{B\d+}} ) + // CHECK-DAG: Div loop:{{B\d+}} public static int innerDiv3(int a, int b) { int result = 0; @@ -88,16 +88,16 @@ public class Main { } // CHECK-START: int Main.arrayLength(int[]) licm (before) - // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} ) - // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} ) + // CHECK-DAG: <<NullCheck:l\d+>> NullCheck loop:{{B\d+}} + // CHECK-DAG: ArrayLength [<<NullCheck>>] loop:{{B\d+}} // CHECK-START: int Main.arrayLength(int[]) licm (after) - // CHECK-NOT: NullCheck ( loop_header:{{B\d+}} ) - // CHECK-NOT: ArrayLength ( loop_header:{{B\d+}} ) + // CHECK-NOT: NullCheck loop:{{B\d+}} + // CHECK-NOT: ArrayLength loop:{{B\d+}} // CHECK-START: int Main.arrayLength(int[]) licm (after) - // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null ) - // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:null ) + // CHECK-DAG: <<NullCheck:l\d+>> NullCheck loop:none + // CHECK-DAG: ArrayLength [<<NullCheck>>] loop:none public static int arrayLength(int[] array) { int result = 0; diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java index ecf071e459..9ed66d64e3 100644 --- a/test/446-checker-inliner2/src/Main.java +++ b/test/446-checker-inliner2/src/Main.java @@ -17,15 +17,15 @@ public class Main { // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after) - // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet - // CHECK-DAG: Return [ [[Field]] ] + // CHECK-DAG: <<Field:i\d+>> InstanceFieldGet + // CHECK-DAG: Return [<<Field>>] public static int inlineInstanceCall(Main m) { return m.foo(); @@ -38,15 +38,15 @@ public class Main { int field = 42; // CHECK-START: int Main.inlineNestedCall() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineNestedCall() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: int Main.inlineNestedCall() inliner (after) - // CHECK-DAG: [[Const38:i\d+]] IntConstant 38 - // CHECK-DAG: Return [ [[Const38]] ] + // CHECK-DAG: <<Const38:i\d+>> IntConstant 38 + // CHECK-DAG: Return [<<Const38>>] public static int inlineNestedCall() { return nestedCall(); diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java index db4b236b73..9d022b95be 100644 --- a/test/447-checker-inliner3/src/Main.java +++ b/test/447-checker-inliner3/src/Main.java @@ -17,8 +17,8 @@ public class Main { // CHECK-START: int Main.inlineIfThenElse() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineIfThenElse() inliner (after) // CHECK-NOT: InvokeStaticOrDirect diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc index 6b4bc11086..0ef2964e35 100644 --- a/test/454-get-vreg/get_vreg_jni.cc +++ b/test/454-get-vreg/get_vreg_jni.cc @@ -29,7 +29,9 @@ class TestVisitor : public StackVisitor { public: TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_value_(this_value), + found_method_index_(0) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/test/455-set-vreg/set_vreg_jni.cc b/test/455-set-vreg/set_vreg_jni.cc index 0a83ac0738..dffbfa47d8 100644 --- a/test/455-set-vreg/set_vreg_jni.cc +++ b/test/455-set-vreg/set_vreg_jni.cc @@ -29,7 +29,8 @@ class TestVisitor : public StackVisitor { public: TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_value_(this_value) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_value_(this_value) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc index 1b32348e25..193ab9dc4e 100644 --- a/test/457-regs/regs_jni.cc +++ b/test/457-regs/regs_jni.cc @@ -29,7 +29,7 @@ class TestVisitor : public StackVisitor { public: TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index efb7b83e33..742210c8fd 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -51,14 +51,14 @@ public class Main { */ // CHECK-START: long Main.Add0(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[Add:j\d+]] Add [ [[Const0]] [[Arg]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<Add:j\d+>> Add [<<Const0>>,<<Arg>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: long Main.Add0(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.Add0(long) instruction_simplifier (after) // CHECK-NOT: Add @@ -68,14 +68,14 @@ public class Main { } // CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1 - // CHECK-DAG: [[And:i\d+]] And [ [[Arg]] [[ConstF]] ] - // CHECK-DAG: Return [ [[And]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 + // CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<ConstF>>] + // CHECK-DAG: Return [<<And>>] // CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after) // CHECK-NOT: And @@ -85,14 +85,14 @@ public class Main { } // CHECK-START: long Main.Div1(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const1:j\d+]] LongConstant 1 - // CHECK-DAG: [[Div:j\d+]] Div [ [[Arg]] [[Const1]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const1:j\d+>> LongConstant 1 + // CHECK-DAG: <<Div:j\d+>> Div [<<Arg>>,<<Const1>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: long Main.Div1(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.Div1(long) instruction_simplifier (after) // CHECK-NOT: Div @@ -102,15 +102,15 @@ public class Main { } // CHECK-START: int Main.DivN1(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstN1:i\d+]] IntConstant -1 - // CHECK-DAG: [[Div:i\d+]] Div [ [[Arg]] [[ConstN1]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1 + // CHECK-DAG: <<Div:i\d+>> Div [<<Arg>>,<<ConstN1>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: int Main.DivN1(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.DivN1(int) instruction_simplifier (after) // CHECK-NOT: Div @@ -120,14 +120,14 @@ public class Main { } // CHECK-START: long Main.Mul1(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const1:j\d+]] LongConstant 1 - // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const1]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const1:j\d+>> LongConstant 1 + // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const1>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: long Main.Mul1(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.Mul1(long) instruction_simplifier (after) // CHECK-NOT: Mul @@ -137,15 +137,15 @@ public class Main { } // CHECK-START: int Main.MulN1(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstN1:i\d+]] IntConstant -1 - // CHECK-DAG: [[Mul:i\d+]] Mul [ [[Arg]] [[ConstN1]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1 + // CHECK-DAG: <<Mul:i\d+>> Mul [<<Arg>>,<<ConstN1>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: int Main.MulN1(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.MulN1(int) instruction_simplifier (after) // CHECK-NOT: Mul @@ -155,16 +155,16 @@ public class Main { } // CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const128:j\d+]] LongConstant 128 - // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const128]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const128:j\d+>> LongConstant 128 + // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const128>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const7:i\d+]] IntConstant 7 - // CHECK-DAG: [[Shl:j\d+]] Shl [ [[Arg]] [[Const7]] ] - // CHECK-DAG: Return [ [[Shl]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const7:i\d+>> IntConstant 7 + // CHECK-DAG: <<Shl:j\d+>> Shl [<<Arg>>,<<Const7>>] + // CHECK-DAG: Return [<<Shl>>] // CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after) // CHECK-NOT: Mul @@ -174,14 +174,14 @@ public class Main { } // CHECK-START: int Main.Or0(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Or:i\d+]] Or [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.Or0(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.Or0(int) instruction_simplifier (after) // CHECK-NOT: Or @@ -191,13 +191,13 @@ public class Main { } // CHECK-START: long Main.OrSame(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Or:j\d+]] Or [ [[Arg]] [[Arg]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Or:j\d+>> Or [<<Arg>>,<<Arg>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: long Main.OrSame(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.OrSame(long) instruction_simplifier (after) // CHECK-NOT: Or @@ -207,14 +207,14 @@ public class Main { } // CHECK-START: int Main.Shl0(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Shl]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Shl>>] // CHECK-START: int Main.Shl0(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.Shl0(int) instruction_simplifier (after) // CHECK-NOT: Shl @@ -224,15 +224,15 @@ public class Main { } // CHECK-START: int Main.Shl1(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Arg]] [[Const1]] ] - // CHECK-DAG: Return [ [[Shl]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const1>>] + // CHECK-DAG: Return [<<Shl>>] // CHECK-START: int Main.Shl1(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg]] [[Arg]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Arg>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.Shl1(int) instruction_simplifier (after) // CHECK-NOT: Shl @@ -242,14 +242,14 @@ public class Main { } // CHECK-START: long Main.Shr0(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Shr:j\d+]] Shr [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Shr]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Shr>>] // CHECK-START: long Main.Shr0(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.Shr0(long) instruction_simplifier (after) // CHECK-NOT: Shr @@ -259,14 +259,14 @@ public class Main { } // CHECK-START: long Main.Sub0(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: long Main.Sub0(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.Sub0(long) instruction_simplifier (after) // CHECK-NOT: Sub @@ -276,15 +276,15 @@ public class Main { } // CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const0]] [[Arg]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const0>>,<<Arg>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after) // CHECK-NOT: Sub @@ -294,14 +294,14 @@ public class Main { } // CHECK-START: long Main.UShr0(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[UShr:j\d+]] UShr [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[UShr]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<UShr>>] // CHECK-START: long Main.UShr0(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.UShr0(long) instruction_simplifier (after) // CHECK-NOT: UShr @@ -311,14 +311,14 @@ public class Main { } // CHECK-START: int Main.Xor0(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[Const0]] ] - // CHECK-DAG: Return [ [[Xor]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Const0>>] + // CHECK-DAG: Return [<<Xor>>] // CHECK-START: int Main.Xor0(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.Xor0(int) instruction_simplifier (after) // CHECK-NOT: Xor @@ -328,15 +328,15 @@ public class Main { } // CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1 - // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[ConstF]] ] - // CHECK-DAG: Return [ [[Xor]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 + // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<ConstF>>] + // CHECK-DAG: Return [<<Xor>>] // CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Not:i\d+]] Not [ [[Arg]] ] - // CHECK-DAG: Return [ [[Not]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>] + // CHECK-DAG: Return [<<Not>>] // CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after) // CHECK-NOT: Xor @@ -353,20 +353,20 @@ public class Main { */ // CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue // CHECK-NOT: Neg - // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg1]] [[Arg2]] ] - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Add]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>] + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>] + // CHECK-DAG: Return [<<Neg>>] public static int AddNegs1(int arg1, int arg2) { return -arg1 + -arg2; @@ -384,34 +384,34 @@ public class Main { */ // CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add1:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: [[Add2:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Add1]] [[Add2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add1:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: <<Add2:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add1:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: [[Add2:i\d+]] Add [ [[Neg1]] [[Neg2]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add1:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: <<Add2:i\d+>> Add [<<Neg1>>,<<Neg2>>] // CHECK-NOT: Neg - // CHECK-DAG: [[Or:i\d+]] Or [ [[Add1]] [[Add2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.AddNegs2(int, int) GVN (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Add]] [[Add]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Add>>,<<Add>>] + // CHECK-DAG: Return [<<Or>>] public static int AddNegs2(int arg1, int arg2) { int temp1 = -arg1; @@ -429,26 +429,26 @@ public class Main { // CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (before) // -------------- Arguments and initial negation operations. - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Arg2]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Arg2>>] // CHECK: Goto // -------------- Loop // CHECK: SuspendCheck - // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ] + // CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>] // CHECK: Goto // CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (after) // -------------- Arguments and initial negation operations. - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Arg2]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Arg2>>] // CHECK: Goto // -------------- Loop // CHECK: SuspendCheck - // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ] + // CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>] // CHECK-NOT: Neg // CHECK: Goto @@ -469,17 +469,17 @@ public class Main { */ // CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Add:j\d+]] Add [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Add:j\d+>> Add [<<Neg>>,<<Arg2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg2]] [[Arg1]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg2>>,<<Arg1>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after) // CHECK-NOT: Neg @@ -499,22 +499,22 @@ public class Main { */ // CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add1:j\d+]] Add [ [[Arg1]] [[Neg]] ] - // CHECK-DAG: [[Add2:j\d+]] Add [ [[Arg1]] [[Neg]] ] - // CHECK-DAG: [[Res:j\d+]] Or [ [[Add1]] [[Add2]] ] - // CHECK-DAG: Return [ [[Res]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add1:j\d+>> Add [<<Arg1>>,<<Neg>>] + // CHECK-DAG: <<Add2:j\d+>> Add [<<Arg1>>,<<Neg>>] + // CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>] + // CHECK-DAG: Return [<<Res>>] // CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg2]] ] - // CHECK-DAG: [[Add1:j\d+]] Add [ [[Arg1]] [[Neg]] ] - // CHECK-DAG: [[Add2:j\d+]] Add [ [[Arg1]] [[Neg]] ] - // CHECK-DAG: [[Res:j\d+]] Or [ [[Add1]] [[Add2]] ] - // CHECK-DAG: Return [ [[Res]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>] + // CHECK-DAG: <<Add1:j\d+>> Add [<<Arg1>>,<<Neg>>] + // CHECK-DAG: <<Add2:j\d+>> Add [<<Arg1>>,<<Neg>>] + // CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>] + // CHECK-DAG: Return [<<Res>>] // CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after) // CHECK-NOT: Sub @@ -530,14 +530,14 @@ public class Main { */ // CHECK-START: long Main.NegNeg1(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Neg1]] ] - // CHECK-DAG: Return [ [[Neg2]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg>>] + // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Neg1>>] + // CHECK-DAG: Return [<<Neg2>>] // CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after) // CHECK-NOT: Neg @@ -554,26 +554,26 @@ public class Main { */ // CHECK-START: int Main.NegNeg2(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Neg1]] ] - // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Neg1>>] + // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg]] [[Arg]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg>>,<<Arg>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after) // CHECK-NOT: Neg // CHECK-NOT: Add // CHECK-START: int Main.NegNeg2(int) constant_folding_after_inlining (after) - // CHECK: [[Const0:i\d+]] IntConstant 0 + // CHECK: <<Const0:i\d+>> IntConstant 0 // CHECK-NOT: Neg // CHECK-NOT: Add - // CHECK: Return [ [[Const0]] ] + // CHECK: Return [<<Const0>>] public static int NegNeg2(int arg) { int temp = -arg; @@ -588,15 +588,15 @@ public class Main { */ // CHECK-START: long Main.NegNeg3(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[Const0:j\d+]] LongConstant 0 - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg]] ] - // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const0]] [[Neg]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<Const0:j\d+>> LongConstant 0 + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg>>] + // CHECK-DAG: <<Sub:j\d+>> Sub [<<Const0>>,<<Neg>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after) // CHECK-NOT: Neg @@ -613,17 +613,17 @@ public class Main { */ // CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ] - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Sub]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Sub>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg2]] [[Arg1]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg2>>,<<Arg1>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after) // CHECK-NOT: Neg @@ -643,22 +643,22 @@ public class Main { */ // CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ] - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Sub]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Sub]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Sub>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Sub>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ] - // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Sub]] ] - // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Sub]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Neg1]] [[Neg2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] + // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Sub>>] + // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Sub>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>] + // CHECK-DAG: Return [<<Or>>] public static int NegSub2(int arg1, int arg2) { int temp = arg1 - arg2; @@ -671,15 +671,15 @@ public class Main { */ // CHECK-START: long Main.NotNot1(long) instruction_simplifier (before) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: [[ConstF1:j\d+]] LongConstant -1 - // CHECK-DAG: [[Xor1:j\d+]] Xor [ [[Arg]] [[ConstF1]] ] - // CHECK-DAG: [[Xor2:j\d+]] Xor [ [[Xor1]] [[ConstF1]] ] - // CHECK-DAG: Return [ [[Xor2]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: <<ConstF1:j\d+>> LongConstant -1 + // CHECK-DAG: <<Xor1:j\d+>> Xor [<<Arg>>,<<ConstF1>>] + // CHECK-DAG: <<Xor2:j\d+>> Xor [<<Xor1>>,<<ConstF1>>] + // CHECK-DAG: Return [<<Xor2>>] // CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) - // CHECK-DAG: [[Arg:j\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:j\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) // CHECK-NOT: Xor @@ -689,18 +689,18 @@ public class Main { } // CHECK-START: int Main.NotNot2(int) instruction_simplifier (before) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[ConstF1:i\d+]] IntConstant -1 - // CHECK-DAG: [[Xor1:i\d+]] Xor [ [[Arg]] [[ConstF1]] ] - // CHECK-DAG: [[Xor2:i\d+]] Xor [ [[Xor1]] [[ConstF1]] ] - // CHECK-DAG: [[Add:i\d+]] Add [ [[Xor1]] [[Xor2]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<ConstF1:i\d+>> IntConstant -1 + // CHECK-DAG: <<Xor1:i\d+>> Xor [<<Arg>>,<<ConstF1>>] + // CHECK-DAG: <<Xor2:i\d+>> Xor [<<Xor1>>,<<ConstF1>>] + // CHECK-DAG: <<Add:i\d+>> Add [<<Xor1>>,<<Xor2>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: [[Not:i\d+]] Not [ [[Arg]] ] - // CHECK-DAG: [[Add:i\d+]] Add [ [[Not]] [[Arg]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>] + // CHECK-DAG: <<Add:i\d+>> Add [<<Not>>,<<Arg>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) // CHECK-NOT: Xor @@ -716,18 +716,18 @@ public class Main { */ // CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Sub:i\d+>> Sub [<<Neg>>,<<Arg2>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg1]] [[Arg2]] ] - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Add]] ] - // CHECK-DAG: Return [ [[Neg]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>] + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>] + // CHECK-DAG: Return [<<Neg>>] // CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after) // CHECK-NOT: Sub @@ -747,22 +747,22 @@ public class Main { */ // CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (before) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Sub1:i\d+]] Sub [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: [[Sub2:i\d+]] Sub [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Sub1]] [[Sub2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Sub1:i\d+>> Sub [<<Neg>>,<<Arg2>>] + // CHECK-DAG: <<Sub2:i\d+>> Sub [<<Neg>>,<<Arg2>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after) - // CHECK-DAG: [[Arg1:i\d+]] ParameterValue - // CHECK-DAG: [[Arg2:i\d+]] ParameterValue - // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ] - // CHECK-DAG: [[Sub1:i\d+]] Sub [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: [[Sub2:i\d+]] Sub [ [[Neg]] [[Arg2]] ] - // CHECK-DAG: [[Or:i\d+]] Or [ [[Sub1]] [[Sub2]] ] - // CHECK-DAG: Return [ [[Or]] ] + // CHECK-DAG: <<Arg1:i\d+>> ParameterValue + // CHECK-DAG: <<Arg2:i\d+>> ParameterValue + // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] + // CHECK-DAG: <<Sub1:i\d+>> Sub [<<Neg>>,<<Arg2>>] + // CHECK-DAG: <<Sub2:i\d+>> Sub [<<Neg>>,<<Arg2>>] + // CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>] + // CHECK-DAG: Return [<<Or>>] // CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after) // CHECK-NOT: Add @@ -781,24 +781,24 @@ public class Main { // CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (before) // -------------- Arguments and initial negation operation. - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>] // CHECK: Goto // -------------- Loop // CHECK: SuspendCheck - // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ] + // CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>] // CHECK: Goto // CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (after) // -------------- Arguments and initial negation operation. - // CHECK-DAG: [[Arg1:j\d+]] ParameterValue - // CHECK-DAG: [[Arg2:j\d+]] ParameterValue - // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ] + // CHECK-DAG: <<Arg1:j\d+>> ParameterValue + // CHECK-DAG: <<Arg2:j\d+>> ParameterValue + // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>] // CHECK-DAG: Goto // -------------- Loop // CHECK: SuspendCheck - // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ] + // CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>] // CHECK-NOT: Neg // CHECK: Goto @@ -812,116 +812,116 @@ public class Main { } // CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Arg]] [[Const1]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Const1>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: If [ [[Arg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: If [<<Arg>>] public static int EqualTrueRhs(boolean arg) { return (arg != true) ? 3 : 5; } // CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Const1]] [[Arg]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> Equal [<<Const1>>,<<Arg>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: If [ [[Arg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: If [<<Arg>>] public static int EqualTrueLhs(boolean arg) { return (true != arg) ? 3 : 5; } // CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Arg]] [[Const0]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Const0>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ] - // CHECK-DAG: If [ [[NotArg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] + // CHECK-DAG: If [<<NotArg>>] public static int EqualFalseRhs(boolean arg) { return (arg != false) ? 3 : 5; } // CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Const0]] [[Arg]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Cond:z\d+>> Equal [<<Const0>>,<<Arg>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ] - // CHECK-DAG: If [ [[NotArg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] + // CHECK-DAG: If [<<NotArg>>] public static int EqualFalseLhs(boolean arg) { return (false != arg) ? 3 : 5; } // CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Arg]] [[Const1]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Const1>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ] - // CHECK-DAG: If [ [[NotArg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] + // CHECK-DAG: If [<<NotArg>>] public static int NotEqualTrueRhs(boolean arg) { return (arg == true) ? 3 : 5; } // CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Const1]] [[Arg]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Const1>>,<<Arg>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ] - // CHECK-DAG: If [ [[NotArg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] + // CHECK-DAG: If [<<NotArg>>] public static int NotEqualTrueLhs(boolean arg) { return (true == arg) ? 3 : 5; } // CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Arg]] [[Const0]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Const0>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: If [ [[Arg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: If [<<Arg>>] public static int NotEqualFalseRhs(boolean arg) { return (arg == false) ? 3 : 5; } // CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Const0]] [[Arg]] ] - // CHECK-DAG: If [ [[Cond]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Const0>>,<<Arg>>] + // CHECK-DAG: If [<<Cond>>] // CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: If [ [[Arg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: If [<<Arg>>] public static int NotEqualFalseLhs(boolean arg) { return (false == arg) ? 3 : 5; @@ -934,15 +934,15 @@ public class Main { */ // CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (before) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ] - // CHECK-DAG: [[NotNotArg:z\d+]] BooleanNot [ [[NotArg]] ] - // CHECK-DAG: Return [ [[NotNotArg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] + // CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>] + // CHECK-DAG: Return [<<NotNotArg>>] // CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) - // CHECK-DAG: [[Arg:z\d+]] ParameterValue - // CHECK-DAG: BooleanNot [ [[Arg]] ] - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:z\d+>> ParameterValue + // CHECK-DAG: BooleanNot [<<Arg>>] + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) // CHECK: BooleanNot @@ -957,16 +957,16 @@ public class Main { } // CHECK-START: float Main.Div2(float) instruction_simplifier (before) - // CHECK-DAG: [[Arg:f\d+]] ParameterValue - // CHECK-DAG: [[Const2:f\d+]] FloatConstant 2 - // CHECK-DAG: [[Div:f\d+]] Div [ [[Arg]] [[Const2]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:f\d+>> ParameterValue + // CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 + // CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<Const2>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: float Main.Div2(float) instruction_simplifier (after) - // CHECK-DAG: [[Arg:f\d+]] ParameterValue - // CHECK-DAG: [[ConstP5:f\d+]] FloatConstant 0.5 - // CHECK-DAG: [[Mul:f\d+]] Mul [ [[Arg]] [[ConstP5]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:f\d+>> ParameterValue + // CHECK-DAG: <<ConstP5:f\d+>> FloatConstant 0.5 + // CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstP5>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: float Main.Div2(float) instruction_simplifier (after) // CHECK-NOT: Div @@ -976,16 +976,16 @@ public class Main { } // CHECK-START: double Main.Div2(double) instruction_simplifier (before) - // CHECK-DAG: [[Arg:d\d+]] ParameterValue - // CHECK-DAG: [[Const2:d\d+]] DoubleConstant 2 - // CHECK-DAG: [[Div:d\d+]] Div [ [[Arg]] [[Const2]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:d\d+>> ParameterValue + // CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 + // CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<Const2>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: double Main.Div2(double) instruction_simplifier (after) - // CHECK-DAG: [[Arg:d\d+]] ParameterValue - // CHECK-DAG: [[ConstP5:d\d+]] DoubleConstant 0.5 - // CHECK-DAG: [[Mul:d\d+]] Mul [ [[Arg]] [[ConstP5]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:d\d+>> ParameterValue + // CHECK-DAG: <<ConstP5:d\d+>> DoubleConstant 0.5 + // CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstP5>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: double Main.Div2(double) instruction_simplifier (after) // CHECK-NOT: Div @@ -994,16 +994,16 @@ public class Main { } // CHECK-START: float Main.DivMP25(float) instruction_simplifier (before) - // CHECK-DAG: [[Arg:f\d+]] ParameterValue - // CHECK-DAG: [[ConstMP25:f\d+]] FloatConstant -0.25 - // CHECK-DAG: [[Div:f\d+]] Div [ [[Arg]] [[ConstMP25]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:f\d+>> ParameterValue + // CHECK-DAG: <<ConstMP25:f\d+>> FloatConstant -0.25 + // CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<ConstMP25>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: float Main.DivMP25(float) instruction_simplifier (after) - // CHECK-DAG: [[Arg:f\d+]] ParameterValue - // CHECK-DAG: [[ConstM4:f\d+]] FloatConstant -4 - // CHECK-DAG: [[Mul:f\d+]] Mul [ [[Arg]] [[ConstM4]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:f\d+>> ParameterValue + // CHECK-DAG: <<ConstM4:f\d+>> FloatConstant -4 + // CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstM4>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: float Main.DivMP25(float) instruction_simplifier (after) // CHECK-NOT: Div @@ -1013,16 +1013,16 @@ public class Main { } // CHECK-START: double Main.DivMP25(double) instruction_simplifier (before) - // CHECK-DAG: [[Arg:d\d+]] ParameterValue - // CHECK-DAG: [[ConstMP25:d\d+]] DoubleConstant -0.25 - // CHECK-DAG: [[Div:d\d+]] Div [ [[Arg]] [[ConstMP25]] ] - // CHECK-DAG: Return [ [[Div]] ] + // CHECK-DAG: <<Arg:d\d+>> ParameterValue + // CHECK-DAG: <<ConstMP25:d\d+>> DoubleConstant -0.25 + // CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<ConstMP25>>] + // CHECK-DAG: Return [<<Div>>] // CHECK-START: double Main.DivMP25(double) instruction_simplifier (after) - // CHECK-DAG: [[Arg:d\d+]] ParameterValue - // CHECK-DAG: [[ConstM4:d\d+]] DoubleConstant -4 - // CHECK-DAG: [[Mul:d\d+]] Mul [ [[Arg]] [[ConstM4]] ] - // CHECK-DAG: Return [ [[Mul]] ] + // CHECK-DAG: <<Arg:d\d+>> ParameterValue + // CHECK-DAG: <<ConstM4:d\d+>> DoubleConstant -4 + // CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstM4>>] + // CHECK-DAG: Return [<<Mul>>] // CHECK-START: double Main.DivMP25(double) instruction_simplifier (after) // CHECK-NOT: Div diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc index f0b78e1f5e..a8ef684e93 100644 --- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc +++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc @@ -29,7 +29,9 @@ class TestVisitor : public StackVisitor { public: TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_value_(this_value), + found_method_index_(0) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java index d5563b85b3..3d583b49e8 100644 --- a/test/462-checker-inlining-across-dex-files/src/Main.java +++ b/test/462-checker-inlining-across-dex-files/src/Main.java @@ -22,7 +22,7 @@ class AAA { public class Main { // CHECK-START: void Main.inlineEmptyMethod() inliner (before) - // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect + // CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect // CHECK-DAG: ReturnVoid // CHECK-START: void Main.inlineEmptyMethod() inliner (after) @@ -33,120 +33,122 @@ public class Main { } // CHECK-START: int Main.inlineReturnIntMethod() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineReturnIntMethod() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: int Main.inlineReturnIntMethod() inliner (after) - // CHECK-DAG: [[Const38:i\d+]] IntConstant 38 - // CHECK-DAG: Return [ [[Const38]] ] + // CHECK-DAG: <<Const38:i\d+>> IntConstant 38 + // CHECK-DAG: Return [<<Const38>>] public static int inlineReturnIntMethod() { return OtherDex.returnIntMethod(); } // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] public static int dontInlineOtherDexStatic() { return OtherDex.returnOtherDexStatic(); } // CHECK-START: int Main.inlineMainStatic() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineMainStatic() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: int Main.inlineMainStatic() inliner (after) - // CHECK-DAG: [[Static:i\d+]] StaticFieldGet - // CHECK-DAG: Return [ [[Static]] ] + // CHECK-DAG: <<Static:i\d+>> StaticFieldGet + // CHECK-DAG: Return [<<Static>>] public static int inlineMainStatic() { return OtherDex.returnMainStatic(); } // CHECK-START: int Main.dontInlineRecursiveCall() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.dontInlineRecursiveCall() inliner (after) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] public static int dontInlineRecursiveCall() { return OtherDex.recursiveCall(); } // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] public static String dontInlineReturnString() { return OtherDex.returnString(); } // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] public static Class dontInlineOtherDexClass() { return OtherDex.returnOtherDexClass(); } // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after) - // CHECK-DAG: [[Class:l\d+]] LoadClass - // CHECK-DAG: Return [ [[Class]] ] + // CHECK-DAG: Return [<<Class:l\d+>>] + // CHECK-DAG: <<Class>> LoadClass + // Note: Verify backwards because there are two LoadClass instructions public static Class inlineMainClass() { return OtherDex.returnMainClass(); } // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] public static Class dontInlineOtherDexClassStaticCall() { return OtherDex.returnOtherDexClassStaticCall(); } // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before) - // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after) - // CHECK-DAG: [[Class:l\d+]] LoadClass - // CHECK-DAG: Return [ [[Class]] ] + // CHECK-DAG: Return [<<Class:l\d+>>] + // CHECK-DAG: <<Class>> LoadClass + // Note: Verify backwards because there are two LoadClass instructions public static Class inlineOtherDexCallingMain() { return OtherDex.returnOtherDexCallingMain(); diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index 4346103c19..e2374480c6 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -38,12 +38,12 @@ public class Main { */ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before) - // CHECK-DAG: [[Param:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: If [ [[Param]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Param:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: If [<<Param>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before) // CHECK: Goto @@ -52,10 +52,10 @@ public class Main { // CHECK-NOT: Goto // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after) - // CHECK-DAG: [[Param:z\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[NotParam:z\d+]] BooleanNot [ [[Param]] ] - // CHECK-DAG: Return [ [[NotParam]] ] + // CHECK-DAG: <<Param:z\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>] + // CHECK-DAG: Return [<<NotParam>>] // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after) // CHECK-NOT: If @@ -75,22 +75,22 @@ public class Main { */ // CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: If [ [[Cond]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const0]] [[Const1]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: If [<<Cond>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,<<Const1>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: Return [ [[Cond]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: Return [<<Cond>>] public static boolean GreaterThan(int x, int y) { return (x <= y) ? false : true; @@ -102,22 +102,22 @@ public class Main { */ // CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: If [ [[Cond]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: If [<<Cond>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Cond:z\d+]] LessThan [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: Return [ [[Cond]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<Cond:z\d+>> LessThan [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: Return [<<Cond>>] // CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after) // CHECK-NOT: GreaterThanOrEqual @@ -132,51 +132,51 @@ public class Main { */ // CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[CondXY:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: If [ [[CondXY]] ] - // CHECK-DAG: [[CondYZ:z\d+]] GreaterThan [ [[ParamY]] [[ParamZ]] ] - // CHECK-DAG: If [ [[CondYZ]] ] - // CHECK-DAG: [[CondXYZ:z\d+]] NotEqual [ [[PhiXY:i\d+]] [[PhiYZ:i\d+]] ] - // CHECK-DAG: If [ [[CondXYZ]] ] - // CHECK-DAG: Return [ [[PhiXYZ:i\d+]] ] - // CHECK-DAG: [[PhiXY]] Phi [ [[Const1]] [[Const0]] ] - // CHECK-DAG: [[PhiYZ]] Phi [ [[Const1]] [[Const0]] ] - // CHECK-DAG: [[PhiXYZ]] Phi [ [[Const1]] [[Const0]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<ParamZ:i\d+>> ParameterValue + // CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + // CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + // CHECK-DAG: <<CondXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: If [<<CondXY>>] + // CHECK-DAG: <<CondYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] + // CHECK-DAG: If [<<CondYZ>>] + // CHECK-DAG: <<CondXYZ:z\d+>> NotEqual [<<PhiXY:i\d+>>,<<PhiYZ:i\d+>>] + // CHECK-DAG: If [<<CondXYZ>>] + // CHECK-DAG: Return [<<PhiXYZ:i\d+>>] + // CHECK-DAG: <<PhiXY>> Phi [<<Const1>>,<<Const0>>] + // CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>] + // CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>] // CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after) - // CHECK-DAG: [[ParamX:i\d+]] ParameterValue - // CHECK-DAG: [[ParamY:i\d+]] ParameterValue - // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue - // CHECK-DAG: [[CmpXY:z\d+]] LessThanOrEqual [ [[ParamX]] [[ParamY]] ] - // CHECK-DAG: [[CmpYZ:z\d+]] LessThanOrEqual [ [[ParamY]] [[ParamZ]] ] - // CHECK-DAG: [[CmpXYZ:z\d+]] Equal [ [[CmpXY]] [[CmpYZ]] ] - // CHECK-DAG: Return [ [[CmpXYZ]] ] + // CHECK-DAG: <<ParamX:i\d+>> ParameterValue + // CHECK-DAG: <<ParamY:i\d+>> ParameterValue + // CHECK-DAG: <<ParamZ:i\d+>> ParameterValue + // CHECK-DAG: <<CmpXY:z\d+>> LessThanOrEqual [<<ParamX>>,<<ParamY>>] + // CHECK-DAG: <<CmpYZ:z\d+>> LessThanOrEqual [<<ParamY>>,<<ParamZ>>] + // CHECK-DAG: <<CmpXYZ:z\d+>> Equal [<<CmpXY>>,<<CmpYZ>>] + // CHECK-DAG: Return [<<CmpXYZ>>] public static boolean ValuesOrdered(int x, int y, int z) { return (x <= y) == (y <= z); } // CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (before) - // CHECK-DAG: [[Param:z\d+]] ParameterValue - // CHECK-DAG: [[Const42:i\d+]] IntConstant 42 - // CHECK-DAG: [[Const43:i\d+]] IntConstant 43 - // CHECK-DAG: [[NotParam:z\d+]] BooleanNot [ [[Param]] ] - // CHECK-DAG: If [ [[NotParam]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const42]] [[Const43]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Param:z\d+>> ParameterValue + // CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + // CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + // CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>] + // CHECK-DAG: If [<<NotParam>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after) - // CHECK-DAG: [[Param:z\d+]] ParameterValue - // CHECK-DAG: [[Const42:i\d+]] IntConstant 42 - // CHECK-DAG: [[Const43:i\d+]] IntConstant 43 - // CHECK-DAG: If [ [[Param]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const42]] [[Const43]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Param:z\d+>> ParameterValue + // CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + // CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + // CHECK-DAG: If [<<Param>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] + // CHECK-DAG: Return [<<Phi>>] // Note: The fact that branches are swapped is verified by running the test. diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java index 1b25b4257f..e451f703c2 100644 --- a/test/464-checker-inline-sharpen-calls/src/Main.java +++ b/test/464-checker-inline-sharpen-calls/src/Main.java @@ -20,7 +20,7 @@ public final class Main { } // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before) - // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect + // CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect // CHECK-DAG: ReturnVoid // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after) @@ -31,15 +31,15 @@ public final class Main { } // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before) - // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect - // CHECK-DAG: Return [ [[Invoke]] ] + // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + // CHECK-DAG: Return [<<Invoke>>] // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) // CHECK-NOT: InvokeStaticOrDirect // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) - // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet - // CHECK-DAG: Return [ [[Field]] ] + // CHECK-DAG: <<Field:i\d+>> InstanceFieldGet + // CHECK-DAG: Return [<<Field>>] public static int inlineSharpenStringInvoke() { return "Foo".length(); diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java index dcaef6fcfe..ac2863ceb4 100644 --- a/test/465-checker-clinit-gvn/src/Main.java +++ b/test/465-checker-clinit-gvn/src/Main.java @@ -27,14 +27,14 @@ class OtherClass { public final class Main { // CHECK-START: int Main.accessTwoStatics() GVN (before) - // CHECK-DAG: [[Class1:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class1]] ] - // CHECK-DAG: [[Class2:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class2]] ] + // CHECK-DAG: <<Class1:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class1>>] + // CHECK-DAG: <<Class2:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class2>>] // CHECK-START: int Main.accessTwoStatics() GVN (after) - // CHECK-DAG: [[Class:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class]] ] + // CHECK-DAG: <<Class:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class>>] // CHECK-NOT: ClinitCheck public static int accessTwoStatics() { @@ -42,14 +42,14 @@ public final class Main { } // CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (before) - // CHECK-DAG: [[Class1:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class1]] ] - // CHECK-DAG: [[Class2:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class2]] ] + // CHECK-DAG: <<Class1:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class1>>] + // CHECK-DAG: <<Class2:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class2>>] // CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (after) - // CHECK-DAG: [[Class:l\d+]] LoadClass - // CHECK-DAG: ClinitCheck [ [[Class]] ] + // CHECK-DAG: <<Class:l\d+>> LoadClass + // CHECK-DAG: ClinitCheck [<<Class>>] // CHECK-NOT: ClinitCheck public static int accessTwoStaticsCallInBetween() { diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc index 6715ba17e6..4724e8ebe4 100644 --- a/test/466-get-live-vreg/get_live_vreg_jni.cc +++ b/test/466-get-live-vreg/get_live_vreg_jni.cc @@ -28,7 +28,7 @@ namespace { class TestVisitor : public StackVisitor { public: TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context) {} + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali index f36304d333..33e6dc3d1e 100644 --- a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali +++ b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali @@ -18,6 +18,19 @@ .field public static value:Z +# CHECK-START: boolean TestCase.testCase() boolean_simplifier (before) +# CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +# CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Value:z\d+>> StaticFieldGet +# CHECK-DAG: If [<<Value>>] +# CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] +# CHECK-DAG: Return [<<Phi>>] + +# CHECK-START: boolean TestCase.testCase() boolean_simplifier (after) +# CHECK-DAG: <<Value:z\d+>> StaticFieldGet +# CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Value>>] +# CHECK-DAG: Return [<<Not>>] + .method public static testCase()Z .registers 2 sget-boolean v0, LTestCase;->value:Z diff --git a/test/468-checker-bool-simplifier-regression/src/Main.java b/test/468-checker-bool-simplifier-regression/src/Main.java index d45f3bfa16..8fe05c7a8a 100644 --- a/test/468-checker-bool-simplifier-regression/src/Main.java +++ b/test/468-checker-bool-simplifier-regression/src/Main.java @@ -18,19 +18,6 @@ import java.lang.reflect.*; public class Main { - // CHECK-START: boolean TestCase.testCase() boolean_simplifier (before) - // CHECK-DAG: [[Const0:i\d+]] IntConstant 0 - // CHECK-DAG: [[Const1:i\d+]] IntConstant 1 - // CHECK-DAG: [[Value:z\d+]] StaticFieldGet - // CHECK-DAG: If [ [[Value]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ] - // CHECK-DAG: Return [ [[Phi]] ] - - // CHECK-START: boolean TestCase.testCase() boolean_simplifier (after) - // CHECK-DAG: [[Value:z\d+]] StaticFieldGet - // CHECK-DAG: [[Not:z\d+]] BooleanNot [ [[Value]] ] - // CHECK-DAG: Return [ [[Not]] ] - public static boolean runTest(boolean input) throws Exception { Class<?> c = Class.forName("TestCase"); Method m = c.getMethod("testCase"); diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java index 9151986ca2..490f7f9cbc 100644 --- a/test/474-checker-boolean-input/src/Main.java +++ b/test/474-checker-boolean-input/src/Main.java @@ -28,8 +28,8 @@ public class Main { */ // CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after) - // CHECK-DAG: [[Phi:i\d+]] Phi - // CHECK-DAG: BooleanNot [ [[Phi]] ] + // CHECK-DAG: <<Phi:i\d+>> Phi + // CHECK-DAG: BooleanNot [<<Phi>>] public static boolean f1; public static boolean f2; @@ -48,8 +48,8 @@ public class Main { */ // CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after) - // CHECK-DAG: [[And:i\d+]] And - // CHECK-DAG: BooleanNot [ [[And]] ] + // CHECK-DAG: <<And:i\d+>> And + // CHECK-DAG: BooleanNot [<<And>>] public static boolean InlineAnd(boolean x, boolean y) { return x & y; @@ -65,8 +65,8 @@ public class Main { */ // CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after) - // CHECK-DAG: [[Or:i\d+]] Or - // CHECK-DAG: BooleanNot [ [[Or]] ] + // CHECK-DAG: <<Or:i\d+>> Or + // CHECK-DAG: BooleanNot [<<Or>>] public static boolean InlineOr(boolean x, boolean y) { return x | y; @@ -82,8 +82,8 @@ public class Main { */ // CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after) - // CHECK-DAG: [[Xor:i\d+]] Xor - // CHECK-DAG: BooleanNot [ [[Xor]] ] + // CHECK-DAG: <<Xor:i\d+>> Xor + // CHECK-DAG: BooleanNot [<<Xor>>] public static boolean InlineXor(boolean x, boolean y) { return x ^ y; diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java index 10aa2ab164..769ae208d9 100644 --- a/test/476-checker-ctor-memory-barrier/src/Main.java +++ b/test/476-checker-ctor-memory-barrier/src/Main.java @@ -17,7 +17,7 @@ class ClassWithoutFinals { // CHECK-START: void ClassWithoutFinals.<init>() register (after) - // CHECK-NOT: MemoryBarrier {{StoreStore}} + // CHECK-NOT: MemoryBarrier kind:StoreStore public ClassWithoutFinals() {} } @@ -26,7 +26,7 @@ class ClassWithFinals { public ClassWithFinals obj; // CHECK-START: void ClassWithFinals.<init>(boolean) register (after) - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid public ClassWithFinals(boolean cond) { @@ -38,7 +38,7 @@ class ClassWithFinals { } // CHECK-START: void ClassWithFinals.<init>() register (after) - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid public ClassWithFinals() { @@ -46,8 +46,8 @@ class ClassWithFinals { } // CHECK-START: void ClassWithFinals.<init>(int) register (after) - // CHECK: MemoryBarrier {{StoreStore}} - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid public ClassWithFinals(int x) { @@ -61,7 +61,7 @@ class ClassWithFinals { class InheritFromClassWithFinals extends ClassWithFinals { // CHECK-START: void InheritFromClassWithFinals.<init>() register (after) - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid @@ -75,7 +75,7 @@ class InheritFromClassWithFinals extends ClassWithFinals { // CHECK: InvokeStaticOrDirect // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after) - // CHECK-NOT: MemoryBarrier {{StoreStore}} + // CHECK-NOT: MemoryBarrier kind:StoreStore public InheritFromClassWithFinals(boolean cond) { super(cond); // should not inline the super constructor @@ -86,8 +86,8 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { final int y; // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after) - // CHECK: MemoryBarrier {{StoreStore}} - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid @@ -100,7 +100,7 @@ class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals { // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after) // CHECK: InvokeStaticOrDirect - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: ReturnVoid public HaveFinalsAndInheritFromClassWithFinals(boolean cond) { @@ -116,13 +116,13 @@ public class Main { // CHECK: InvokeStaticOrDirect // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after) - // CHECK-NOT: MemoryBarrier {{StoreStore}} + // CHECK-NOT: MemoryBarrier kind:StoreStore public static ClassWithFinals noInlineNoConstructorBarrier() { return new ClassWithFinals(false); } // CHECK-START: ClassWithFinals Main.inlineConstructorBarrier() register (after) - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: Return @@ -133,7 +133,7 @@ public class Main { } // CHECK-START: InheritFromClassWithFinals Main.doubleInlineConstructorBarrier() register (after) - // CHECK: MemoryBarrier {{StoreStore}} + // CHECK: MemoryBarrier kind:StoreStore // CHECK-NOT: {{.*}} // CHECK: Return diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java index 6da8945c43..61199a719b 100644 --- a/test/478-checker-clinit-check-pruning/src/Main.java +++ b/test/478-checker-clinit-check-pruning/src/Main.java @@ -24,13 +24,13 @@ public class Main { */ // CHECK-START: void Main.invokeStaticInlined() builder (after) - // CHECK-DAG: [[LoadClass:l\d+]] LoadClass - // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ] - // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ] + // CHECK-DAG: <<LoadClass:l\d+>> LoadClass + // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] + // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>] // CHECK-START: void Main.invokeStaticInlined() inliner (after) - // CHECK-DAG: [[LoadClass:l\d+]] LoadClass - // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ] + // CHECK-DAG: <<LoadClass:l\d+>> LoadClass + // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] // CHECK-START: void Main.invokeStaticInlined() inliner (after) // CHECK-NOT: InvokeStaticOrDirect @@ -67,14 +67,14 @@ public class Main { */ // CHECK-START: void Main.invokeStaticNotInlined() builder (after) - // CHECK-DAG: [[LoadClass:l\d+]] LoadClass - // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ] - // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ] + // CHECK-DAG: <<LoadClass:l\d+>> LoadClass + // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] + // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>] // CHECK-START: void Main.invokeStaticNotInlined() inliner (after) - // CHECK-DAG: [[LoadClass:l\d+]] LoadClass - // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ] - // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ] + // CHECK-DAG: <<LoadClass:l\d+>> LoadClass + // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] + // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>] // The following checks ensure the clinit check and load class // instructions added by the builder are pruned by the diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java index 83dbb26898..b76755e07c 100644 --- a/test/480-checker-dead-blocks/src/Main.java +++ b/test/480-checker-dead-blocks/src/Main.java @@ -31,19 +31,19 @@ public class Main { } // CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (before) - // CHECK-DAG: [[ArgX:i\d+]] ParameterValue - // CHECK-DAG: [[ArgY:i\d+]] ParameterValue + // CHECK-DAG: <<ArgX:i\d+>> ParameterValue + // CHECK-DAG: <<ArgY:i\d+>> ParameterValue // CHECK-DAG: If - // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after) - // CHECK-DAG: [[ArgX:i\d+]] ParameterValue - // CHECK-DAG: [[ArgY:i\d+]] ParameterValue - // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: Return [ [[Add]] ] + // CHECK-DAG: <<ArgX:i\d+>> ParameterValue + // CHECK-DAG: <<ArgY:i\d+>> ParameterValue + // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: Return [<<Add>>] // CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after) // CHECK-NOT: If @@ -61,19 +61,19 @@ public class Main { } // CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (before) - // CHECK-DAG: [[ArgX:i\d+]] ParameterValue - // CHECK-DAG: [[ArgY:i\d+]] ParameterValue + // CHECK-DAG: <<ArgX:i\d+>> ParameterValue + // CHECK-DAG: <<ArgY:i\d+>> ParameterValue // CHECK-DAG: If - // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ] - // CHECK-DAG: Return [ [[Phi]] ] + // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] + // CHECK-DAG: Return [<<Phi>>] // CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after) - // CHECK-DAG: [[ArgX:i\d+]] ParameterValue - // CHECK-DAG: [[ArgY:i\d+]] ParameterValue - // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ] - // CHECK-DAG: Return [ [[Sub]] ] + // CHECK-DAG: <<ArgX:i\d+>> ParameterValue + // CHECK-DAG: <<ArgY:i\d+>> ParameterValue + // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] + // CHECK-DAG: Return [<<Sub>>] // CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after) // CHECK-NOT: If @@ -125,8 +125,8 @@ public class Main { // CHECK-DAG: Add // CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after) // CHECK-NOT: If @@ -145,8 +145,8 @@ public class Main { // CHECK-DAG: Add // CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after) - // CHECK-DAG: [[Arg:i\d+]] ParameterValue - // CHECK-DAG: Return [ [[Arg]] ] + // CHECK-DAG: <<Arg:i\d+>> ParameterValue + // CHECK-DAG: Return [<<Arg>>] // CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after) // CHECK-NOT: If diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 74184e8252..9e1076a851 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -18,16 +18,16 @@ public class Main { // CHECK-START: void Main.loop1(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 22) }, uses: { 17 22 } - // CHECK: Goto (liveness: 20) + // CHECK: ParameterValue liveness:2 ranges:[2-22] uses:[17,22] + // CHECK: Goto liveness:20 public static void loop1(boolean incoming) { while (incoming) {} } // CHECK-START: void Main.loop2(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 42) }, uses: { 33 38 42 } - // CHECK: Goto (liveness: 36) - // CHECK: Goto (liveness: 40) + // CHECK: ParameterValue liveness:2 ranges:[2-42] uses:[33,38,42] + // CHECK: Goto liveness:36 + // CHECK: Goto liveness:40 public static void loop2(boolean incoming) { while (true) { System.out.println("foo"); @@ -36,11 +36,11 @@ public class Main { } // CHECK-START: void Main.loop3(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 60) }, uses: { 56 60 } - // CHECK: Goto (liveness: 58) + // CHECK: ParameterValue liveness:2 ranges:[2-60] uses:[56,60] + // CHECK: Goto liveness:58 // CHECK-START: void Main.loop3(boolean) liveness (after) - // CHECK-NOT: Goto (liveness: 54) + // CHECK-NOT: Goto liveness:54 public static void loop3(boolean incoming) { // 'incoming' only needs a use at the outer loop's back edge. while (System.currentTimeMillis() != 42) { @@ -50,10 +50,10 @@ public class Main { } // CHECK-START: void Main.loop4(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 22) }, uses: { 22 } + // CHECK: ParameterValue liveness:2 ranges:[2-22] uses:[22] // CHECK-START: void Main.loop4(boolean) liveness (after) - // CHECK-NOT: Goto (liveness: 20) + // CHECK-NOT: Goto liveness:20 public static void loop4(boolean incoming) { // 'incoming' has no loop use, so should not have back edge uses. System.out.println(incoming); @@ -63,9 +63,9 @@ public class Main { } // CHECK-START: void Main.loop5(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 50) }, uses: { 33 42 46 50 } - // CHECK: Goto (liveness: 44) - // CHECK: Goto (liveness: 48) + // CHECK: ParameterValue liveness:2 ranges:[2-50] uses:[33,42,46,50] + // CHECK: Goto liveness:44 + // CHECK: Goto liveness:48 public static void loop5(boolean incoming) { // 'incoming' must have a use at both back edges. while (Runtime.getRuntime() != null) { @@ -76,11 +76,11 @@ public class Main { } // CHECK-START: void Main.loop6(boolean) liveness (after) - // CHECK ParameterValue (liveness: 2 ranges: { [2, 46) }, uses: { 24 46 } - // CHECK: Goto (liveness: 44) + // CHECK ParameterValue liveness:2 ranges:[2-46] uses:[24,46] + // CHECK: Goto liveness:44 // CHECK-START: void Main.loop6(boolean) liveness (after) - // CHECK-NOT: Goto (liveness: 22) + // CHECK-NOT: Goto liveness:22 public static void loop6(boolean incoming) { // 'incoming' must have a use only at the first loop's back edge. while (true) { @@ -90,9 +90,9 @@ public class Main { } // CHECK-START: void Main.loop7(boolean) liveness (after) - // CHECK: ParameterValue (liveness: 2 ranges: { [2, 50) }, uses: { 32 41 46 50 } - // CHECK: Goto (liveness: 44) - // CHECK: Goto (liveness: 48) + // CHECK: ParameterValue liveness:2 ranges:[2-50] uses:[32,41,46,50] + // CHECK: Goto liveness:44 + // CHECK: Goto liveness:48 public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. while (Runtime.getRuntime() != null) { @@ -102,9 +102,9 @@ public class Main { } // CHECK-START: void Main.loop8() liveness (after) - // CHECK: StaticFieldGet (liveness: 12 ranges: { [12, 44) }, uses: { 35 40 44 } - // CHECK: Goto (liveness: 38) - // CHECK: Goto (liveness: 42) + // CHECK: StaticFieldGet liveness:12 ranges:[12-44] uses:[35,40,44] + // CHECK: Goto liveness:38 + // CHECK: Goto liveness:42 public static void loop8() { // 'incoming' must have a use at both back edges. boolean incoming = field; @@ -114,8 +114,8 @@ public class Main { } // CHECK-START: void Main.loop9() liveness (after) - // CHECK: StaticFieldGet (liveness: 22 ranges: { [22, 36) }, uses: { 31 36 } - // CHECK: Goto (liveness: 38) + // CHECK: StaticFieldGet liveness:22 ranges:[22-36] uses:[31,36] + // CHECK: Goto liveness:38 public static void loop9() { while (Runtime.getRuntime() != null) { // 'incoming' must only have a use in the inner loop. diff --git a/test/485-checker-dce-loop-update/expected.txt b/test/485-checker-dce-loop-update/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/485-checker-dce-loop-update/expected.txt diff --git a/test/485-checker-dce-loop-update/info.txt b/test/485-checker-dce-loop-update/info.txt new file mode 100644 index 0000000000..fccf10cc8e --- /dev/null +++ b/test/485-checker-dce-loop-update/info.txt @@ -0,0 +1,2 @@ +Tests loop information update after DCE because block removal can disconnect loops, leaving other +live blocks outside the loop they had been a member of.
\ No newline at end of file diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali new file mode 100644 index 0000000000..487a5dfbff --- /dev/null +++ b/test/485-checker-dce-loop-update/smali/TestCase.smali @@ -0,0 +1,274 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.method public static $inline$True()Z + .registers 1 + const/4 v0, 1 + return v0 +.end method + + +# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (before) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: Return [<<PhiX>>] loop:none + +# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (after) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<AddX:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: <<AddX>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: Return [<<PhiX>>] loop:none + +.method public static testSingleExit(IZ)I + .registers 3 + + # p0 = int X + # p1 = boolean Y + # v0 = true + + invoke-static {}, LTestCase;->$inline$True()Z + move-result v0 + + :loop_start + if-eqz p1, :loop_body # cannot be determined statically + if-nez v0, :loop_end # will always exit + + # Dead block + add-int/lit8 p0, p0, 5 + goto :loop_start + + # Live block + :loop_body + add-int/lit8 p0, p0, 7 + goto :loop_start + + :loop_end + return p0 +.end method + + +# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (before) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: Return [<<PhiX>>] loop:none + +# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (after) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<ArgZ>>] loop:none +# CHECK-DAG: Return [<<PhiX>>] loop:none + +.method public static testMultipleExits(IZZ)I + .registers 4 + + # p0 = int X + # p1 = boolean Y + # p2 = boolean Z + # v0 = true + + invoke-static {}, LTestCase;->$inline$True()Z + move-result v0 + + :loop_start + if-eqz p1, :loop_body # cannot be determined statically + if-nez p2, :loop_end # may exit + if-nez v0, :loop_end # will always exit + + # Dead block + add-int/lit8 p0, p0, 5 + goto :loop_start + + # Live block + :loop_body + add-int/lit8 p0, p0, 7 + goto :loop_start + + :loop_end + return p0 +.end method + + +# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (before) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<Cst9:i\d+>> IntConstant 9 +# CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>> +# CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst9>>] loop:<<HeaderY>> +# CHECK-DAG: <<PhiX2:i\d+>> Phi [<<Mul9>>,<<PhiX1>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add5>> Add [<<PhiX2>>,<<Cst5>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: Return [<<PhiX2>>] loop:none + +# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# CHECK-DAG: <<Cst9:i\d+>> IntConstant 9 +# CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<ArgZ>>] loop:none +# CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst9>>] loop:none +# CHECK-DAG: <<PhiX2:i\d+>> Phi [<<Mul9>>,<<PhiX1>>] loop:none +# CHECK-DAG: Return [<<PhiX2>>] loop:none + +.method public static testExitPredecessors(IZZ)I + .registers 4 + + # p0 = int X + # p1 = boolean Y + # p2 = boolean Z + # v0 = true + + invoke-static {}, LTestCase;->$inline$True()Z + move-result v0 + + :loop_start + if-eqz p1, :loop_body # cannot be determined statically + + # Additional logic which will end up outside the loop + if-eqz p2, :skip_if + mul-int/lit8 p0, p0, 9 + :skip_if + + if-nez v0, :loop_end # will always take the branch + + # Dead block + add-int/lit8 p0, p0, 5 + goto :loop_start + + # Live block + :loop_body + add-int/lit8 p0, p0, 7 + goto :loop_start + + :loop_end + return p0 +.end method + + +# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (before) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst0:i\d+>> IntConstant 0 +# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: <<PhiZ1:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>,<<PhiZ1>>] loop:<<HeaderY>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# +# ### Inner loop ### +# CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<XorZ>>] loop:<<HeaderZ:B\d+>> +# CHECK-DAG: <<XorZ>> Xor [<<PhiZ2>>,<<Cst1>>] loop:<<HeaderZ>> +# CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>> +# CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>> +# +# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# CHECK-DAG: Return [<<PhiX>>] loop:none + +# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (after) +# CHECK-DAG: <<ArgX:i\d+>> ParameterValue +# CHECK-DAG: <<ArgY:z\d+>> ParameterValue +# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue +# CHECK-DAG: <<Cst0:i\d+>> IntConstant 0 +# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 +# +# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> +# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +# +# ### Inner loop ### +# CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>] loop:<<HeaderZ:B\d+>> +# CHECK-DAG: <<XorZ>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>> +# CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>> +# CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>> +# +# CHECK-DAG: Return [<<PhiX>>] loop:none + +.method public static testInnerLoop(IZZ)I + .registers 4 + + # p0 = int X + # p1 = boolean Y + # p2 = boolean Z + # v0 = true + + invoke-static {}, LTestCase;->$inline$True()Z + move-result v0 + + :loop_start + if-eqz p1, :loop_body # cannot be determined statically + + # Inner loop which will end up outside its parent + :inner_loop_start + xor-int/lit8 p2, p2, 1 + if-eqz p2, :inner_loop_start + + if-nez v0, :loop_end # will always take the branch + + # Dead block + add-int/lit8 p0, p0, 5 + goto :loop_start + + # Live block + :loop_body + add-int/lit8 p0, p0, 7 + goto :loop_start + + :loop_end + return p0 +.end method diff --git a/test/485-checker-dce-loop-update/src/Main.java b/test/485-checker-dce-loop-update/src/Main.java new file mode 100644 index 0000000000..6bfe08b0d3 --- /dev/null +++ b/test/485-checker-dce-loop-update/src/Main.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + return; + } +} diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build new file mode 100644 index 0000000000..1dc8452d91 --- /dev/null +++ b/test/701-easy-div-rem/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Stop if something fails. +set -e + +# Write out the source file. +mkdir src +python ./genMain.py + +# Increase the file size limitation for classes.lst as the machine generated +# source file contains a lot of methods and is quite large. +ulimit -S 4096 + +./default-build diff --git a/test/701-easy-div-rem/genMain.py b/test/701-easy-div-rem/genMain.py index 80eac34463..75eee17fe6 100644 --- a/test/701-easy-div-rem/genMain.py +++ b/test/701-easy-div-rem/genMain.py @@ -12,15 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +upper_bound_int_pow2 = 31 +upper_bound_long_pow2 = 63 +upper_bound_constant = 100 all_tests = [ ({'@INT@': 'int', '@SUFFIX@':''}, - [('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(31)]), - ('CheckDiv', 'idiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]), - ('CheckRem', 'irem_by_pow2_', [2**i for i in range(31)])]), + [('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]), + ('CheckDiv', 'idiv_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]), + ('CheckDiv', 'idiv_by_constant_', [i for i in range(1, upper_bound_constant)]), + ('CheckDiv', 'idiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]), + ('CheckRem', 'irem_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]), + ('CheckRem', 'irem_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]), + ('CheckRem', 'irem_by_constant_', [i for i in range(1, upper_bound_constant)]), + ('CheckRem', 'irem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])]), ({'@INT@': 'long', '@SUFFIX@': 'l'}, - [('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(63)]), - ('CheckDiv', 'ldiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]), - ('CheckRem', 'lrem_by_pow2_', [2**i for i in range(63)])]) + [('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]), + ('CheckDiv', 'ldiv_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]), + ('CheckDiv', 'ldiv_by_constant_', [i for i in range(1, upper_bound_constant)]), + ('CheckDiv', 'ldiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]), + ('CheckRem', 'lrem_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]), + ('CheckRem', 'lrem_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]), + ('CheckRem', 'lrem_by_constant_', [i for i in range(1, upper_bound_constant)]), + ('CheckRem', 'lrem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])]) ] def subst_vars(variables, text): diff --git a/test/701-easy-div-rem/src/Main.java b/test/701-easy-div-rem/src/Main.java deleted file mode 100644 index f995f61953..0000000000 --- a/test/701-easy-div-rem/src/Main.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -public class Main { - public static int num_errors = 0; - - public static void reportError(String message) { - if (num_errors == 10) { - System.out.println("Omitting other error messages..."); - } else if (num_errors < 10) { - System.out.println(message); - } - num_errors += 1; - } - - public static void intCheckDiv(String desc, int result, int dividend, int divisor) { - int correct_result = dividend / divisor; - if (result != correct_result) { - reportError(desc + "(" + dividend + ") == " + result + - " should be " + correct_result); - } - } - public static void intCheckRem(String desc, int result, int dividend, int divisor) { - int correct_result = dividend % divisor; - if (result != correct_result) { - reportError(desc + "(" + dividend + ") == " + result + - " should be " + correct_result); - } - } - public static void longCheckDiv(String desc, long result, long dividend, long divisor) { - long correct_result = dividend / divisor; - if (result != correct_result) { - reportError(desc + "(" + dividend + ") == " + result + - " should be " + correct_result); - } - } - public static void longCheckRem(String desc, long result, long dividend, long divisor) { - long correct_result = dividend % divisor; - if (result != correct_result) { - reportError(desc + "(" + dividend + ") == " + result + - " should be " + correct_result); - } - } - - public static int idiv_by_pow2_0(int x) {return x / 1;} - public static int idiv_by_pow2_1(int x) {return x / 2;} - public static int idiv_by_pow2_2(int x) {return x / 4;} - public static int idiv_by_pow2_3(int x) {return x / 8;} - public static int idiv_by_pow2_4(int x) {return x / 16;} - public static int idiv_by_pow2_5(int x) {return x / 32;} - public static int idiv_by_pow2_6(int x) {return x / 64;} - public static int idiv_by_pow2_7(int x) {return x / 128;} - public static int idiv_by_pow2_8(int x) {return x / 256;} - public static int idiv_by_pow2_9(int x) {return x / 512;} - public static int idiv_by_pow2_10(int x) {return x / 1024;} - public static int idiv_by_pow2_11(int x) {return x / 2048;} - public static int idiv_by_pow2_12(int x) {return x / 4096;} - public static int idiv_by_pow2_13(int x) {return x / 8192;} - public static int idiv_by_pow2_14(int x) {return x / 16384;} - public static int idiv_by_pow2_15(int x) {return x / 32768;} - public static int idiv_by_pow2_16(int x) {return x / 65536;} - public static int idiv_by_pow2_17(int x) {return x / 131072;} - public static int idiv_by_pow2_18(int x) {return x / 262144;} - public static int idiv_by_pow2_19(int x) {return x / 524288;} - public static int idiv_by_pow2_20(int x) {return x / 1048576;} - public static int idiv_by_pow2_21(int x) {return x / 2097152;} - public static int idiv_by_pow2_22(int x) {return x / 4194304;} - public static int idiv_by_pow2_23(int x) {return x / 8388608;} - public static int idiv_by_pow2_24(int x) {return x / 16777216;} - public static int idiv_by_pow2_25(int x) {return x / 33554432;} - public static int idiv_by_pow2_26(int x) {return x / 67108864;} - public static int idiv_by_pow2_27(int x) {return x / 134217728;} - public static int idiv_by_pow2_28(int x) {return x / 268435456;} - public static int idiv_by_pow2_29(int x) {return x / 536870912;} - public static int idiv_by_pow2_30(int x) {return x / 1073741824;} - public static int idiv_by_small_0(int x) {return x / 3;} - public static int idiv_by_small_1(int x) {return x / 5;} - public static int idiv_by_small_2(int x) {return x / 6;} - public static int idiv_by_small_3(int x) {return x / 7;} - public static int idiv_by_small_4(int x) {return x / 9;} - public static int idiv_by_small_5(int x) {return x / 10;} - public static int idiv_by_small_6(int x) {return x / 11;} - public static int idiv_by_small_7(int x) {return x / 12;} - public static int idiv_by_small_8(int x) {return x / 13;} - public static int idiv_by_small_9(int x) {return x / 14;} - public static int idiv_by_small_10(int x) {return x / 15;} - public static int irem_by_pow2_0(int x) {return x % 1;} - public static int irem_by_pow2_1(int x) {return x % 2;} - public static int irem_by_pow2_2(int x) {return x % 4;} - public static int irem_by_pow2_3(int x) {return x % 8;} - public static int irem_by_pow2_4(int x) {return x % 16;} - public static int irem_by_pow2_5(int x) {return x % 32;} - public static int irem_by_pow2_6(int x) {return x % 64;} - public static int irem_by_pow2_7(int x) {return x % 128;} - public static int irem_by_pow2_8(int x) {return x % 256;} - public static int irem_by_pow2_9(int x) {return x % 512;} - public static int irem_by_pow2_10(int x) {return x % 1024;} - public static int irem_by_pow2_11(int x) {return x % 2048;} - public static int irem_by_pow2_12(int x) {return x % 4096;} - public static int irem_by_pow2_13(int x) {return x % 8192;} - public static int irem_by_pow2_14(int x) {return x % 16384;} - public static int irem_by_pow2_15(int x) {return x % 32768;} - public static int irem_by_pow2_16(int x) {return x % 65536;} - public static int irem_by_pow2_17(int x) {return x % 131072;} - public static int irem_by_pow2_18(int x) {return x % 262144;} - public static int irem_by_pow2_19(int x) {return x % 524288;} - public static int irem_by_pow2_20(int x) {return x % 1048576;} - public static int irem_by_pow2_21(int x) {return x % 2097152;} - public static int irem_by_pow2_22(int x) {return x % 4194304;} - public static int irem_by_pow2_23(int x) {return x % 8388608;} - public static int irem_by_pow2_24(int x) {return x % 16777216;} - public static int irem_by_pow2_25(int x) {return x % 33554432;} - public static int irem_by_pow2_26(int x) {return x % 67108864;} - public static int irem_by_pow2_27(int x) {return x % 134217728;} - public static int irem_by_pow2_28(int x) {return x % 268435456;} - public static int irem_by_pow2_29(int x) {return x % 536870912;} - public static int irem_by_pow2_30(int x) {return x % 1073741824;} - public static long ldiv_by_pow2_0(long x) {return x / 1l;} - public static long ldiv_by_pow2_1(long x) {return x / 2l;} - public static long ldiv_by_pow2_2(long x) {return x / 4l;} - public static long ldiv_by_pow2_3(long x) {return x / 8l;} - public static long ldiv_by_pow2_4(long x) {return x / 16l;} - public static long ldiv_by_pow2_5(long x) {return x / 32l;} - public static long ldiv_by_pow2_6(long x) {return x / 64l;} - public static long ldiv_by_pow2_7(long x) {return x / 128l;} - public static long ldiv_by_pow2_8(long x) {return x / 256l;} - public static long ldiv_by_pow2_9(long x) {return x / 512l;} - public static long ldiv_by_pow2_10(long x) {return x / 1024l;} - public static long ldiv_by_pow2_11(long x) {return x / 2048l;} - public static long ldiv_by_pow2_12(long x) {return x / 4096l;} - public static long ldiv_by_pow2_13(long x) {return x / 8192l;} - public static long ldiv_by_pow2_14(long x) {return x / 16384l;} - public static long ldiv_by_pow2_15(long x) {return x / 32768l;} - public static long ldiv_by_pow2_16(long x) {return x / 65536l;} - public static long ldiv_by_pow2_17(long x) {return x / 131072l;} - public static long ldiv_by_pow2_18(long x) {return x / 262144l;} - public static long ldiv_by_pow2_19(long x) {return x / 524288l;} - public static long ldiv_by_pow2_20(long x) {return x / 1048576l;} - public static long ldiv_by_pow2_21(long x) {return x / 2097152l;} - public static long ldiv_by_pow2_22(long x) {return x / 4194304l;} - public static long ldiv_by_pow2_23(long x) {return x / 8388608l;} - public static long ldiv_by_pow2_24(long x) {return x / 16777216l;} - public static long ldiv_by_pow2_25(long x) {return x / 33554432l;} - public static long ldiv_by_pow2_26(long x) {return x / 67108864l;} - public static long ldiv_by_pow2_27(long x) {return x / 134217728l;} - public static long ldiv_by_pow2_28(long x) {return x / 268435456l;} - public static long ldiv_by_pow2_29(long x) {return x / 536870912l;} - public static long ldiv_by_pow2_30(long x) {return x / 1073741824l;} - public static long ldiv_by_pow2_31(long x) {return x / 2147483648l;} - public static long ldiv_by_pow2_32(long x) {return x / 4294967296l;} - public static long ldiv_by_pow2_33(long x) {return x / 8589934592l;} - public static long ldiv_by_pow2_34(long x) {return x / 17179869184l;} - public static long ldiv_by_pow2_35(long x) {return x / 34359738368l;} - public static long ldiv_by_pow2_36(long x) {return x / 68719476736l;} - public static long ldiv_by_pow2_37(long x) {return x / 137438953472l;} - public static long ldiv_by_pow2_38(long x) {return x / 274877906944l;} - public static long ldiv_by_pow2_39(long x) {return x / 549755813888l;} - public static long ldiv_by_pow2_40(long x) {return x / 1099511627776l;} - public static long ldiv_by_pow2_41(long x) {return x / 2199023255552l;} - public static long ldiv_by_pow2_42(long x) {return x / 4398046511104l;} - public static long ldiv_by_pow2_43(long x) {return x / 8796093022208l;} - public static long ldiv_by_pow2_44(long x) {return x / 17592186044416l;} - public static long ldiv_by_pow2_45(long x) {return x / 35184372088832l;} - public static long ldiv_by_pow2_46(long x) {return x / 70368744177664l;} - public static long ldiv_by_pow2_47(long x) {return x / 140737488355328l;} - public static long ldiv_by_pow2_48(long x) {return x / 281474976710656l;} - public static long ldiv_by_pow2_49(long x) {return x / 562949953421312l;} - public static long ldiv_by_pow2_50(long x) {return x / 1125899906842624l;} - public static long ldiv_by_pow2_51(long x) {return x / 2251799813685248l;} - public static long ldiv_by_pow2_52(long x) {return x / 4503599627370496l;} - public static long ldiv_by_pow2_53(long x) {return x / 9007199254740992l;} - public static long ldiv_by_pow2_54(long x) {return x / 18014398509481984l;} - public static long ldiv_by_pow2_55(long x) {return x / 36028797018963968l;} - public static long ldiv_by_pow2_56(long x) {return x / 72057594037927936l;} - public static long ldiv_by_pow2_57(long x) {return x / 144115188075855872l;} - public static long ldiv_by_pow2_58(long x) {return x / 288230376151711744l;} - public static long ldiv_by_pow2_59(long x) {return x / 576460752303423488l;} - public static long ldiv_by_pow2_60(long x) {return x / 1152921504606846976l;} - public static long ldiv_by_pow2_61(long x) {return x / 2305843009213693952l;} - public static long ldiv_by_pow2_62(long x) {return x / 4611686018427387904l;} - public static long ldiv_by_small_0(long x) {return x / 3l;} - public static long ldiv_by_small_1(long x) {return x / 5l;} - public static long ldiv_by_small_2(long x) {return x / 6l;} - public static long ldiv_by_small_3(long x) {return x / 7l;} - public static long ldiv_by_small_4(long x) {return x / 9l;} - public static long ldiv_by_small_5(long x) {return x / 10l;} - public static long ldiv_by_small_6(long x) {return x / 11l;} - public static long ldiv_by_small_7(long x) {return x / 12l;} - public static long ldiv_by_small_8(long x) {return x / 13l;} - public static long ldiv_by_small_9(long x) {return x / 14l;} - public static long ldiv_by_small_10(long x) {return x / 15l;} - public static long lrem_by_pow2_0(long x) {return x % 1l;} - public static long lrem_by_pow2_1(long x) {return x % 2l;} - public static long lrem_by_pow2_2(long x) {return x % 4l;} - public static long lrem_by_pow2_3(long x) {return x % 8l;} - public static long lrem_by_pow2_4(long x) {return x % 16l;} - public static long lrem_by_pow2_5(long x) {return x % 32l;} - public static long lrem_by_pow2_6(long x) {return x % 64l;} - public static long lrem_by_pow2_7(long x) {return x % 128l;} - public static long lrem_by_pow2_8(long x) {return x % 256l;} - public static long lrem_by_pow2_9(long x) {return x % 512l;} - public static long lrem_by_pow2_10(long x) {return x % 1024l;} - public static long lrem_by_pow2_11(long x) {return x % 2048l;} - public static long lrem_by_pow2_12(long x) {return x % 4096l;} - public static long lrem_by_pow2_13(long x) {return x % 8192l;} - public static long lrem_by_pow2_14(long x) {return x % 16384l;} - public static long lrem_by_pow2_15(long x) {return x % 32768l;} - public static long lrem_by_pow2_16(long x) {return x % 65536l;} - public static long lrem_by_pow2_17(long x) {return x % 131072l;} - public static long lrem_by_pow2_18(long x) {return x % 262144l;} - public static long lrem_by_pow2_19(long x) {return x % 524288l;} - public static long lrem_by_pow2_20(long x) {return x % 1048576l;} - public static long lrem_by_pow2_21(long x) {return x % 2097152l;} - public static long lrem_by_pow2_22(long x) {return x % 4194304l;} - public static long lrem_by_pow2_23(long x) {return x % 8388608l;} - public static long lrem_by_pow2_24(long x) {return x % 16777216l;} - public static long lrem_by_pow2_25(long x) {return x % 33554432l;} - public static long lrem_by_pow2_26(long x) {return x % 67108864l;} - public static long lrem_by_pow2_27(long x) {return x % 134217728l;} - public static long lrem_by_pow2_28(long x) {return x % 268435456l;} - public static long lrem_by_pow2_29(long x) {return x % 536870912l;} - public static long lrem_by_pow2_30(long x) {return x % 1073741824l;} - public static long lrem_by_pow2_31(long x) {return x % 2147483648l;} - public static long lrem_by_pow2_32(long x) {return x % 4294967296l;} - public static long lrem_by_pow2_33(long x) {return x % 8589934592l;} - public static long lrem_by_pow2_34(long x) {return x % 17179869184l;} - public static long lrem_by_pow2_35(long x) {return x % 34359738368l;} - public static long lrem_by_pow2_36(long x) {return x % 68719476736l;} - public static long lrem_by_pow2_37(long x) {return x % 137438953472l;} - public static long lrem_by_pow2_38(long x) {return x % 274877906944l;} - public static long lrem_by_pow2_39(long x) {return x % 549755813888l;} - public static long lrem_by_pow2_40(long x) {return x % 1099511627776l;} - public static long lrem_by_pow2_41(long x) {return x % 2199023255552l;} - public static long lrem_by_pow2_42(long x) {return x % 4398046511104l;} - public static long lrem_by_pow2_43(long x) {return x % 8796093022208l;} - public static long lrem_by_pow2_44(long x) {return x % 17592186044416l;} - public static long lrem_by_pow2_45(long x) {return x % 35184372088832l;} - public static long lrem_by_pow2_46(long x) {return x % 70368744177664l;} - public static long lrem_by_pow2_47(long x) {return x % 140737488355328l;} - public static long lrem_by_pow2_48(long x) {return x % 281474976710656l;} - public static long lrem_by_pow2_49(long x) {return x % 562949953421312l;} - public static long lrem_by_pow2_50(long x) {return x % 1125899906842624l;} - public static long lrem_by_pow2_51(long x) {return x % 2251799813685248l;} - public static long lrem_by_pow2_52(long x) {return x % 4503599627370496l;} - public static long lrem_by_pow2_53(long x) {return x % 9007199254740992l;} - public static long lrem_by_pow2_54(long x) {return x % 18014398509481984l;} - public static long lrem_by_pow2_55(long x) {return x % 36028797018963968l;} - public static long lrem_by_pow2_56(long x) {return x % 72057594037927936l;} - public static long lrem_by_pow2_57(long x) {return x % 144115188075855872l;} - public static long lrem_by_pow2_58(long x) {return x % 288230376151711744l;} - public static long lrem_by_pow2_59(long x) {return x % 576460752303423488l;} - public static long lrem_by_pow2_60(long x) {return x % 1152921504606846976l;} - public static long lrem_by_pow2_61(long x) {return x % 2305843009213693952l;} - public static long lrem_by_pow2_62(long x) {return x % 4611686018427387904l;} - - public static void intCheckAll(int x) { - intCheckDiv("idiv_by_pow2_0", idiv_by_pow2_0(x), x, 1); - intCheckDiv("idiv_by_pow2_1", idiv_by_pow2_1(x), x, 2); - intCheckDiv("idiv_by_pow2_2", idiv_by_pow2_2(x), x, 4); - intCheckDiv("idiv_by_pow2_3", idiv_by_pow2_3(x), x, 8); - intCheckDiv("idiv_by_pow2_4", idiv_by_pow2_4(x), x, 16); - intCheckDiv("idiv_by_pow2_5", idiv_by_pow2_5(x), x, 32); - intCheckDiv("idiv_by_pow2_6", idiv_by_pow2_6(x), x, 64); - intCheckDiv("idiv_by_pow2_7", idiv_by_pow2_7(x), x, 128); - intCheckDiv("idiv_by_pow2_8", idiv_by_pow2_8(x), x, 256); - intCheckDiv("idiv_by_pow2_9", idiv_by_pow2_9(x), x, 512); - intCheckDiv("idiv_by_pow2_10", idiv_by_pow2_10(x), x, 1024); - intCheckDiv("idiv_by_pow2_11", idiv_by_pow2_11(x), x, 2048); - intCheckDiv("idiv_by_pow2_12", idiv_by_pow2_12(x), x, 4096); - intCheckDiv("idiv_by_pow2_13", idiv_by_pow2_13(x), x, 8192); - intCheckDiv("idiv_by_pow2_14", idiv_by_pow2_14(x), x, 16384); - intCheckDiv("idiv_by_pow2_15", idiv_by_pow2_15(x), x, 32768); - intCheckDiv("idiv_by_pow2_16", idiv_by_pow2_16(x), x, 65536); - intCheckDiv("idiv_by_pow2_17", idiv_by_pow2_17(x), x, 131072); - intCheckDiv("idiv_by_pow2_18", idiv_by_pow2_18(x), x, 262144); - intCheckDiv("idiv_by_pow2_19", idiv_by_pow2_19(x), x, 524288); - intCheckDiv("idiv_by_pow2_20", idiv_by_pow2_20(x), x, 1048576); - intCheckDiv("idiv_by_pow2_21", idiv_by_pow2_21(x), x, 2097152); - intCheckDiv("idiv_by_pow2_22", idiv_by_pow2_22(x), x, 4194304); - intCheckDiv("idiv_by_pow2_23", idiv_by_pow2_23(x), x, 8388608); - intCheckDiv("idiv_by_pow2_24", idiv_by_pow2_24(x), x, 16777216); - intCheckDiv("idiv_by_pow2_25", idiv_by_pow2_25(x), x, 33554432); - intCheckDiv("idiv_by_pow2_26", idiv_by_pow2_26(x), x, 67108864); - intCheckDiv("idiv_by_pow2_27", idiv_by_pow2_27(x), x, 134217728); - intCheckDiv("idiv_by_pow2_28", idiv_by_pow2_28(x), x, 268435456); - intCheckDiv("idiv_by_pow2_29", idiv_by_pow2_29(x), x, 536870912); - intCheckDiv("idiv_by_pow2_30", idiv_by_pow2_30(x), x, 1073741824); - intCheckDiv("idiv_by_small_0", idiv_by_small_0(x), x, 3); - intCheckDiv("idiv_by_small_1", idiv_by_small_1(x), x, 5); - intCheckDiv("idiv_by_small_2", idiv_by_small_2(x), x, 6); - intCheckDiv("idiv_by_small_3", idiv_by_small_3(x), x, 7); - intCheckDiv("idiv_by_small_4", idiv_by_small_4(x), x, 9); - intCheckDiv("idiv_by_small_5", idiv_by_small_5(x), x, 10); - intCheckDiv("idiv_by_small_6", idiv_by_small_6(x), x, 11); - intCheckDiv("idiv_by_small_7", idiv_by_small_7(x), x, 12); - intCheckDiv("idiv_by_small_8", idiv_by_small_8(x), x, 13); - intCheckDiv("idiv_by_small_9", idiv_by_small_9(x), x, 14); - intCheckDiv("idiv_by_small_10", idiv_by_small_10(x), x, 15); - intCheckRem("irem_by_pow2_0", irem_by_pow2_0(x), x, 1); - intCheckRem("irem_by_pow2_1", irem_by_pow2_1(x), x, 2); - intCheckRem("irem_by_pow2_2", irem_by_pow2_2(x), x, 4); - intCheckRem("irem_by_pow2_3", irem_by_pow2_3(x), x, 8); - intCheckRem("irem_by_pow2_4", irem_by_pow2_4(x), x, 16); - intCheckRem("irem_by_pow2_5", irem_by_pow2_5(x), x, 32); - intCheckRem("irem_by_pow2_6", irem_by_pow2_6(x), x, 64); - intCheckRem("irem_by_pow2_7", irem_by_pow2_7(x), x, 128); - intCheckRem("irem_by_pow2_8", irem_by_pow2_8(x), x, 256); - intCheckRem("irem_by_pow2_9", irem_by_pow2_9(x), x, 512); - intCheckRem("irem_by_pow2_10", irem_by_pow2_10(x), x, 1024); - intCheckRem("irem_by_pow2_11", irem_by_pow2_11(x), x, 2048); - intCheckRem("irem_by_pow2_12", irem_by_pow2_12(x), x, 4096); - intCheckRem("irem_by_pow2_13", irem_by_pow2_13(x), x, 8192); - intCheckRem("irem_by_pow2_14", irem_by_pow2_14(x), x, 16384); - intCheckRem("irem_by_pow2_15", irem_by_pow2_15(x), x, 32768); - intCheckRem("irem_by_pow2_16", irem_by_pow2_16(x), x, 65536); - intCheckRem("irem_by_pow2_17", irem_by_pow2_17(x), x, 131072); - intCheckRem("irem_by_pow2_18", irem_by_pow2_18(x), x, 262144); - intCheckRem("irem_by_pow2_19", irem_by_pow2_19(x), x, 524288); - intCheckRem("irem_by_pow2_20", irem_by_pow2_20(x), x, 1048576); - intCheckRem("irem_by_pow2_21", irem_by_pow2_21(x), x, 2097152); - intCheckRem("irem_by_pow2_22", irem_by_pow2_22(x), x, 4194304); - intCheckRem("irem_by_pow2_23", irem_by_pow2_23(x), x, 8388608); - intCheckRem("irem_by_pow2_24", irem_by_pow2_24(x), x, 16777216); - intCheckRem("irem_by_pow2_25", irem_by_pow2_25(x), x, 33554432); - intCheckRem("irem_by_pow2_26", irem_by_pow2_26(x), x, 67108864); - intCheckRem("irem_by_pow2_27", irem_by_pow2_27(x), x, 134217728); - intCheckRem("irem_by_pow2_28", irem_by_pow2_28(x), x, 268435456); - intCheckRem("irem_by_pow2_29", irem_by_pow2_29(x), x, 536870912); - intCheckRem("irem_by_pow2_30", irem_by_pow2_30(x), x, 1073741824); - } - - public static void longCheckAll(long x) { - longCheckDiv("ldiv_by_pow2_0", ldiv_by_pow2_0(x), x, 1l); - longCheckDiv("ldiv_by_pow2_1", ldiv_by_pow2_1(x), x, 2l); - longCheckDiv("ldiv_by_pow2_2", ldiv_by_pow2_2(x), x, 4l); - longCheckDiv("ldiv_by_pow2_3", ldiv_by_pow2_3(x), x, 8l); - longCheckDiv("ldiv_by_pow2_4", ldiv_by_pow2_4(x), x, 16l); - longCheckDiv("ldiv_by_pow2_5", ldiv_by_pow2_5(x), x, 32l); - longCheckDiv("ldiv_by_pow2_6", ldiv_by_pow2_6(x), x, 64l); - longCheckDiv("ldiv_by_pow2_7", ldiv_by_pow2_7(x), x, 128l); - longCheckDiv("ldiv_by_pow2_8", ldiv_by_pow2_8(x), x, 256l); - longCheckDiv("ldiv_by_pow2_9", ldiv_by_pow2_9(x), x, 512l); - longCheckDiv("ldiv_by_pow2_10", ldiv_by_pow2_10(x), x, 1024l); - longCheckDiv("ldiv_by_pow2_11", ldiv_by_pow2_11(x), x, 2048l); - longCheckDiv("ldiv_by_pow2_12", ldiv_by_pow2_12(x), x, 4096l); - longCheckDiv("ldiv_by_pow2_13", ldiv_by_pow2_13(x), x, 8192l); - longCheckDiv("ldiv_by_pow2_14", ldiv_by_pow2_14(x), x, 16384l); - longCheckDiv("ldiv_by_pow2_15", ldiv_by_pow2_15(x), x, 32768l); - longCheckDiv("ldiv_by_pow2_16", ldiv_by_pow2_16(x), x, 65536l); - longCheckDiv("ldiv_by_pow2_17", ldiv_by_pow2_17(x), x, 131072l); - longCheckDiv("ldiv_by_pow2_18", ldiv_by_pow2_18(x), x, 262144l); - longCheckDiv("ldiv_by_pow2_19", ldiv_by_pow2_19(x), x, 524288l); - longCheckDiv("ldiv_by_pow2_20", ldiv_by_pow2_20(x), x, 1048576l); - longCheckDiv("ldiv_by_pow2_21", ldiv_by_pow2_21(x), x, 2097152l); - longCheckDiv("ldiv_by_pow2_22", ldiv_by_pow2_22(x), x, 4194304l); - longCheckDiv("ldiv_by_pow2_23", ldiv_by_pow2_23(x), x, 8388608l); - longCheckDiv("ldiv_by_pow2_24", ldiv_by_pow2_24(x), x, 16777216l); - longCheckDiv("ldiv_by_pow2_25", ldiv_by_pow2_25(x), x, 33554432l); - longCheckDiv("ldiv_by_pow2_26", ldiv_by_pow2_26(x), x, 67108864l); - longCheckDiv("ldiv_by_pow2_27", ldiv_by_pow2_27(x), x, 134217728l); - longCheckDiv("ldiv_by_pow2_28", ldiv_by_pow2_28(x), x, 268435456l); - longCheckDiv("ldiv_by_pow2_29", ldiv_by_pow2_29(x), x, 536870912l); - longCheckDiv("ldiv_by_pow2_30", ldiv_by_pow2_30(x), x, 1073741824l); - longCheckDiv("ldiv_by_pow2_31", ldiv_by_pow2_31(x), x, 2147483648l); - longCheckDiv("ldiv_by_pow2_32", ldiv_by_pow2_32(x), x, 4294967296l); - longCheckDiv("ldiv_by_pow2_33", ldiv_by_pow2_33(x), x, 8589934592l); - longCheckDiv("ldiv_by_pow2_34", ldiv_by_pow2_34(x), x, 17179869184l); - longCheckDiv("ldiv_by_pow2_35", ldiv_by_pow2_35(x), x, 34359738368l); - longCheckDiv("ldiv_by_pow2_36", ldiv_by_pow2_36(x), x, 68719476736l); - longCheckDiv("ldiv_by_pow2_37", ldiv_by_pow2_37(x), x, 137438953472l); - longCheckDiv("ldiv_by_pow2_38", ldiv_by_pow2_38(x), x, 274877906944l); - longCheckDiv("ldiv_by_pow2_39", ldiv_by_pow2_39(x), x, 549755813888l); - longCheckDiv("ldiv_by_pow2_40", ldiv_by_pow2_40(x), x, 1099511627776l); - longCheckDiv("ldiv_by_pow2_41", ldiv_by_pow2_41(x), x, 2199023255552l); - longCheckDiv("ldiv_by_pow2_42", ldiv_by_pow2_42(x), x, 4398046511104l); - longCheckDiv("ldiv_by_pow2_43", ldiv_by_pow2_43(x), x, 8796093022208l); - longCheckDiv("ldiv_by_pow2_44", ldiv_by_pow2_44(x), x, 17592186044416l); - longCheckDiv("ldiv_by_pow2_45", ldiv_by_pow2_45(x), x, 35184372088832l); - longCheckDiv("ldiv_by_pow2_46", ldiv_by_pow2_46(x), x, 70368744177664l); - longCheckDiv("ldiv_by_pow2_47", ldiv_by_pow2_47(x), x, 140737488355328l); - longCheckDiv("ldiv_by_pow2_48", ldiv_by_pow2_48(x), x, 281474976710656l); - longCheckDiv("ldiv_by_pow2_49", ldiv_by_pow2_49(x), x, 562949953421312l); - longCheckDiv("ldiv_by_pow2_50", ldiv_by_pow2_50(x), x, 1125899906842624l); - longCheckDiv("ldiv_by_pow2_51", ldiv_by_pow2_51(x), x, 2251799813685248l); - longCheckDiv("ldiv_by_pow2_52", ldiv_by_pow2_52(x), x, 4503599627370496l); - longCheckDiv("ldiv_by_pow2_53", ldiv_by_pow2_53(x), x, 9007199254740992l); - longCheckDiv("ldiv_by_pow2_54", ldiv_by_pow2_54(x), x, 18014398509481984l); - longCheckDiv("ldiv_by_pow2_55", ldiv_by_pow2_55(x), x, 36028797018963968l); - longCheckDiv("ldiv_by_pow2_56", ldiv_by_pow2_56(x), x, 72057594037927936l); - longCheckDiv("ldiv_by_pow2_57", ldiv_by_pow2_57(x), x, 144115188075855872l); - longCheckDiv("ldiv_by_pow2_58", ldiv_by_pow2_58(x), x, 288230376151711744l); - longCheckDiv("ldiv_by_pow2_59", ldiv_by_pow2_59(x), x, 576460752303423488l); - longCheckDiv("ldiv_by_pow2_60", ldiv_by_pow2_60(x), x, 1152921504606846976l); - longCheckDiv("ldiv_by_pow2_61", ldiv_by_pow2_61(x), x, 2305843009213693952l); - longCheckDiv("ldiv_by_pow2_62", ldiv_by_pow2_62(x), x, 4611686018427387904l); - longCheckDiv("ldiv_by_small_0", ldiv_by_small_0(x), x, 3l); - longCheckDiv("ldiv_by_small_1", ldiv_by_small_1(x), x, 5l); - longCheckDiv("ldiv_by_small_2", ldiv_by_small_2(x), x, 6l); - longCheckDiv("ldiv_by_small_3", ldiv_by_small_3(x), x, 7l); - longCheckDiv("ldiv_by_small_4", ldiv_by_small_4(x), x, 9l); - longCheckDiv("ldiv_by_small_5", ldiv_by_small_5(x), x, 10l); - longCheckDiv("ldiv_by_small_6", ldiv_by_small_6(x), x, 11l); - longCheckDiv("ldiv_by_small_7", ldiv_by_small_7(x), x, 12l); - longCheckDiv("ldiv_by_small_8", ldiv_by_small_8(x), x, 13l); - longCheckDiv("ldiv_by_small_9", ldiv_by_small_9(x), x, 14l); - longCheckDiv("ldiv_by_small_10", ldiv_by_small_10(x), x, 15l); - longCheckRem("lrem_by_pow2_0", lrem_by_pow2_0(x), x, 1l); - longCheckRem("lrem_by_pow2_1", lrem_by_pow2_1(x), x, 2l); - longCheckRem("lrem_by_pow2_2", lrem_by_pow2_2(x), x, 4l); - longCheckRem("lrem_by_pow2_3", lrem_by_pow2_3(x), x, 8l); - longCheckRem("lrem_by_pow2_4", lrem_by_pow2_4(x), x, 16l); - longCheckRem("lrem_by_pow2_5", lrem_by_pow2_5(x), x, 32l); - longCheckRem("lrem_by_pow2_6", lrem_by_pow2_6(x), x, 64l); - longCheckRem("lrem_by_pow2_7", lrem_by_pow2_7(x), x, 128l); - longCheckRem("lrem_by_pow2_8", lrem_by_pow2_8(x), x, 256l); - longCheckRem("lrem_by_pow2_9", lrem_by_pow2_9(x), x, 512l); - longCheckRem("lrem_by_pow2_10", lrem_by_pow2_10(x), x, 1024l); - longCheckRem("lrem_by_pow2_11", lrem_by_pow2_11(x), x, 2048l); - longCheckRem("lrem_by_pow2_12", lrem_by_pow2_12(x), x, 4096l); - longCheckRem("lrem_by_pow2_13", lrem_by_pow2_13(x), x, 8192l); - longCheckRem("lrem_by_pow2_14", lrem_by_pow2_14(x), x, 16384l); - longCheckRem("lrem_by_pow2_15", lrem_by_pow2_15(x), x, 32768l); - longCheckRem("lrem_by_pow2_16", lrem_by_pow2_16(x), x, 65536l); - longCheckRem("lrem_by_pow2_17", lrem_by_pow2_17(x), x, 131072l); - longCheckRem("lrem_by_pow2_18", lrem_by_pow2_18(x), x, 262144l); - longCheckRem("lrem_by_pow2_19", lrem_by_pow2_19(x), x, 524288l); - longCheckRem("lrem_by_pow2_20", lrem_by_pow2_20(x), x, 1048576l); - longCheckRem("lrem_by_pow2_21", lrem_by_pow2_21(x), x, 2097152l); - longCheckRem("lrem_by_pow2_22", lrem_by_pow2_22(x), x, 4194304l); - longCheckRem("lrem_by_pow2_23", lrem_by_pow2_23(x), x, 8388608l); - longCheckRem("lrem_by_pow2_24", lrem_by_pow2_24(x), x, 16777216l); - longCheckRem("lrem_by_pow2_25", lrem_by_pow2_25(x), x, 33554432l); - longCheckRem("lrem_by_pow2_26", lrem_by_pow2_26(x), x, 67108864l); - longCheckRem("lrem_by_pow2_27", lrem_by_pow2_27(x), x, 134217728l); - longCheckRem("lrem_by_pow2_28", lrem_by_pow2_28(x), x, 268435456l); - longCheckRem("lrem_by_pow2_29", lrem_by_pow2_29(x), x, 536870912l); - longCheckRem("lrem_by_pow2_30", lrem_by_pow2_30(x), x, 1073741824l); - longCheckRem("lrem_by_pow2_31", lrem_by_pow2_31(x), x, 2147483648l); - longCheckRem("lrem_by_pow2_32", lrem_by_pow2_32(x), x, 4294967296l); - longCheckRem("lrem_by_pow2_33", lrem_by_pow2_33(x), x, 8589934592l); - longCheckRem("lrem_by_pow2_34", lrem_by_pow2_34(x), x, 17179869184l); - longCheckRem("lrem_by_pow2_35", lrem_by_pow2_35(x), x, 34359738368l); - longCheckRem("lrem_by_pow2_36", lrem_by_pow2_36(x), x, 68719476736l); - longCheckRem("lrem_by_pow2_37", lrem_by_pow2_37(x), x, 137438953472l); - longCheckRem("lrem_by_pow2_38", lrem_by_pow2_38(x), x, 274877906944l); - longCheckRem("lrem_by_pow2_39", lrem_by_pow2_39(x), x, 549755813888l); - longCheckRem("lrem_by_pow2_40", lrem_by_pow2_40(x), x, 1099511627776l); - longCheckRem("lrem_by_pow2_41", lrem_by_pow2_41(x), x, 2199023255552l); - longCheckRem("lrem_by_pow2_42", lrem_by_pow2_42(x), x, 4398046511104l); - longCheckRem("lrem_by_pow2_43", lrem_by_pow2_43(x), x, 8796093022208l); - longCheckRem("lrem_by_pow2_44", lrem_by_pow2_44(x), x, 17592186044416l); - longCheckRem("lrem_by_pow2_45", lrem_by_pow2_45(x), x, 35184372088832l); - longCheckRem("lrem_by_pow2_46", lrem_by_pow2_46(x), x, 70368744177664l); - longCheckRem("lrem_by_pow2_47", lrem_by_pow2_47(x), x, 140737488355328l); - longCheckRem("lrem_by_pow2_48", lrem_by_pow2_48(x), x, 281474976710656l); - longCheckRem("lrem_by_pow2_49", lrem_by_pow2_49(x), x, 562949953421312l); - longCheckRem("lrem_by_pow2_50", lrem_by_pow2_50(x), x, 1125899906842624l); - longCheckRem("lrem_by_pow2_51", lrem_by_pow2_51(x), x, 2251799813685248l); - longCheckRem("lrem_by_pow2_52", lrem_by_pow2_52(x), x, 4503599627370496l); - longCheckRem("lrem_by_pow2_53", lrem_by_pow2_53(x), x, 9007199254740992l); - longCheckRem("lrem_by_pow2_54", lrem_by_pow2_54(x), x, 18014398509481984l); - longCheckRem("lrem_by_pow2_55", lrem_by_pow2_55(x), x, 36028797018963968l); - longCheckRem("lrem_by_pow2_56", lrem_by_pow2_56(x), x, 72057594037927936l); - longCheckRem("lrem_by_pow2_57", lrem_by_pow2_57(x), x, 144115188075855872l); - longCheckRem("lrem_by_pow2_58", lrem_by_pow2_58(x), x, 288230376151711744l); - longCheckRem("lrem_by_pow2_59", lrem_by_pow2_59(x), x, 576460752303423488l); - longCheckRem("lrem_by_pow2_60", lrem_by_pow2_60(x), x, 1152921504606846976l); - longCheckRem("lrem_by_pow2_61", lrem_by_pow2_61(x), x, 2305843009213693952l); - longCheckRem("lrem_by_pow2_62", lrem_by_pow2_62(x), x, 4611686018427387904l); - } - - public static void main(String[] args) { - int i; - long l; - - System.out.println("Begin"); - - System.out.println("Int: checking some equally spaced dividends..."); - for (i = -1000; i < 1000; i += 300) { - intCheckAll(i); - intCheckAll(-i); - } - - System.out.println("Int: checking small dividends..."); - for (i = 1; i < 100; i += 1) { - intCheckAll(i); - intCheckAll(-i); - } - - System.out.println("Int: checking big dividends..."); - for (i = 0; i < 100; i += 1) { - intCheckAll(Integer.MAX_VALUE - i); - intCheckAll(Integer.MIN_VALUE + i); - } - - System.out.println("Long: checking some equally spaced dividends..."); - for (l = 0l; l < 1000000000000l; l += 300000000000l) { - longCheckAll(l); - longCheckAll(-l); - } - - System.out.println("Long: checking small dividends..."); - for (l = 1l; l < 100l; l += 1l) { - longCheckAll(l); - longCheckAll(-l); - } - - System.out.println("Long: checking big dividends..."); - for (l = 0l; l < 100l; l += 1l) { - longCheckAll(Long.MAX_VALUE - l); - longCheckAll(Long.MIN_VALUE + l); - } - - System.out.println("End"); - } -} diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build index eacf730cb9..20030fa466 100644 --- a/test/702-LargeBranchOffset/build +++ b/test/702-LargeBranchOffset/build @@ -17,11 +17,7 @@ # Stop if something fails. set -e -# Write out a bunch of source files. +# Write out the source file. cpp -P src/Main.java.in src/Main.java -mkdir classes -${JAVAC} -d classes src/*.java - -${DX} --debug --dex --output=classes.dex classes -zip $TEST_NAME.jar classes.dex +./default-build diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 40f5f00b8f..07e76205a4 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -428,8 +428,7 @@ endif TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := # Tests that should fail in the read barrier configuration. -TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \ - 098-ddmc # b/20720510 +TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := ifeq ($(ART_USE_READ_BARRIER),true) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/run-test b/test/run-test index 2873a35c83..54c6bbda12 100755 --- a/test/run-test +++ b/test/run-test @@ -39,7 +39,7 @@ if [ -z "$TMPDIR" ]; then else tmp_dir="${TMPDIR}/$USER/${test_dir}" fi -checker="${progdir}/../tools/checker.py" +checker="${progdir}/../tools/checker/checker.py" export JAVA="java" export JAVAC="javac -g" @@ -501,14 +501,20 @@ cd "$tmp_dir" if [ '!' -r "$build" ]; then cp "${progdir}/etc/default-build" build +else + cp "${progdir}/etc/default-build" . fi if [ '!' -r "$run" ]; then cp "${progdir}/etc/default-run" run +else + cp "${progdir}/etc/default-run" . fi if [ '!' -r "$check_cmd" ]; then cp "${progdir}/etc/default-check" check +else + cp "${progdir}/etc/default-check" . fi chmod 755 "$build" diff --git a/tools/checker.py b/tools/checker.py deleted file mode 100755 index 0bce236223..0000000000 --- a/tools/checker.py +++ /dev/null @@ -1,777 +0,0 @@ -#!/usr/bin/env python2 -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# Checker is a testing tool which compiles a given test file and compares the -# state of the control-flow graph before and after each optimization pass -# against a set of assertions specified alongside the tests. -# -# Tests are written in Java, turned into DEX and compiled with the Optimizing -# compiler. "Check lines" are assertions formatted as comments of the Java file. -# They begin with prefix 'CHECK' followed by a pattern that the engine attempts -# to match in the compiler-generated output. -# -# Assertions are tested in groups which correspond to the individual compiler -# passes. Each group of check lines therefore must start with a 'CHECK-START' -# header which specifies the output group it should be tested against. The group -# name must exactly match one of the groups recognized in the output (they can -# be listed with the '--list-groups' command-line flag). -# -# Matching of check lines is carried out in the order of appearance in the -# source file. There are three types of check lines: -# - CHECK: Must match an output line which appears in the output group -# later than lines matched against any preceeding checks. Output -# lines must therefore match the check lines in the same order. -# These are referred to as "in-order" checks in the code. -# - CHECK-DAG: Must match an output line which appears in the output group -# later than lines matched against any preceeding in-order checks. -# In other words, the order of output lines does not matter -# between consecutive DAG checks. -# - CHECK-NOT: Must not match any output line which appears in the output group -# later than lines matched against any preceeding checks and -# earlier than lines matched against any subsequent checks. -# Surrounding non-negative checks (or boundaries of the group) -# therefore create a scope within which the assertion is verified. -# -# Check-line patterns are treated as plain text rather than regular expressions -# but are whitespace agnostic. -# -# Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If -# curly brackets need to be used inside the body of the regex, they need to be -# enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse -# the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'. -# -# Regex patterns can be named and referenced later. A new variable is defined -# with '[[name:regex]]' and can be referenced with '[[name]]'. Variables are -# only valid within the scope of the defining group. Within a group they cannot -# be redefined or used undefined. -# -# Example: -# The following assertions can be placed in a Java source file: -# -# // CHECK-START: int MyClass.MyMethod() constant_folding (after) -# // CHECK: [[ID:i[0-9]+]] IntConstant {{11|22}} -# // CHECK: Return [ [[ID]] ] -# -# The engine will attempt to match the check lines against the output of the -# group named on the first line. Together they verify that the CFG after -# constant folding returns an integer constant with value either 11 or 22. -# - -from __future__ import print_function -import argparse -import os -import re -import shutil -import sys -import tempfile - -class Logger(object): - - class Level(object): - NoOutput, Error, Info = range(3) - - class Color(object): - Default, Blue, Gray, Purple, Red = range(5) - - @staticmethod - def terminalCode(color, out=sys.stdout): - if not out.isatty(): - return '' - elif color == Logger.Color.Blue: - return '\033[94m' - elif color == Logger.Color.Gray: - return '\033[37m' - elif color == Logger.Color.Purple: - return '\033[95m' - elif color == Logger.Color.Red: - return '\033[91m' - else: - return '\033[0m' - - Verbosity = Level.Info - - @staticmethod - def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout): - if level <= Logger.Verbosity: - text = Logger.Color.terminalCode(color, out) + text + \ - Logger.Color.terminalCode(Logger.Color.Default, out) - if newLine: - print(text, file=out) - else: - print(text, end="", file=out) - out.flush() - - @staticmethod - def fail(msg, file=None, line=-1): - location = "" - if file: - location += file + ":" - if line > 0: - location += str(line) + ":" - if location: - location += " " - - Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) - Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr) - Logger.log(msg, Logger.Level.Error, out=sys.stderr) - sys.exit(msg) - - @staticmethod - def startTest(name): - Logger.log("TEST ", color=Logger.Color.Purple, newLine=False) - Logger.log(name + "... ", newLine=False) - - @staticmethod - def testPassed(): - Logger.log("PASS", color=Logger.Color.Blue) - - @staticmethod - def testFailed(msg, file=None, line=-1): - Logger.log("FAIL", color=Logger.Color.Red) - Logger.fail(msg, file, line) - -class CommonEqualityMixin: - """Mixin for class equality as equality of the fields.""" - def __eq__(self, other): - return (isinstance(other, self.__class__) - and self.__dict__ == other.__dict__) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "<%s: %s>" % (type(self).__name__, str(self.__dict__)) - - -class CheckElement(CommonEqualityMixin): - """Single element of the check line.""" - - class Variant(object): - """Supported language constructs.""" - Text, Pattern, VarRef, VarDef, Separator = range(5) - - rStartOptional = r"(" - rEndOptional = r")?" - - rName = r"([a-zA-Z][a-zA-Z0-9]*)" - rRegex = r"(.+?)" - rPatternStartSym = r"(\{\{)" - rPatternEndSym = r"(\}\})" - rVariableStartSym = r"(\[\[)" - rVariableEndSym = r"(\]\])" - rVariableSeparator = r"(:)" - - regexPattern = rPatternStartSym + rRegex + rPatternEndSym - regexVariable = rVariableStartSym + \ - rName + \ - (rStartOptional + rVariableSeparator + rRegex + rEndOptional) + \ - rVariableEndSym - - def __init__(self, variant, name, pattern): - self.variant = variant - self.name = name - self.pattern = pattern - - @staticmethod - def newSeparator(): - return CheckElement(CheckElement.Variant.Separator, None, None) - - @staticmethod - def parseText(text): - return CheckElement(CheckElement.Variant.Text, None, re.escape(text)) - - @staticmethod - def parsePattern(patternElem): - return CheckElement(CheckElement.Variant.Pattern, None, patternElem[2:-2]) - - @staticmethod - def parseVariable(varElem): - colonPos = varElem.find(":") - if colonPos == -1: - # Variable reference - name = varElem[2:-2] - return CheckElement(CheckElement.Variant.VarRef, name, None) - else: - # Variable definition - name = varElem[2:colonPos] - body = varElem[colonPos+1:-2] - return CheckElement(CheckElement.Variant.VarDef, name, body) - -class CheckLine(CommonEqualityMixin): - """Representation of a single assertion in the check file formed of one or - more regex elements. Matching against an output line is successful only - if all regex elements can be matched in the given order.""" - - class Variant(object): - """Supported types of assertions.""" - InOrder, DAG, Not = range(3) - - def __init__(self, content, variant=Variant.InOrder, fileName=None, lineNo=-1): - self.fileName = fileName - self.lineNo = lineNo - self.content = content.strip() - - self.variant = variant - self.lineParts = self.__parse(self.content) - if not self.lineParts: - Logger.fail("Empty check line", self.fileName, self.lineNo) - - if self.variant == CheckLine.Variant.Not: - for elem in self.lineParts: - if elem.variant == CheckElement.Variant.VarDef: - Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) - - def __eq__(self, other): - return (isinstance(other, self.__class__) and - self.variant == other.variant and - self.lineParts == other.lineParts) - - # Returns True if the given Match object was at the beginning of the line. - def __isMatchAtStart(self, match): - return (match is not None) and (match.start() == 0) - - # Takes in a list of Match objects and returns the minimal start point among - # them. If there aren't any successful matches it returns the length of - # the searched string. - def __firstMatch(self, matches, string): - starts = map(lambda m: len(string) if m is None else m.start(), matches) - return min(starts) - - # This method parses the content of a check line stripped of the initial - # comment symbol and the CHECK keyword. - def __parse(self, line): - lineParts = [] - # Loop as long as there is something to parse. - while line: - # Search for the nearest occurrence of the special markers. - matchWhitespace = re.search(r"\s+", line) - matchPattern = re.search(CheckElement.regexPattern, line) - matchVariable = re.search(CheckElement.regexVariable, line) - - # If one of the above was identified at the current position, extract them - # from the line, parse them and add to the list of line parts. - if self.__isMatchAtStart(matchWhitespace): - # A whitespace in the check line creates a new separator of line parts. - # This allows for ignored output between the previous and next parts. - line = line[matchWhitespace.end():] - lineParts.append(CheckElement.newSeparator()) - elif self.__isMatchAtStart(matchPattern): - pattern = line[0:matchPattern.end()] - line = line[matchPattern.end():] - lineParts.append(CheckElement.parsePattern(pattern)) - elif self.__isMatchAtStart(matchVariable): - var = line[0:matchVariable.end()] - line = line[matchVariable.end():] - lineParts.append(CheckElement.parseVariable(var)) - else: - # If we're not currently looking at a special marker, this is a plain - # text match all the way until the first special marker (or the end - # of the line). - firstMatch = self.__firstMatch([ matchWhitespace, matchPattern, matchVariable ], line) - text = line[0:firstMatch] - line = line[firstMatch:] - lineParts.append(CheckElement.parseText(text)) - return lineParts - - # Returns the regex pattern to be matched in the output line. Variable - # references are substituted with their current values provided in the - # 'varState' argument. - # An exception is raised if a referenced variable is undefined. - def __generatePattern(self, linePart, varState): - if linePart.variant == CheckElement.Variant.VarRef: - try: - return re.escape(varState[linePart.name]) - except KeyError: - Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"", - self.fileName, self.lineNo) - else: - return linePart.pattern - - def __isSeparated(self, outputLine, matchStart): - return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace()) - - # Attempts to match the check line against a line from the output file with - # the given initial variable values. It returns the new variable state if - # successful and None otherwise. - def match(self, outputLine, initialVarState): - # Do the full matching on a shadow copy of the variable state. If the - # matching fails half-way, we will not need to revert the state. - varState = dict(initialVarState) - - matchStart = 0 - isAfterSeparator = True - - # Now try to parse all of the parts of the check line in the right order. - # Variable values are updated on-the-fly, meaning that a variable can - # be referenced immediately after its definition. - for part in self.lineParts: - if part.variant == CheckElement.Variant.Separator: - isAfterSeparator = True - continue - - # Find the earliest match for this line part. - pattern = self.__generatePattern(part, varState) - while True: - match = re.search(pattern, outputLine[matchStart:]) - if (match is None) or (not isAfterSeparator and not self.__isMatchAtStart(match)): - return None - matchEnd = matchStart + match.end() - matchStart += match.start() - - # Check if this is a valid match if we expect a whitespace separator - # before the matched text. Otherwise loop and look for another match. - if not isAfterSeparator or self.__isSeparated(outputLine, matchStart): - break - else: - matchStart += 1 - - if part.variant == CheckElement.Variant.VarDef: - if part.name in varState: - Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"", - self.fileName, self.lineNo) - varState[part.name] = outputLine[matchStart:matchEnd] - - matchStart = matchEnd - isAfterSeparator = False - - # All parts were successfully matched. Return the new variable state. - return varState - - -class CheckGroup(CommonEqualityMixin): - """Represents a named collection of check lines which are to be matched - against an output group of the same name.""" - - def __init__(self, name, lines, fileName=None, lineNo=-1): - self.fileName = fileName - self.lineNo = lineNo - - if not name: - Logger.fail("Check group does not have a name", self.fileName, self.lineNo) - if not lines: - Logger.fail("Check group does not have a body", self.fileName, self.lineNo) - - self.name = name - self.lines = lines - - def __eq__(self, other): - return (isinstance(other, self.__class__) and - self.name == other.name and - self.lines == other.lines) - - def __headAndTail(self, list): - return list[0], list[1:] - - # Splits a list of check lines at index 'i' such that lines[i] is the first - # element whose variant is not equal to the given parameter. - def __splitByVariant(self, lines, variant): - i = 0 - while i < len(lines) and lines[i].variant == variant: - i += 1 - return lines[:i], lines[i:] - - # Extracts the first sequence of check lines which are independent of each - # other's match location, i.e. either consecutive DAG lines or a single - # InOrder line. Any Not lines preceeding this sequence are also extracted. - def __nextIndependentChecks(self, checkLines): - notChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.Not) - if not checkLines: - return notChecks, [], [] - - head, tail = self.__headAndTail(checkLines) - if head.variant == CheckLine.Variant.InOrder: - return notChecks, [head], tail - else: - assert head.variant == CheckLine.Variant.DAG - independentChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.DAG) - return notChecks, independentChecks, checkLines - - # If successful, returns the line number of the first output line matching the - # check line and the updated variable state. Otherwise returns -1 and None, - # respectively. The 'lineFilter' parameter can be used to supply a list of - # line numbers (counting from 1) which should be skipped. - def __findFirstMatch(self, checkLine, outputLines, startLineNo, lineFilter, varState): - matchLineNo = startLineNo - for outputLine in outputLines: - if matchLineNo not in lineFilter: - newVarState = checkLine.match(outputLine, varState) - if newVarState is not None: - return matchLineNo, newVarState - matchLineNo += 1 - return -1, None - - # Matches the given positive check lines against the output in order of - # appearance. Variable state is propagated but the scope of the search remains - # the same for all checks. Each output line can only be matched once. - # If all check lines are matched, the resulting variable state is returned - # together with the remaining output. The function also returns output lines - # which appear before either of the matched lines so they can be tested - # against Not checks. - def __matchIndependentChecks(self, checkLines, outputLines, startLineNo, varState): - # If no checks are provided, skip over the entire output. - if not checkLines: - return outputLines, [], startLineNo + len(outputLines), varState - - # Keep track of which lines have been matched. - matchedLines = [] - - # Find first unused output line which matches each check line. - for checkLine in checkLines: - matchLineNo, varState = \ - self.__findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState) - if varState is None: - Logger.testFailed("Could not match check line \"" + checkLine.content + "\" " + - "starting from output line " + str(startLineNo), - self.fileName, checkLine.lineNo) - matchedLines.append(matchLineNo) - - # Return new variable state and the output lines which lie outside the - # match locations of this independent group. - minMatchLineNo = min(matchedLines) - maxMatchLineNo = max(matchedLines) - preceedingLines = outputLines[:minMatchLineNo - startLineNo] - remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:] - return preceedingLines, remainingLines, maxMatchLineNo + 1, varState - - # Makes sure that the given check lines do not match any of the given output - # lines. Variable state does not change. - def __matchNotLines(self, checkLines, outputLines, startLineNo, varState): - for checkLine in checkLines: - assert checkLine.variant == CheckLine.Variant.Not - matchLineNo, matchVarState = \ - self.__findFirstMatch(checkLine, outputLines, startLineNo, [], varState) - if matchVarState is not None: - Logger.testFailed("CHECK-NOT line \"" + checkLine.content + "\" matches output line " + \ - str(matchLineNo), self.fileName, checkLine.lineNo) - - # Matches the check lines in this group against an output group. It is - # responsible for running the checks in the right order and scope, and - # for propagating the variable state between the check lines. - def match(self, outputGroup): - varState = {} - checkLines = self.lines - outputLines = outputGroup.body - startLineNo = outputGroup.lineNo - - while checkLines: - # Extract the next sequence of location-independent checks to be matched. - notChecks, independentChecks, checkLines = self.__nextIndependentChecks(checkLines) - - # Match the independent checks. - notOutput, outputLines, newStartLineNo, newVarState = \ - self.__matchIndependentChecks(independentChecks, outputLines, startLineNo, varState) - - # Run the Not checks against the output lines which lie between the last - # two independent groups or the bounds of the output. - self.__matchNotLines(notChecks, notOutput, startLineNo, varState) - - # Update variable state. - startLineNo = newStartLineNo - varState = newVarState - -class OutputGroup(CommonEqualityMixin): - """Represents a named part of the test output against which a check group of - the same name is to be matched.""" - - def __init__(self, name, body, fileName=None, lineNo=-1): - if not name: - Logger.fail("Output group does not have a name", fileName, lineNo) - if not body: - Logger.fail("Output group does not have a body", fileName, lineNo) - - self.name = name - self.body = body - self.lineNo = lineNo - - def __eq__(self, other): - return (isinstance(other, self.__class__) and - self.name == other.name and - self.body == other.body) - - -class FileSplitMixin(object): - """Mixin for representing text files which need to be split into smaller - chunks before being parsed.""" - - def _parseStream(self, stream): - lineNo = 0 - allGroups = [] - currentGroup = None - - for line in stream: - lineNo += 1 - line = line.strip() - if not line: - continue - - # Let the child class process the line and return information about it. - # The _processLine method can modify the content of the line (or delete it - # entirely) and specify whether it starts a new group. - processedLine, newGroupName = self._processLine(line, lineNo) - if newGroupName is not None: - currentGroup = (newGroupName, [], lineNo) - allGroups.append(currentGroup) - if processedLine is not None: - if currentGroup is not None: - currentGroup[1].append(processedLine) - else: - self._exceptionLineOutsideGroup(line, lineNo) - - # Finally, take the generated line groups and let the child class process - # each one before storing the final outcome. - return list(map(lambda group: self._processGroup(group[0], group[1], group[2]), allGroups)) - - -class CheckFile(FileSplitMixin): - """Collection of check groups extracted from the input test file.""" - - def __init__(self, prefix, checkStream, fileName=None): - self.fileName = fileName - self.prefix = prefix - self.groups = self._parseStream(checkStream) - - # Attempts to parse a check line. The regex searches for a comment symbol - # followed by the CHECK keyword, given attribute and a colon at the very - # beginning of the line. Whitespaces are ignored. - def _extractLine(self, prefix, line): - rIgnoreWhitespace = r"\s*" - rCommentSymbols = [r"//", r"#"] - regexPrefix = rIgnoreWhitespace + \ - r"(" + r"|".join(rCommentSymbols) + r")" + \ - rIgnoreWhitespace + \ - prefix + r":" - - # The 'match' function succeeds only if the pattern is matched at the - # beginning of the line. - match = re.match(regexPrefix, line) - if match is not None: - return line[match.end():].strip() - else: - return None - - # This function is invoked on each line of the check file and returns a pair - # which instructs the parser how the line should be handled. If the line is to - # be included in the current check group, it is returned in the first value. - # If the line starts a new check group, the name of the group is returned in - # the second value. - def _processLine(self, line, lineNo): - # Lines beginning with 'CHECK-START' start a new check group. - startLine = self._extractLine(self.prefix + "-START", line) - if startLine is not None: - return None, startLine - - # Lines starting only with 'CHECK' are matched in order. - plainLine = self._extractLine(self.prefix, line) - if plainLine is not None: - return (plainLine, CheckLine.Variant.InOrder, lineNo), None - - # 'CHECK-DAG' lines are no-order assertions. - dagLine = self._extractLine(self.prefix + "-DAG", line) - if dagLine is not None: - return (dagLine, CheckLine.Variant.DAG, lineNo), None - - # 'CHECK-NOT' lines are no-order negative assertions. - notLine = self._extractLine(self.prefix + "-NOT", line) - if notLine is not None: - return (notLine, CheckLine.Variant.Not, lineNo), None - - # Other lines are ignored. - return None, None - - def _exceptionLineOutsideGroup(self, line, lineNo): - Logger.fail("Check line not inside a group", self.fileName, lineNo) - - # Constructs a check group from the parser-collected check lines. - def _processGroup(self, name, lines, lineNo): - checkLines = list(map(lambda line: CheckLine(line[0], line[1], self.fileName, line[2]), lines)) - return CheckGroup(name, checkLines, self.fileName, lineNo) - - def match(self, outputFile): - for checkGroup in self.groups: - # TODO: Currently does not handle multiple occurrences of the same group - # name, e.g. when a pass is run multiple times. It will always try to - # match a check group against the first output group of the same name. - outputGroup = outputFile.findGroup(checkGroup.name) - if outputGroup is None: - Logger.fail("Group \"" + checkGroup.name + "\" not found in the output", - self.fileName, checkGroup.lineNo) - Logger.startTest(checkGroup.name) - checkGroup.match(outputGroup) - Logger.testPassed() - - -class OutputFile(FileSplitMixin): - """Representation of the output generated by the test and split into groups - within which the checks are performed. - - C1visualizer format is parsed with a state machine which differentiates - between the 'compilation' and 'cfg' blocks. The former marks the beginning - of a method. It is parsed for the method's name but otherwise ignored. Each - subsequent CFG block represents one stage of the compilation pipeline and - is parsed into an output group named "<method name> <pass name>". - """ - - class ParsingState: - OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4) - - def __init__(self, outputStream, fileName=None): - self.fileName = fileName - - # Initialize the state machine - self.lastMethodName = None - self.state = OutputFile.ParsingState.OutsideBlock - self.groups = self._parseStream(outputStream) - - # This function is invoked on each line of the output file and returns a pair - # which instructs the parser how the line should be handled. If the line is to - # be included in the current group, it is returned in the first value. If the - # line starts a new output group, the name of the group is returned in the - # second value. - def _processLine(self, line, lineNo): - if self.state == OutputFile.ParsingState.StartingCfgBlock: - # Previous line started a new 'cfg' block which means that this one must - # contain the name of the pass (this is enforced by C1visualizer). - if re.match("name\s+\"[^\"]+\"", line): - # Extract the pass name, prepend it with the name of the method and - # return as the beginning of a new group. - self.state = OutputFile.ParsingState.InsideCfgBlock - return (None, self.lastMethodName + " " + line.split("\"")[1]) - else: - Logger.fail("Expected output group name", self.fileName, lineNo) - - elif self.state == OutputFile.ParsingState.InsideCfgBlock: - if line == "end_cfg": - self.state = OutputFile.ParsingState.OutsideBlock - return (None, None) - else: - return (line, None) - - elif self.state == OutputFile.ParsingState.InsideCompilationBlock: - # Search for the method's name. Format: method "<name>" - if re.match("method\s+\"[^\"]*\"", line): - methodName = line.split("\"")[1].strip() - if not methodName: - Logger.fail("Empty method name in output", self.fileName, lineNo) - self.lastMethodName = methodName - elif line == "end_compilation": - self.state = OutputFile.ParsingState.OutsideBlock - return (None, None) - - else: - assert self.state == OutputFile.ParsingState.OutsideBlock - if line == "begin_cfg": - # The line starts a new group but we'll wait until the next line from - # which we can extract the name of the pass. - if self.lastMethodName is None: - Logger.fail("Expected method header", self.fileName, lineNo) - self.state = OutputFile.ParsingState.StartingCfgBlock - return (None, None) - elif line == "begin_compilation": - self.state = OutputFile.ParsingState.InsideCompilationBlock - return (None, None) - else: - Logger.fail("Output line not inside a group", self.fileName, lineNo) - - # Constructs an output group from the parser-collected output lines. - def _processGroup(self, name, lines, lineNo): - return OutputGroup(name, lines, self.fileName, lineNo + 1) - - def findGroup(self, name): - for group in self.groups: - if group.name == name: - return group - return None - - -def ParseArguments(): - parser = argparse.ArgumentParser() - parser.add_argument("tested_file", - help="text file the checks should be verified against") - parser.add_argument("source_path", nargs="?", - help="path to file/folder with checking annotations") - parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX", - help="prefix of checks in the test files (default: CHECK)") - parser.add_argument("--list-groups", dest="list_groups", action="store_true", - help="print a list of all groups found in the tested file") - parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP", - help="print the contents of an output group") - parser.add_argument("-q", "--quiet", action="store_true", - help="print only errors") - return parser.parse_args() - - -def ListGroups(outputFilename): - outputFile = OutputFile(open(outputFilename, "r")) - for group in outputFile.groups: - Logger.log(group.name) - - -def DumpGroup(outputFilename, groupName): - outputFile = OutputFile(open(outputFilename, "r")) - group = outputFile.findGroup(groupName) - if group: - lineNo = group.lineNo - maxLineNo = lineNo + len(group.body) - lenLineNo = len(str(maxLineNo)) + 2 - for line in group.body: - Logger.log((str(lineNo) + ":").ljust(lenLineNo) + line) - lineNo += 1 - else: - Logger.fail("Group \"" + groupName + "\" not found in the output") - - -# Returns a list of files to scan for check annotations in the given path. Path -# to a file is returned as a single-element list, directories are recursively -# traversed and all '.java' files returned. -def FindCheckFiles(path): - if not path: - Logger.fail("No source path provided") - elif os.path.isfile(path): - return [ path ] - elif os.path.isdir(path): - foundFiles = [] - for root, dirs, files in os.walk(path): - for file in files: - if os.path.splitext(file)[1] == ".java": - foundFiles.append(os.path.join(root, file)) - return foundFiles - else: - Logger.fail("Source path \"" + path + "\" not found") - - -def RunChecks(checkPrefix, checkPath, outputFilename): - outputBaseName = os.path.basename(outputFilename) - outputFile = OutputFile(open(outputFilename, "r"), outputBaseName) - - for checkFilename in FindCheckFiles(checkPath): - checkBaseName = os.path.basename(checkFilename) - checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName) - checkFile.match(outputFile) - - -if __name__ == "__main__": - args = ParseArguments() - - if args.quiet: - Logger.Verbosity = Logger.Level.Error - - if args.list_groups: - ListGroups(args.tested_file) - elif args.dump_group: - DumpGroup(args.tested_file, args.dump_group) - else: - RunChecks(args.check_prefix, args.source_path, args.tested_file) diff --git a/tools/checker/README b/tools/checker/README new file mode 100644 index 0000000000..276394826f --- /dev/null +++ b/tools/checker/README @@ -0,0 +1,54 @@ +Checker is a testing tool which compiles a given test file and compares the +state of the control-flow graph before and after each optimization pass +against a set of assertions specified alongside the tests. + +Tests are written in Java, turned into DEX and compiled with the Optimizing +compiler. "Check lines" are assertions formatted as comments of the Java file. +They begin with prefix 'CHECK' followed by a pattern that the engine attempts +to match in the compiler-generated output. + +Assertions are tested in groups which correspond to the individual compiler +passes. Each group of check lines therefore must start with a 'CHECK-START' +header which specifies the output group it should be tested against. The group +name must exactly match one of the groups recognized in the output (they can +be listed with the '--list-passes' command-line flag). + +Matching of check lines is carried out in the order of appearance in the +source file. There are three types of check lines: + - CHECK: Must match an output line which appears in the output group + later than lines matched against any preceeding checks. Output + lines must therefore match the check lines in the same order. + These are referred to as "in-order" checks in the code. + - CHECK-DAG: Must match an output line which appears in the output group + later than lines matched against any preceeding in-order checks. + In other words, the order of output lines does not matter + between consecutive DAG checks. + - CHECK-NOT: Must not match any output line which appears in the output group + later than lines matched against any preceeding checks and + earlier than lines matched against any subsequent checks. + Surrounding non-negative checks (or boundaries of the group) + therefore create a scope within which the assertion is verified. + +Check-line patterns are treated as plain text rather than regular expressions +but are whitespace agnostic. + +Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If +curly brackets need to be used inside the body of the regex, they need to be +enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse +the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'. + +Regex patterns can be named and referenced later. A new variable is defined +with '<<name:regex>>' and can be referenced with '<<name>>'. Variables are +only valid within the scope of the defining group. Within a group they cannot +be redefined or used undefined. + +Example: + The following assertions can be placed in a Java source file: + + // CHECK-START: int MyClass.MyMethod() constant_folding (after) + // CHECK: <<ID:i\d+>> IntConstant {{11|22}} + // CHECK: Return [ <<ID>> ] + + The engine will attempt to match the check lines against the output of the + group named on the first line. Together they verify that the CFG after + constant folding returns an integer constant with value either 11 or 22. diff --git a/tools/checker/checker.py b/tools/checker/checker.py new file mode 100755 index 0000000000..ed630e3d12 --- /dev/null +++ b/tools/checker/checker.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python2 +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os + +from common.logger import Logger +from file_format.c1visualizer.parser import ParseC1visualizerStream +from file_format.checker.parser import ParseCheckerStream +from match.file import MatchFiles + +def ParseArguments(): + parser = argparse.ArgumentParser() + parser.add_argument("tested_file", + help="text file the checks should be verified against") + parser.add_argument("source_path", nargs="?", + help="path to file/folder with checking annotations") + parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX", + help="prefix of checks in the test files (default: CHECK)") + parser.add_argument("--list-passes", dest="list_passes", action="store_true", + help="print a list of all passes found in the tested file") + parser.add_argument("--dump-pass", dest="dump_pass", metavar="PASS", + help="print a compiler pass dump") + parser.add_argument("-q", "--quiet", action="store_true", + help="print only errors") + return parser.parse_args() + + +def ListPasses(outputFilename): + c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r")) + for compiler_pass in c1File.passes: + Logger.log(compiler_pass.name) + + +def DumpPass(outputFilename, passName): + c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r")) + compiler_pass = c1File.findPass(passName) + if compiler_pass: + maxLineNo = compiler_pass.startLineNo + len(compiler_pass.body) + lenLineNo = len(str(maxLineNo)) + 2 + curLineNo = compiler_pass.startLineNo + for line in compiler_pass.body: + Logger.log((str(curLineNo) + ":").ljust(lenLineNo) + line) + curLineNo += 1 + else: + Logger.fail("Pass \"" + passName + "\" not found in the output") + + +def FindCheckerFiles(path): + """ Returns a list of files to scan for check annotations in the given path. + Path to a file is returned as a single-element list, directories are + recursively traversed and all '.java' files returned. + """ + if not path: + Logger.fail("No source path provided") + elif os.path.isfile(path): + return [ path ] + elif os.path.isdir(path): + foundFiles = [] + for root, dirs, files in os.walk(path): + for file in files: + extension = os.path.splitext(file)[1] + if extension in [".java", ".smali"]: + foundFiles.append(os.path.join(root, file)) + return foundFiles + else: + Logger.fail("Source path \"" + path + "\" not found") + + +def RunTests(checkPrefix, checkPath, outputFilename): + c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r")) + for checkFilename in FindCheckerFiles(checkPath): + checkerFile = ParseCheckerStream(os.path.basename(checkFilename), + checkPrefix, + open(checkFilename, "r")) + MatchFiles(checkerFile, c1File) + + +if __name__ == "__main__": + args = ParseArguments() + + if args.quiet: + Logger.Verbosity = Logger.Level.Error + + if args.list_passes: + ListPasses(args.tested_file) + elif args.dump_pass: + DumpPass(args.tested_file, args.dump_pass) + else: + RunTests(args.check_prefix, args.source_path, args.tested_file) diff --git a/tools/checker/common/__init__.py b/tools/checker/common/__init__.py new file mode 100644 index 0000000000..d0a140be2b --- /dev/null +++ b/tools/checker/common/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tools/checker/common/logger.py b/tools/checker/common/logger.py new file mode 100644 index 0000000000..28bb458da7 --- /dev/null +++ b/tools/checker/common/logger.py @@ -0,0 +1,81 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function +import sys + +class Logger(object): + + class Level(object): + NoOutput, Error, Info = range(3) + + class Color(object): + Default, Blue, Gray, Purple, Red = range(5) + + @staticmethod + def terminalCode(color, out=sys.stdout): + if not out.isatty(): + return '' + elif color == Logger.Color.Blue: + return '\033[94m' + elif color == Logger.Color.Gray: + return '\033[37m' + elif color == Logger.Color.Purple: + return '\033[95m' + elif color == Logger.Color.Red: + return '\033[91m' + else: + return '\033[0m' + + Verbosity = Level.Info + + @staticmethod + def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout): + if level <= Logger.Verbosity: + text = Logger.Color.terminalCode(color, out) + text + \ + Logger.Color.terminalCode(Logger.Color.Default, out) + if newLine: + print(text, file=out) + else: + print(text, end="", file=out) + out.flush() + + @staticmethod + def fail(msg, file=None, line=-1): + location = "" + if file: + location += file + ":" + if line > 0: + location += str(line) + ":" + if location: + location += " " + + Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) + Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr) + Logger.log(msg, Logger.Level.Error, out=sys.stderr) + sys.exit(msg) + + @staticmethod + def startTest(name): + Logger.log("TEST ", color=Logger.Color.Purple, newLine=False) + Logger.log(name + "... ", newLine=False) + + @staticmethod + def testPassed(): + Logger.log("PASS", color=Logger.Color.Blue) + + @staticmethod + def testFailed(msg, file=None, line=-1): + Logger.log("FAIL", color=Logger.Color.Red) + Logger.fail(msg, file, line) diff --git a/tools/checker/common/mixins.py b/tools/checker/common/mixins.py new file mode 100644 index 0000000000..819de240a7 --- /dev/null +++ b/tools/checker/common/mixins.py @@ -0,0 +1,26 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class EqualityMixin: + """ Object equality via equality of dictionaries. """ + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.__dict__ == other.__dict__ + +class PrintableMixin: + """ Prints object as name-dictionary pair. """ + + def __repr__(self): + return "<%s: %s>" % (type(self).__name__, str(self.__dict__)) diff --git a/tools/checker/common/testing.py b/tools/checker/common/testing.py new file mode 100644 index 0000000000..1299c07d5f --- /dev/null +++ b/tools/checker/common/testing.py @@ -0,0 +1,22 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +def ToUnicode(string): + """ Converts a string into Unicode. + + This is a delegate function for the built-in `unicode`. It checks if the input + is not `None`, because `unicode` turns it into an actual "None" string. + """ + assert string is not None + return unicode(string) diff --git a/tools/checker/file_format/__init__.py b/tools/checker/file_format/__init__.py new file mode 100644 index 0000000000..d0a140be2b --- /dev/null +++ b/tools/checker/file_format/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tools/checker/file_format/c1visualizer/__init__.py b/tools/checker/file_format/c1visualizer/__init__.py new file mode 100644 index 0000000000..d0a140be2b --- /dev/null +++ b/tools/checker/file_format/c1visualizer/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tools/checker/file_format/c1visualizer/parser.py b/tools/checker/file_format/c1visualizer/parser.py new file mode 100644 index 0000000000..335a195883 --- /dev/null +++ b/tools/checker/file_format/c1visualizer/parser.py @@ -0,0 +1,87 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from file_format.common import SplitStream +from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass + +import re + +class C1ParserState: + OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4) + + def __init__(self): + self.currentState = C1ParserState.OutsideBlock + self.lastMethodName = None + +def __parseC1Line(line, lineNo, state, fileName): + """ This function is invoked on each line of the output file and returns + a pair which instructs the parser how the line should be handled. If the + line is to be included in the current group, it is returned in the first + value. If the line starts a new output group, the name of the group is + returned in the second value. + """ + if state.currentState == C1ParserState.StartingCfgBlock: + # Previous line started a new 'cfg' block which means that this one must + # contain the name of the pass (this is enforced by C1visualizer). + if re.match("name\s+\"[^\"]+\"", line): + # Extract the pass name, prepend it with the name of the method and + # return as the beginning of a new group. + state.currentState = C1ParserState.InsideCfgBlock + return (None, state.lastMethodName + " " + line.split("\"")[1]) + else: + Logger.fail("Expected output group name", fileName, lineNo) + + elif state.currentState == C1ParserState.InsideCfgBlock: + if line == "end_cfg": + state.currentState = C1ParserState.OutsideBlock + return (None, None) + else: + return (line, None) + + elif state.currentState == C1ParserState.InsideCompilationBlock: + # Search for the method's name. Format: method "<name>" + if re.match("method\s+\"[^\"]*\"", line): + methodName = line.split("\"")[1].strip() + if not methodName: + Logger.fail("Empty method name in output", fileName, lineNo) + state.lastMethodName = methodName + elif line == "end_compilation": + state.currentState = C1ParserState.OutsideBlock + return (None, None) + + else: + assert state.currentState == C1ParserState.OutsideBlock + if line == "begin_cfg": + # The line starts a new group but we'll wait until the next line from + # which we can extract the name of the pass. + if state.lastMethodName is None: + Logger.fail("Expected method header", fileName, lineNo) + state.currentState = C1ParserState.StartingCfgBlock + return (None, None) + elif line == "begin_compilation": + state.currentState = C1ParserState.InsideCompilationBlock + return (None, None) + else: + Logger.fail("C1visualizer line not inside a group", fileName, lineNo) + +def ParseC1visualizerStream(fileName, stream): + c1File = C1visualizerFile(fileName) + state = C1ParserState() + fnProcessLine = lambda line, lineNo: __parseC1Line(line, lineNo, state, fileName) + fnLineOutsideChunk = lambda line, lineNo: \ + Logger.fail("C1visualizer line not inside a group", fileName, lineNo) + for passName, passLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk): + C1visualizerPass(c1File, passName, passLines, startLineNo + 1) + return c1File diff --git a/tools/checker/file_format/c1visualizer/struct.py b/tools/checker/file_format/c1visualizer/struct.py new file mode 100644 index 0000000000..991564eff4 --- /dev/null +++ b/tools/checker/file_format/c1visualizer/struct.py @@ -0,0 +1,60 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from common.mixins import PrintableMixin + +class C1visualizerFile(PrintableMixin): + + def __init__(self, fileName): + self.fileName = fileName + self.passes = [] + + def addPass(self, new_pass): + self.passes.append(new_pass) + + def findPass(self, name): + for entry in self.passes: + if entry.name == name: + return entry + return None + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.passes == other.passes + + +class C1visualizerPass(PrintableMixin): + + def __init__(self, parent, name, body, startLineNo): + self.parent = parent + self.name = name + self.body = body + self.startLineNo = startLineNo + + if not self.name: + Logger.fail("C1visualizer pass does not have a name", self.fileName, self.startLineNo) + if not self.body: + Logger.fail("C1visualizer pass does not have a body", self.fileName, self.startLineNo) + + self.parent.addPass(self) + + @property + def fileName(self): + return self.parent.fileName + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.name == other.name \ + and self.body == other.body diff --git a/tools/checker/file_format/c1visualizer/test.py b/tools/checker/file_format/c1visualizer/test.py new file mode 100644 index 0000000000..812a4cf9ce --- /dev/null +++ b/tools/checker/file_format/c1visualizer/test.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python2 +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.testing import ToUnicode +from file_format.c1visualizer.parser import ParseC1visualizerStream +from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass + +import io +import unittest + +class C1visualizerParser_Test(unittest.TestCase): + + def createFile(self, passList): + """ Creates an instance of CheckerFile from provided info. + + Data format: [ ( <case-name>, [ ( <text>, <assert-variant> ), ... ] ), ... ] + """ + c1File = C1visualizerFile("<c1_file>") + for passEntry in passList: + passName = passEntry[0] + passBody = passEntry[1] + c1Pass = C1visualizerPass(c1File, passName, passBody, 0) + return c1File + + def assertParsesTo(self, c1Text, expectedData): + expectedFile = self.createFile(expectedData) + actualFile = ParseC1visualizerStream("<c1_file>", io.StringIO(ToUnicode(c1Text))) + return self.assertEqual(expectedFile, actualFile) + + def test_EmptyFile(self): + self.assertParsesTo("", []) + + def test_SingleGroup(self): + self.assertParsesTo( + """ + begin_compilation + method "MyMethod" + end_compilation + begin_cfg + name "pass1" + foo + bar + end_cfg + """, + [ ( "MyMethod pass1", [ "foo", "bar" ] ) ]) + + def test_MultipleGroups(self): + self.assertParsesTo( + """ + begin_compilation + name "xyz1" + method "MyMethod1" + date 1234 + end_compilation + begin_cfg + name "pass1" + foo + bar + end_cfg + begin_cfg + name "pass2" + abc + def + end_cfg + """, + [ ( "MyMethod1 pass1", [ "foo", "bar" ] ), + ( "MyMethod1 pass2", [ "abc", "def" ] ) ]) + self.assertParsesTo( + """ + begin_compilation + name "xyz1" + method "MyMethod1" + date 1234 + end_compilation + begin_cfg + name "pass1" + foo + bar + end_cfg + begin_compilation + name "xyz2" + method "MyMethod2" + date 5678 + end_compilation + begin_cfg + name "pass2" + abc + def + end_cfg + """, + [ ( "MyMethod1 pass1", [ "foo", "bar" ] ), + ( "MyMethod2 pass2", [ "abc", "def" ] ) ]) diff --git a/tools/checker/file_format/checker/__init__.py b/tools/checker/file_format/checker/__init__.py new file mode 100644 index 0000000000..d0a140be2b --- /dev/null +++ b/tools/checker/file_format/checker/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py new file mode 100644 index 0000000000..d7a38dab4c --- /dev/null +++ b/tools/checker/file_format/checker/parser.py @@ -0,0 +1,142 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from file_format.common import SplitStream +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression + +import re + +def __extractLine(prefix, line): + """ Attempts to parse a check line. The regex searches for a comment symbol + followed by the CHECK keyword, given attribute and a colon at the very + beginning of the line. Whitespaces are ignored. + """ + rIgnoreWhitespace = r"\s*" + rCommentSymbols = [r"//", r"#"] + regexPrefix = rIgnoreWhitespace + \ + r"(" + r"|".join(rCommentSymbols) + r")" + \ + rIgnoreWhitespace + \ + prefix + r":" + + # The 'match' function succeeds only if the pattern is matched at the + # beginning of the line. + match = re.match(regexPrefix, line) + if match is not None: + return line[match.end():].strip() + else: + return None + +def __processLine(line, lineNo, prefix): + """ This function is invoked on each line of the check file and returns a pair + which instructs the parser how the line should be handled. If the line is + to be included in the current check group, it is returned in the first + value. If the line starts a new check group, the name of the group is + returned in the second value. + """ + # Lines beginning with 'CHECK-START' start a new test case. + startLine = __extractLine(prefix + "-START", line) + if startLine is not None: + return None, startLine + + # Lines starting only with 'CHECK' are matched in order. + plainLine = __extractLine(prefix, line) + if plainLine is not None: + return (plainLine, TestAssertion.Variant.InOrder, lineNo), None + + # 'CHECK-DAG' lines are no-order assertions. + dagLine = __extractLine(prefix + "-DAG", line) + if dagLine is not None: + return (dagLine, TestAssertion.Variant.DAG, lineNo), None + + # 'CHECK-NOT' lines are no-order negative assertions. + notLine = __extractLine(prefix + "-NOT", line) + if notLine is not None: + return (notLine, TestAssertion.Variant.Not, lineNo), None + + # Other lines are ignored. + return None, None + +def __isMatchAtStart(match): + """ Tests if the given Match occurred at the beginning of the line. """ + return (match is not None) and (match.start() == 0) + +def __firstMatch(matches, string): + """ Takes in a list of Match objects and returns the minimal start point among + them. If there aren't any successful matches it returns the length of + the searched string. + """ + starts = map(lambda m: len(string) if m is None else m.start(), matches) + return min(starts) + +def ParseCheckerAssertion(parent, line, variant, lineNo): + """ This method parses the content of a check line stripped of the initial + comment symbol and the CHECK keyword. + """ + assertion = TestAssertion(parent, variant, line, lineNo) + # Loop as long as there is something to parse. + while line: + # Search for the nearest occurrence of the special markers. + matchWhitespace = re.search(r"\s+", line) + matchPattern = re.search(RegexExpression.Regex.regexPattern, line) + matchVariableReference = re.search(RegexExpression.Regex.regexVariableReference, line) + matchVariableDefinition = re.search(RegexExpression.Regex.regexVariableDefinition, line) + + # If one of the above was identified at the current position, extract them + # from the line, parse them and add to the list of line parts. + if __isMatchAtStart(matchWhitespace): + # A whitespace in the check line creates a new separator of line parts. + # This allows for ignored output between the previous and next parts. + line = line[matchWhitespace.end():] + assertion.addExpression(RegexExpression.createSeparator()) + elif __isMatchAtStart(matchPattern): + pattern = line[0:matchPattern.end()] + pattern = pattern[2:-2] + line = line[matchPattern.end():] + assertion.addExpression(RegexExpression.createPattern(pattern)) + elif __isMatchAtStart(matchVariableReference): + var = line[0:matchVariableReference.end()] + line = line[matchVariableReference.end():] + name = var[2:-2] + assertion.addExpression(RegexExpression.createVariableReference(name)) + elif __isMatchAtStart(matchVariableDefinition): + var = line[0:matchVariableDefinition.end()] + line = line[matchVariableDefinition.end():] + colonPos = var.find(":") + name = var[2:colonPos] + body = var[colonPos+1:-2] + assertion.addExpression(RegexExpression.createVariableDefinition(name, body)) + else: + # If we're not currently looking at a special marker, this is a plain + # text match all the way until the first special marker (or the end + # of the line). + firstMatch = __firstMatch([ matchWhitespace, + matchPattern, + matchVariableReference, + matchVariableDefinition ], + line) + text = line[0:firstMatch] + line = line[firstMatch:] + assertion.addExpression(RegexExpression.createText(text)) + return assertion + +def ParseCheckerStream(fileName, prefix, stream): + checkerFile = CheckerFile(fileName) + fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix) + fnLineOutsideChunk = lambda line, lineNo: \ + Logger.fail("C1visualizer line not inside a group", fileName, lineNo) + for caseName, caseLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk): + testCase = TestCase(checkerFile, caseName, startLineNo) + for caseLine in caseLines: + ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2]) + return checkerFile diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py new file mode 100644 index 0000000000..381c92bb2a --- /dev/null +++ b/tools/checker/file_format/checker/struct.py @@ -0,0 +1,156 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from common.mixins import EqualityMixin, PrintableMixin + +import re + +class CheckerFile(PrintableMixin): + + def __init__(self, fileName): + self.fileName = fileName + self.testCases = [] + + def addTestCase(self, new_test_case): + self.testCases.append(new_test_case) + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.testCases == other.testCases + + +class TestCase(PrintableMixin): + + def __init__(self, parent, name, startLineNo): + assert isinstance(parent, CheckerFile) + + self.parent = parent + self.name = name + self.assertions = [] + self.startLineNo = startLineNo + + if not self.name: + Logger.fail("Test case does not have a name", self.parent.fileName, self.startLineNo) + + self.parent.addTestCase(self) + + @property + def fileName(self): + return self.parent.fileName + + def addAssertion(self, new_assertion): + self.assertions.append(new_assertion) + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.name == other.name \ + and self.assertions == other.assertions + + +class TestAssertion(PrintableMixin): + + class Variant(object): + """Supported types of assertions.""" + InOrder, DAG, Not = range(3) + + def __init__(self, parent, variant, originalText, lineNo): + assert isinstance(parent, TestCase) + + self.parent = parent + self.variant = variant + self.expressions = [] + self.lineNo = lineNo + self.originalText = originalText + + self.parent.addAssertion(self) + + @property + def fileName(self): + return self.parent.fileName + + def addExpression(self, new_expression): + assert isinstance(new_expression, RegexExpression) + if self.variant == TestAssertion.Variant.Not: + if new_expression.variant == RegexExpression.Variant.VarDef: + Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) + self.expressions.append(new_expression) + + def toRegex(self): + """ Returns a regex pattern for this entire assertion. Only used in tests. """ + regex = "" + for expression in self.expressions: + if expression.variant == RegexExpression.Variant.Separator: + regex = regex + ", " + else: + regex = regex + "(" + expression.pattern + ")" + return regex + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.variant == other.variant \ + and self.expressions == other.expressions + + +class RegexExpression(EqualityMixin, PrintableMixin): + + class Variant(object): + """Supported language constructs.""" + Text, Pattern, VarRef, VarDef, Separator = range(5) + + class Regex(object): + rName = r"([a-zA-Z][a-zA-Z0-9]*)" + rRegex = r"(.+?)" + rPatternStartSym = r"(\{\{)" + rPatternEndSym = r"(\}\})" + rVariableStartSym = r"(<<)" + rVariableEndSym = r"(>>)" + rVariableSeparator = r"(:)" + + regexPattern = rPatternStartSym + rRegex + rPatternEndSym + regexVariableReference = rVariableStartSym + rName + rVariableEndSym + regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym + + def __init__(self, variant, name, pattern): + self.variant = variant + self.name = name + self.pattern = pattern + + def __eq__(self, other): + return isinstance(other, self.__class__) \ + and self.variant == other.variant \ + and self.name == other.name \ + and self.pattern == other.pattern + + @staticmethod + def createSeparator(): + return RegexExpression(RegexExpression.Variant.Separator, None, None) + + @staticmethod + def createText(text): + return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text)) + + @staticmethod + def createPattern(pattern): + return RegexExpression(RegexExpression.Variant.Pattern, None, pattern) + + @staticmethod + def createVariableReference(name): + assert re.match(RegexExpression.Regex.rName, name) + return RegexExpression(RegexExpression.Variant.VarRef, name, None) + + @staticmethod + def createVariableDefinition(name, pattern): + assert re.match(RegexExpression.Regex.rName, name) + return RegexExpression(RegexExpression.Variant.VarDef, name, pattern) diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py new file mode 100644 index 0000000000..475e8c3d07 --- /dev/null +++ b/tools/checker/file_format/checker/test.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python2 +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.testing import ToUnicode +from file_format.checker.parser import ParseCheckerStream +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression + +import io +import unittest + +CheckerException = SystemExit + +class CheckerParser_PrefixTest(unittest.TestCase): + + def tryParse(self, string): + checkerText = u"// CHECK-START: pass\n" + ToUnicode(string) + checkFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText)) + self.assertEqual(len(checkFile.testCases), 1) + testCase = checkFile.testCases[0] + return len(testCase.assertions) != 0 + + def test_InvalidFormat(self): + self.assertFalse(self.tryParse("CHECK")) + self.assertFalse(self.tryParse(":CHECK")) + self.assertFalse(self.tryParse("CHECK:")) + self.assertFalse(self.tryParse("//CHECK")) + self.assertFalse(self.tryParse("#CHECK")) + + self.assertTrue(self.tryParse("//CHECK:foo")) + self.assertTrue(self.tryParse("#CHECK:bar")) + + def test_InvalidLabel(self): + self.assertFalse(self.tryParse("//ACHECK:foo")) + self.assertFalse(self.tryParse("#ACHECK:foo")) + + def test_NotFirstOnTheLine(self): + self.assertFalse(self.tryParse("A// CHECK: foo")) + self.assertFalse(self.tryParse("A # CHECK: foo")) + self.assertFalse(self.tryParse("// // CHECK: foo")) + self.assertFalse(self.tryParse("# # CHECK: foo")) + + def test_WhitespaceAgnostic(self): + self.assertTrue(self.tryParse(" //CHECK: foo")) + self.assertTrue(self.tryParse("// CHECK: foo")) + self.assertTrue(self.tryParse(" //CHECK: foo")) + self.assertTrue(self.tryParse("// CHECK: foo")) + + +class CheckerParser_RegexExpressionTest(unittest.TestCase): + + def parseAssertion(self, string, variant=""): + checkerText = u"// CHECK-START: pass\n// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string) + checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText)) + self.assertEqual(len(checkerFile.testCases), 1) + testCase = checkerFile.testCases[0] + self.assertEqual(len(testCase.assertions), 1) + return testCase.assertions[0] + + def parseExpression(self, string): + line = self.parseAssertion(string) + self.assertEqual(1, len(line.expressions)) + return line.expressions[0] + + def assertEqualsRegex(self, string, expected): + self.assertEqual(expected, self.parseAssertion(string).toRegex()) + + def assertEqualsText(self, string, text): + self.assertEqual(self.parseExpression(string), RegexExpression.createText(text)) + + def assertEqualsPattern(self, string, pattern): + self.assertEqual(self.parseExpression(string), RegexExpression.createPattern(pattern)) + + def assertEqualsVarRef(self, string, name): + self.assertEqual(self.parseExpression(string), RegexExpression.createVariableReference(name)) + + def assertEqualsVarDef(self, string, name, pattern): + self.assertEqual(self.parseExpression(string), + RegexExpression.createVariableDefinition(name, pattern)) + + def assertVariantNotEqual(self, string, variant): + self.assertNotEqual(variant, self.parseExpression(string).variant) + + # Test that individual parts of the line are recognized + + def test_TextOnly(self): + self.assertEqualsText("foo", "foo") + self.assertEqualsText(" foo ", "foo") + self.assertEqualsRegex("f$o^o", "(f\$o\^o)") + + def test_PatternOnly(self): + self.assertEqualsPattern("{{a?b.c}}", "a?b.c") + + def test_VarRefOnly(self): + self.assertEqualsVarRef("<<ABC>>", "ABC") + + def test_VarDefOnly(self): + self.assertEqualsVarDef("<<ABC:a?b.c>>", "ABC", "a?b.c") + + def test_TextWithWhitespace(self): + self.assertEqualsRegex("foo bar", "(foo), (bar)") + self.assertEqualsRegex("foo bar", "(foo), (bar)") + + def test_TextWithRegex(self): + self.assertEqualsRegex("foo{{abc}}bar", "(foo)(abc)(bar)") + + def test_TextWithVar(self): + self.assertEqualsRegex("foo<<ABC:abc>>bar", "(foo)(abc)(bar)") + + def test_PlainWithRegexAndWhitespaces(self): + self.assertEqualsRegex("foo {{abc}}bar", "(foo), (abc)(bar)") + self.assertEqualsRegex("foo{{abc}} bar", "(foo)(abc), (bar)") + self.assertEqualsRegex("foo {{abc}} bar", "(foo), (abc), (bar)") + + def test_PlainWithVarAndWhitespaces(self): + self.assertEqualsRegex("foo <<ABC:abc>>bar", "(foo), (abc)(bar)") + self.assertEqualsRegex("foo<<ABC:abc>> bar", "(foo)(abc), (bar)") + self.assertEqualsRegex("foo <<ABC:abc>> bar", "(foo), (abc), (bar)") + + def test_AllKinds(self): + self.assertEqualsRegex("foo <<ABC:abc>>{{def}}bar", "(foo), (abc)(def)(bar)") + self.assertEqualsRegex("foo<<ABC:abc>> {{def}}bar", "(foo)(abc), (def)(bar)") + self.assertEqualsRegex("foo <<ABC:abc>> {{def}} bar", "(foo), (abc), (def), (bar)") + + # # Test that variables and patterns are parsed correctly + + def test_ValidPattern(self): + self.assertEqualsPattern("{{abc}}", "abc") + self.assertEqualsPattern("{{a[b]c}}", "a[b]c") + self.assertEqualsPattern("{{(a{bc})}}", "(a{bc})") + + def test_ValidRef(self): + self.assertEqualsVarRef("<<ABC>>", "ABC") + self.assertEqualsVarRef("<<A1BC2>>", "A1BC2") + + def test_ValidDef(self): + self.assertEqualsVarDef("<<ABC:abc>>", "ABC", "abc") + self.assertEqualsVarDef("<<ABC:ab:c>>", "ABC", "ab:c") + self.assertEqualsVarDef("<<ABC:a[b]c>>", "ABC", "a[b]c") + self.assertEqualsVarDef("<<ABC:(a[bc])>>", "ABC", "(a[bc])") + + def test_Empty(self): + self.assertVariantNotEqual("{{}}", RegexExpression.Variant.Pattern) + self.assertVariantNotEqual("<<>>", RegexExpression.Variant.VarRef) + self.assertVariantNotEqual("<<:>>", RegexExpression.Variant.VarDef) + + def test_InvalidVarName(self): + self.assertVariantNotEqual("<<0ABC>>", RegexExpression.Variant.VarRef) + self.assertVariantNotEqual("<<AB=C>>", RegexExpression.Variant.VarRef) + self.assertVariantNotEqual("<<ABC=>>", RegexExpression.Variant.VarRef) + self.assertVariantNotEqual("<<0ABC:abc>>", RegexExpression.Variant.VarDef) + self.assertVariantNotEqual("<<AB=C:abc>>", RegexExpression.Variant.VarDef) + self.assertVariantNotEqual("<<ABC=:abc>>", RegexExpression.Variant.VarDef) + + def test_BodyMatchNotGreedy(self): + self.assertEqualsRegex("{{abc}}{{def}}", "(abc)(def)") + self.assertEqualsRegex("<<ABC:abc>><<DEF:def>>", "(abc)(def)") + + def test_NoVarDefsInNotChecks(self): + with self.assertRaises(CheckerException): + self.parseAssertion("<<ABC:abc>>", "-NOT") + + +class CheckerParser_FileLayoutTest(unittest.TestCase): + + # Creates an instance of CheckerFile from provided info. + # Data format: [ ( <case-name>, [ ( <text>, <assert-variant> ), ... ] ), ... ] + def createFile(self, caseList): + testFile = CheckerFile("<test_file>") + for caseEntry in caseList: + caseName = caseEntry[0] + testCase = TestCase(testFile, caseName, 0) + assertionList = caseEntry[1] + for assertionEntry in assertionList: + content = assertionEntry[0] + variant = assertionEntry[1] + assertion = TestAssertion(testCase, variant, content, 0) + assertion.addExpression(RegexExpression.createText(content)) + return testFile + + def assertParsesTo(self, checkerText, expectedData): + expectedFile = self.createFile(expectedData) + actualFile = ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText))) + return self.assertEqual(expectedFile, actualFile) + + def test_EmptyFile(self): + self.assertParsesTo("", []) + + def test_SingleGroup(self): + self.assertParsesTo( + """ + // CHECK-START: Example Group + // CHECK: foo + // CHECK: bar + """, + [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder), + ("bar", TestAssertion.Variant.InOrder) ] ) ]) + + def test_MultipleGroups(self): + self.assertParsesTo( + """ + // CHECK-START: Example Group1 + // CHECK: foo + // CHECK: bar + // CHECK-START: Example Group2 + // CHECK: abc + // CHECK: def + """, + [ ( "Example Group1", [ ("foo", TestAssertion.Variant.InOrder), + ("bar", TestAssertion.Variant.InOrder) ] ), + ( "Example Group2", [ ("abc", TestAssertion.Variant.InOrder), + ("def", TestAssertion.Variant.InOrder) ] ) ]) + + def test_AssertionVariants(self): + self.assertParsesTo( + """ + // CHECK-START: Example Group + // CHECK: foo + // CHECK-NOT: bar + // CHECK-DAG: abc + // CHECK-DAG: def + """, + [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder), + ("bar", TestAssertion.Variant.Not), + ("abc", TestAssertion.Variant.DAG), + ("def", TestAssertion.Variant.DAG) ] ) ]) diff --git a/tools/checker/file_format/common.py b/tools/checker/file_format/common.py new file mode 100644 index 0000000000..f91fdeb9cc --- /dev/null +++ b/tools/checker/file_format/common.py @@ -0,0 +1,48 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +def SplitStream(stream, fnProcessLine, fnLineOutsideChunk): + """ Reads the given input stream and splits it into chunks based on + information extracted from individual lines. + + Arguments: + - fnProcessLine: Called on each line with the text and line number. Must + return a pair, name of the chunk started on this line and data extracted + from this line (or None in both cases). + - fnLineOutsideChunk: Called on attempt to attach data prior to creating + a chunk. + """ + lineNo = 0 + allChunks = [] + currentChunk = None + + for line in stream: + lineNo += 1 + line = line.strip() + if not line: + continue + + # Let the child class process the line and return information about it. + # The _processLine method can modify the content of the line (or delete it + # entirely) and specify whether it starts a new group. + processedLine, newChunkName = fnProcessLine(line, lineNo) + if newChunkName is not None: + currentChunk = (newChunkName, [], lineNo) + allChunks.append(currentChunk) + if processedLine is not None: + if currentChunk is not None: + currentChunk[1].append(processedLine) + else: + fnLineOutsideChunk(line, lineNo) + return allChunks diff --git a/tools/checker/match/__init__.py b/tools/checker/match/__init__.py new file mode 100644 index 0000000000..d0a140be2b --- /dev/null +++ b/tools/checker/match/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py new file mode 100644 index 0000000000..2ed4aa7bc3 --- /dev/null +++ b/tools/checker/match/file.py @@ -0,0 +1,147 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion +from match.line import MatchLines + +def __headAndTail(list): + return list[0], list[1:] + +def __splitByVariant(lines, variant): + """ Splits a list of check lines at index 'i' such that lines[i] is the first + element whose variant is not equal to the given parameter. + """ + i = 0 + while i < len(lines) and lines[i].variant == variant: + i += 1 + return lines[:i], lines[i:] + +def __nextIndependentChecks(checkLines): + """ Extracts the first sequence of check lines which are independent of each + other's match location, i.e. either consecutive DAG lines or a single + InOrder line. Any Not lines preceeding this sequence are also extracted. + """ + notChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.Not) + if not checkLines: + return notChecks, [], [] + + head, tail = __headAndTail(checkLines) + if head.variant == TestAssertion.Variant.InOrder: + return notChecks, [head], tail + else: + assert head.variant == TestAssertion.Variant.DAG + independentChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.DAG) + return notChecks, independentChecks, checkLines + +def __findFirstMatch(checkLine, outputLines, startLineNo, lineFilter, varState): + """ If successful, returns the line number of the first output line matching + the check line and the updated variable state. Otherwise returns -1 and + None, respectively. The 'lineFilter' parameter can be used to supply a + list of line numbers (counting from 1) which should be skipped. + """ + matchLineNo = startLineNo + for outputLine in outputLines: + if matchLineNo not in lineFilter: + newVarState = MatchLines(checkLine, outputLine, varState) + if newVarState is not None: + return matchLineNo, newVarState + matchLineNo += 1 + return -1, None + +def __matchIndependentChecks(checkLines, outputLines, startLineNo, varState): + """ Matches the given positive check lines against the output in order of + appearance. Variable state is propagated but the scope of the search + remains the same for all checks. Each output line can only be matched + once. If all check lines are matched, the resulting variable state is + returned together with the remaining output. The function also returns + output lines which appear before either of the matched lines so they can + be tested against Not checks. + """ + # If no checks are provided, skip over the entire output. + if not checkLines: + return outputLines, [], startLineNo + len(outputLines), varState + + # Keep track of which lines have been matched. + matchedLines = [] + + # Find first unused output line which matches each check line. + for checkLine in checkLines: + matchLineNo, varState = \ + __findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState) + if varState is None: + Logger.testFailed("Could not match check line \"" + checkLine.originalText + "\" " + + "starting from output line " + str(startLineNo), + checkLine.fileName, checkLine.lineNo) + matchedLines.append(matchLineNo) + + # Return new variable state and the output lines which lie outside the + # match locations of this independent group. + minMatchLineNo = min(matchedLines) + maxMatchLineNo = max(matchedLines) + preceedingLines = outputLines[:minMatchLineNo - startLineNo] + remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:] + return preceedingLines, remainingLines, maxMatchLineNo + 1, varState + +def __matchNotLines(checkLines, outputLines, startLineNo, varState): + """ Makes sure that the given check lines do not match any of the given output + lines. Variable state does not change. + """ + for checkLine in checkLines: + assert checkLine.variant == TestAssertion.Variant.Not + matchLineNo, matchVarState = \ + __findFirstMatch(checkLine, outputLines, startLineNo, [], varState) + if matchVarState is not None: + Logger.testFailed("CHECK-NOT line \"" + checkLine.originalText + "\" matches output line " + \ + str(matchLineNo), checkLine.fileName, checkLine.lineNo) + +def __matchGroups(checkGroup, outputGroup): + """ Matches the check lines in this group against an output group. It is + responsible for running the checks in the right order and scope, and + for propagating the variable state between the check lines. + """ + varState = {} + checkLines = checkGroup.assertions + outputLines = outputGroup.body + startLineNo = outputGroup.startLineNo + + while checkLines: + # Extract the next sequence of location-independent checks to be matched. + notChecks, independentChecks, checkLines = __nextIndependentChecks(checkLines) + + # Match the independent checks. + notOutput, outputLines, newStartLineNo, newVarState = \ + __matchIndependentChecks(independentChecks, outputLines, startLineNo, varState) + + # Run the Not checks against the output lines which lie between the last + # two independent groups or the bounds of the output. + __matchNotLines(notChecks, notOutput, startLineNo, varState) + + # Update variable state. + startLineNo = newStartLineNo + varState = newVarState + +def MatchFiles(checkerFile, c1File): + for testCase in checkerFile.testCases: + # TODO: Currently does not handle multiple occurrences of the same group + # name, e.g. when a pass is run multiple times. It will always try to + # match a check group against the first output group of the same name. + c1Pass = c1File.findPass(testCase.name) + if c1Pass is None: + Logger.fail("Test case \"" + testCase.name + "\" not found in the C1visualizer output", + testCase.fileName, testCase.startLineNo) + Logger.startTest(testCase.name) + __matchGroups(testCase, c1Pass) + Logger.testPassed() diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py new file mode 100644 index 0000000000..f0253c351b --- /dev/null +++ b/tools/checker/match/line.py @@ -0,0 +1,89 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from file_format.checker.struct import TestAssertion, RegexExpression + +import re + +def __isMatchAtStart(match): + """ Tests if the given Match occurred at the beginning of the line. """ + return (match is not None) and (match.start() == 0) + +def __generatePattern(checkLine, linePart, varState): + """ Returns the regex pattern to be matched in the output line. Variable + references are substituted with their current values provided in the + 'varState' argument. + + An exception is raised if a referenced variable is undefined. + """ + if linePart.variant == RegexExpression.Variant.VarRef: + try: + return re.escape(varState[linePart.name]) + except KeyError: + Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"", + checkLine.fileName, checkLine.lineNo) + else: + return linePart.pattern + +def __isSeparated(outputLine, matchStart): + return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace()) + +def MatchLines(checkLine, outputLine, initialVarState): + """ Attempts to match the check line against a line from the output file with + the given initial variable values. It returns the new variable state if + successful and None otherwise. + """ + # Do the full matching on a shadow copy of the variable state. If the + # matching fails half-way, we will not need to revert the state. + varState = dict(initialVarState) + + matchStart = 0 + isAfterSeparator = True + + # Now try to parse all of the parts of the check line in the right order. + # Variable values are updated on-the-fly, meaning that a variable can + # be referenced immediately after its definition. + for part in checkLine.expressions: + if part.variant == RegexExpression.Variant.Separator: + isAfterSeparator = True + continue + + # Find the earliest match for this line part. + pattern = __generatePattern(checkLine, part, varState) + while True: + match = re.search(pattern, outputLine[matchStart:]) + if (match is None) or (not isAfterSeparator and not __isMatchAtStart(match)): + return None + matchEnd = matchStart + match.end() + matchStart += match.start() + + # Check if this is a valid match if we expect a whitespace separator + # before the matched text. Otherwise loop and look for another match. + if not isAfterSeparator or __isSeparated(outputLine, matchStart): + break + else: + matchStart += 1 + + if part.variant == RegexExpression.Variant.VarDef: + if part.name in varState: + Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"", + checkLine.fileName, checkLine.lineNo) + varState[part.name] = outputLine[matchStart:matchEnd] + + matchStart = matchEnd + isAfterSeparator = False + + # All parts were successfully matched. Return the new variable state. + return varState diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py new file mode 100644 index 0000000000..bb3b1af388 --- /dev/null +++ b/tools/checker/match/test.py @@ -0,0 +1,326 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.testing import ToUnicode +from file_format.c1visualizer.parser import ParseC1visualizerStream +from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass +from file_format.checker.parser import ParseCheckerStream, ParseCheckerAssertion +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression +from match.file import MatchFiles +from match.line import MatchLines + +import io +import unittest + +CheckerException = SystemExit + +class MatchLines_Test(unittest.TestCase): + + def createTestAssertion(self, checkerString): + checkerFile = CheckerFile("<checker-file>") + testCase = TestCase(checkerFile, "TestMethod TestPass", 0) + return ParseCheckerAssertion(testCase, checkerString, TestAssertion.Variant.InOrder, 0) + + def tryMatch(self, checkerString, c1String, varState={}): + return MatchLines(self.createTestAssertion(checkerString), ToUnicode(c1String), varState) + + def matches(self, checkerString, c1String, varState={}): + return self.tryMatch(checkerString, c1String, varState) is not None + + def test_TextAndWhitespace(self): + self.assertTrue(self.matches("foo", "foo")) + self.assertTrue(self.matches("foo", " foo ")) + self.assertTrue(self.matches("foo", "foo bar")) + self.assertFalse(self.matches("foo", "XfooX")) + self.assertFalse(self.matches("foo", "zoo")) + + self.assertTrue(self.matches("foo bar", "foo bar")) + self.assertTrue(self.matches("foo bar", "abc foo bar def")) + self.assertTrue(self.matches("foo bar", "foo foo bar bar")) + + self.assertTrue(self.matches("foo bar", "foo X bar")) + self.assertFalse(self.matches("foo bar", "foo Xbar")) + + def test_Pattern(self): + self.assertTrue(self.matches("foo{{A|B}}bar", "fooAbar")) + self.assertTrue(self.matches("foo{{A|B}}bar", "fooBbar")) + self.assertFalse(self.matches("foo{{A|B}}bar", "fooCbar")) + + def test_VariableReference(self): + self.assertTrue(self.matches("foo<<X>>bar", "foobar", {"X": ""})) + self.assertTrue(self.matches("foo<<X>>bar", "fooAbar", {"X": "A"})) + self.assertTrue(self.matches("foo<<X>>bar", "fooBbar", {"X": "B"})) + self.assertFalse(self.matches("foo<<X>>bar", "foobar", {"X": "A"})) + self.assertFalse(self.matches("foo<<X>>bar", "foo bar", {"X": "A"})) + with self.assertRaises(CheckerException): + self.assertTrue(self.matches("foo<<X>>bar", "foobar", {})) + + def test_VariableDefinition(self): + self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooAbar")) + self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooBbar")) + self.assertFalse(self.matches("foo<<X:A|B>>bar", "fooCbar")) + + env = self.tryMatch("foo<<X:A.*B>>bar", "fooABbar", {}) + self.assertEqual(env, {"X": "AB"}) + env = self.tryMatch("foo<<X:A.*B>>bar", "fooAxxBbar", {}) + self.assertEqual(env, {"X": "AxxB"}) + + self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz")) + self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz")) + self.assertFalse(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz")) + + def test_NoVariableRedefinition(self): + with self.assertRaises(CheckerException): + self.matches("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar") + + def test_EnvNotChangedOnPartialMatch(self): + env = {"Y": "foo"} + self.assertFalse(self.matches("<<X:A>>bar", "Abaz", env)) + self.assertFalse("X" in env.keys()) + + def test_VariableContentEscaped(self): + self.assertTrue(self.matches("<<X:..>>foo<<X>>", ".*foo.*")) + self.assertFalse(self.matches("<<X:..>>foo<<X>>", ".*fooAAAA")) + + +class MatchFiles_Test(unittest.TestCase): + + def matches(self, checkerString, c1String): + checkerString = \ + """ + // CHECK-START: MyMethod MyPass + """ + checkerString + c1String = \ + """ + begin_compilation + name "MyMethod" + method "MyMethod" + date 1234 + end_compilation + begin_cfg + name "MyPass" + """ + c1String + \ + """ + end_cfg + """ + checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(ToUnicode(checkerString))) + c1File = ParseC1visualizerStream("<c1-file>", io.StringIO(ToUnicode(c1String))) + try: + MatchFiles(checkerFile, c1File) + return True + except CheckerException: + return False + + def test_Text(self): + self.assertTrue(self.matches( "// CHECK: foo bar", "foo bar")) + self.assertFalse(self.matches("// CHECK: foo bar", "abc def")) + + def test_Pattern(self): + self.assertTrue(self.matches( "// CHECK: abc {{de.}}", "abc de#")) + self.assertFalse(self.matches("// CHECK: abc {{de.}}", "abc d#f")) + + def test_Variables(self): + self.assertTrue(self.matches( + """ + // CHECK: foo<<X:.>>bar + // CHECK: abc<<X>>def + """, + """ + foo bar + abc def + """)) + self.assertTrue(self.matches( + """ + // CHECK: foo<<X:([0-9]+)>>bar + // CHECK: abc<<X>>def + // CHECK: ### <<X>> ### + """, + """ + foo1234bar + abc1234def + ### 1234 ### + """)) + self.assertFalse(self.matches( + """ + // CHECK: foo<<X:([0-9]+)>>bar + // CHECK: abc<<X>>def + """, + """ + foo1234bar + abc1235def + """)) + + def test_InOrderAssertions(self): + self.assertTrue(self.matches( + """ + // CHECK: foo + // CHECK: bar + """, + """ + foo + bar + """)) + self.assertFalse(self.matches( + """ + // CHECK: foo + // CHECK: bar + """, + """ + bar + foo + """)) + + def test_DagAssertions(self): + self.assertTrue(self.matches( + """ + // CHECK-DAG: foo + // CHECK-DAG: bar + """, + """ + foo + bar + """)) + self.assertTrue(self.matches( + """ + // CHECK-DAG: foo + // CHECK-DAG: bar + """, + """ + bar + foo + """)) + + def test_DagAssertionsScope(self): + self.assertTrue(self.matches( + """ + // CHECK: foo + // CHECK-DAG: abc + // CHECK-DAG: def + // CHECK: bar + """, + """ + foo + def + abc + bar + """)) + self.assertFalse(self.matches( + """ + // CHECK: foo + // CHECK-DAG: abc + // CHECK-DAG: def + // CHECK: bar + """, + """ + foo + abc + bar + def + """)) + self.assertFalse(self.matches( + """ + // CHECK: foo + // CHECK-DAG: abc + // CHECK-DAG: def + // CHECK: bar + """, + """ + foo + def + bar + abc + """)) + + def test_NotAssertions(self): + self.assertTrue(self.matches( + """ + // CHECK-NOT: foo + """, + """ + abc + def + """)) + self.assertFalse(self.matches( + """ + // CHECK-NOT: foo + """, + """ + abc foo + def + """)) + self.assertFalse(self.matches( + """ + // CHECK-NOT: foo + // CHECK-NOT: bar + """, + """ + abc + def bar + """)) + + def test_NotAssertionsScope(self): + self.assertTrue(self.matches( + """ + // CHECK: abc + // CHECK-NOT: foo + // CHECK: def + """, + """ + abc + def + """)) + self.assertTrue(self.matches( + """ + // CHECK: abc + // CHECK-NOT: foo + // CHECK: def + """, + """ + abc + def + foo + """)) + self.assertFalse(self.matches( + """ + // CHECK: abc + // CHECK-NOT: foo + // CHECK: def + """, + """ + abc + foo + def + """)) + + def test_LineOnlyMatchesOnce(self): + self.assertTrue(self.matches( + """ + // CHECK-DAG: foo + // CHECK-DAG: foo + """, + """ + foo + abc + foo + """)) + self.assertFalse(self.matches( + """ + // CHECK-DAG: foo + // CHECK-DAG: foo + """, + """ + foo + abc + bar + """)) diff --git a/tools/checker/run_unit_tests.py b/tools/checker/run_unit_tests.py new file mode 100755 index 0000000000..01708dbd27 --- /dev/null +++ b/tools/checker/run_unit_tests.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python2 +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.logger import Logger +from file_format.c1visualizer.test import C1visualizerParser_Test +from file_format.checker.test import CheckerParser_PrefixTest, \ + CheckerParser_RegexExpressionTest, \ + CheckerParser_FileLayoutTest +from match.test import MatchLines_Test, \ + MatchFiles_Test + +import unittest + +if __name__ == '__main__': + Logger.Verbosity = Logger.Level.NoOutput + unittest.main(verbosity=2) diff --git a/tools/checker_test.py b/tools/checker_test.py deleted file mode 100755 index 667ca90079..0000000000 --- a/tools/checker_test.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/env python2 -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a test file which exercises all feautres supported by the domain- -# specific markup language implemented by Checker. - -import checker -import io -import unittest - -# The parent type of exception expected to be thrown by Checker during tests. -# It must be specific enough to not cover exceptions thrown due to actual flaws -# in Checker. -CheckerException = SystemExit - - -class TestCheckFile_PrefixExtraction(unittest.TestCase): - def __tryParse(self, string): - checkFile = checker.CheckFile(None, []) - return checkFile._extractLine("CHECK", string) - - def test_InvalidFormat(self): - self.assertIsNone(self.__tryParse("CHECK")) - self.assertIsNone(self.__tryParse(":CHECK")) - self.assertIsNone(self.__tryParse("CHECK:")) - self.assertIsNone(self.__tryParse("//CHECK")) - self.assertIsNone(self.__tryParse("#CHECK")) - - self.assertIsNotNone(self.__tryParse("//CHECK:foo")) - self.assertIsNotNone(self.__tryParse("#CHECK:bar")) - - def test_InvalidLabel(self): - self.assertIsNone(self.__tryParse("//ACHECK:foo")) - self.assertIsNone(self.__tryParse("#ACHECK:foo")) - - def test_NotFirstOnTheLine(self): - self.assertIsNone(self.__tryParse("A// CHECK: foo")) - self.assertIsNone(self.__tryParse("A # CHECK: foo")) - self.assertIsNone(self.__tryParse("// // CHECK: foo")) - self.assertIsNone(self.__tryParse("# # CHECK: foo")) - - def test_WhitespaceAgnostic(self): - self.assertIsNotNone(self.__tryParse(" //CHECK: foo")) - self.assertIsNotNone(self.__tryParse("// CHECK: foo")) - self.assertIsNotNone(self.__tryParse(" //CHECK: foo")) - self.assertIsNotNone(self.__tryParse("// CHECK: foo")) - - -class TestCheckLine_Parse(unittest.TestCase): - def __getPartPattern(self, linePart): - if linePart.variant == checker.CheckElement.Variant.Separator: - return "\s+" - else: - return linePart.pattern - - def __getRegex(self, checkLine): - return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts)) - - def __tryParse(self, string): - return checker.CheckLine(string) - - def __parsesTo(self, string, expected): - self.assertEqual(expected, self.__getRegex(self.__tryParse(string))) - - def __tryParseNot(self, string): - return checker.CheckLine(string, checker.CheckLine.Variant.Not) - - def __parsesPattern(self, string, pattern): - line = self.__tryParse(string) - self.assertEqual(1, len(line.lineParts)) - self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant) - self.assertEqual(pattern, line.lineParts[0].pattern) - - def __parsesVarRef(self, string, name): - line = self.__tryParse(string) - self.assertEqual(1, len(line.lineParts)) - self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant) - self.assertEqual(name, line.lineParts[0].name) - - def __parsesVarDef(self, string, name, body): - line = self.__tryParse(string) - self.assertEqual(1, len(line.lineParts)) - self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant) - self.assertEqual(name, line.lineParts[0].name) - self.assertEqual(body, line.lineParts[0].pattern) - - def __doesNotParse(self, string, partType): - line = self.__tryParse(string) - self.assertEqual(1, len(line.lineParts)) - self.assertNotEqual(partType, line.lineParts[0].variant) - - # Test that individual parts of the line are recognized - - def test_TextOnly(self): - self.__parsesTo("foo", "(foo)") - self.__parsesTo(" foo ", "(foo)") - self.__parsesTo("f$o^o", "(f\$o\^o)") - - def test_TextWithWhitespace(self): - self.__parsesTo("foo bar", "(foo)(\s+)(bar)") - self.__parsesTo("foo bar", "(foo)(\s+)(bar)") - - def test_RegexOnly(self): - self.__parsesPattern("{{a?b.c}}", "a?b.c") - - def test_VarRefOnly(self): - self.__parsesVarRef("[[ABC]]", "ABC") - - def test_VarDefOnly(self): - self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c") - - def test_TextWithRegex(self): - self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)") - - def test_TextWithVar(self): - self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)") - - def test_PlainWithRegexAndWhitespaces(self): - self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)") - self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)") - self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)") - - def test_PlainWithVarAndWhitespaces(self): - self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)") - self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)") - self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)") - - def test_AllKinds(self): - self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)") - self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)") - self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)") - - # Test that variables and patterns are parsed correctly - - def test_ValidPattern(self): - self.__parsesPattern("{{abc}}", "abc") - self.__parsesPattern("{{a[b]c}}", "a[b]c") - self.__parsesPattern("{{(a{bc})}}", "(a{bc})") - - def test_ValidRef(self): - self.__parsesVarRef("[[ABC]]", "ABC") - self.__parsesVarRef("[[A1BC2]]", "A1BC2") - - def test_ValidDef(self): - self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc") - self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c") - self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c") - self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])") - - def test_Empty(self): - self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern) - self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef) - self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef) - - def test_InvalidVarName(self): - self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef) - self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef) - self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef) - self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef) - self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef) - self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef) - - def test_BodyMatchNotGreedy(self): - self.__parsesTo("{{abc}}{{def}}", "(abc)(def)") - self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)") - - def test_NoVarDefsInNotChecks(self): - with self.assertRaises(CheckerException): - self.__tryParseNot("[[ABC:abc]]") - -class TestCheckLine_Match(unittest.TestCase): - def __matchSingle(self, checkString, outputString, varState={}): - checkLine = checker.CheckLine(checkString) - newVarState = checkLine.match(outputString, varState) - self.assertIsNotNone(newVarState) - return newVarState - - def __notMatchSingle(self, checkString, outputString, varState={}): - checkLine = checker.CheckLine(checkString) - self.assertIsNone(checkLine.match(outputString, varState)) - - def test_TextAndWhitespace(self): - self.__matchSingle("foo", "foo") - self.__matchSingle("foo", " foo ") - self.__matchSingle("foo", "foo bar") - self.__notMatchSingle("foo", "XfooX") - self.__notMatchSingle("foo", "zoo") - - self.__matchSingle("foo bar", "foo bar") - self.__matchSingle("foo bar", "abc foo bar def") - self.__matchSingle("foo bar", "foo foo bar bar") - - self.__matchSingle("foo bar", "foo X bar") - self.__notMatchSingle("foo bar", "foo Xbar") - - def test_Pattern(self): - self.__matchSingle("foo{{A|B}}bar", "fooAbar") - self.__matchSingle("foo{{A|B}}bar", "fooBbar") - self.__notMatchSingle("foo{{A|B}}bar", "fooCbar") - - def test_VariableReference(self): - self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""}) - self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"}) - self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"}) - self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"}) - self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"}) - with self.assertRaises(CheckerException): - self.__matchSingle("foo[[X]]bar", "foobar", {}) - - def test_VariableDefinition(self): - self.__matchSingle("foo[[X:A|B]]bar", "fooAbar") - self.__matchSingle("foo[[X:A|B]]bar", "fooBbar") - self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar") - - env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {}) - self.assertEqual(env, {"X": "AB"}) - env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {}) - self.assertEqual(env, {"X": "AxxB"}) - - self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz") - self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz") - self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz") - - def test_NoVariableRedefinition(self): - with self.assertRaises(CheckerException): - self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar") - - def test_EnvNotChangedOnPartialMatch(self): - env = {"Y": "foo"} - self.__notMatchSingle("[[X:A]]bar", "Abaz", env) - self.assertFalse("X" in env.keys()) - - def test_VariableContentEscaped(self): - self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*") - self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA") - - -CheckVariant = checker.CheckLine.Variant - -def prepareSingleCheck(line): - if isinstance(line, str): - return checker.CheckLine(line) - else: - return checker.CheckLine(line[0], line[1]) - -def prepareChecks(lines): - if isinstance(lines, str): - lines = lines.splitlines() - return list(map(lambda line: prepareSingleCheck(line), lines)) - - -class TestCheckGroup_Match(unittest.TestCase): - def __matchMulti(self, checkLines, outputString): - checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines)) - outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines()) - return checkGroup.match(outputGroup) - - def __notMatchMulti(self, checkString, outputString): - with self.assertRaises(CheckerException): - self.__matchMulti(checkString, outputString) - - def test_TextAndPattern(self): - self.__matchMulti("""foo bar - abc {{def}}""", - """foo bar - abc def"""); - self.__matchMulti("""foo bar - abc {{de.}}""", - """======= - foo bar - ======= - abc de# - ======="""); - self.__notMatchMulti("""//XYZ: foo bar - //XYZ: abc {{def}}""", - """======= - foo bar - ======= - abc de# - ======="""); - - def test_Variables(self): - self.__matchMulti("""foo[[X:.]]bar - abc[[X]]def""", - """foo bar - abc def"""); - self.__matchMulti("""foo[[X:([0-9]+)]]bar - abc[[X]]def - ### [[X]] ###""", - """foo1234bar - abc1234def - ### 1234 ###"""); - - def test_Ordering(self): - self.__matchMulti([("foo", CheckVariant.InOrder), - ("bar", CheckVariant.InOrder)], - """foo - bar""") - self.__notMatchMulti([("foo", CheckVariant.InOrder), - ("bar", CheckVariant.InOrder)], - """bar - foo""") - self.__matchMulti([("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG)], - """abc - def""") - self.__matchMulti([("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG)], - """def - abc""") - self.__matchMulti([("foo", CheckVariant.InOrder), - ("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG), - ("bar", CheckVariant.InOrder)], - """foo - def - abc - bar""") - self.__notMatchMulti([("foo", CheckVariant.InOrder), - ("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG), - ("bar", CheckVariant.InOrder)], - """foo - abc - bar""") - self.__notMatchMulti([("foo", CheckVariant.InOrder), - ("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG), - ("bar", CheckVariant.InOrder)], - """foo - def - bar""") - - def test_NotAssertions(self): - self.__matchMulti([("foo", CheckVariant.Not)], - """abc - def""") - self.__notMatchMulti([("foo", CheckVariant.Not)], - """abc foo - def""") - self.__notMatchMulti([("foo", CheckVariant.Not), - ("bar", CheckVariant.Not)], - """abc - def bar""") - - def test_LineOnlyMatchesOnce(self): - self.__matchMulti([("foo", CheckVariant.DAG), - ("foo", CheckVariant.DAG)], - """foo - foo""") - self.__notMatchMulti([("foo", CheckVariant.DAG), - ("foo", CheckVariant.DAG)], - """foo - bar""") - -class TestOutputFile_Parse(unittest.TestCase): - def __parsesTo(self, string, expected): - if isinstance(string, str): - string = unicode(string) - outputStream = io.StringIO(string) - return self.assertEqual(checker.OutputFile(outputStream).groups, expected) - - def test_NoInput(self): - self.__parsesTo(None, []) - self.__parsesTo("", []) - - def test_SingleGroup(self): - self.__parsesTo("""begin_compilation - method "MyMethod" - end_compilation - begin_cfg - name "pass1" - foo - bar - end_cfg""", - [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ]) - - def test_MultipleGroups(self): - self.__parsesTo("""begin_compilation - name "xyz1" - method "MyMethod1" - date 1234 - end_compilation - begin_cfg - name "pass1" - foo - bar - end_cfg - begin_cfg - name "pass2" - abc - def - end_cfg""", - [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]), - checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ]) - - self.__parsesTo("""begin_compilation - name "xyz1" - method "MyMethod1" - date 1234 - end_compilation - begin_cfg - name "pass1" - foo - bar - end_cfg - begin_compilation - name "xyz2" - method "MyMethod2" - date 5678 - end_compilation - begin_cfg - name "pass2" - abc - def - end_cfg""", - [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]), - checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ]) - -class TestCheckFile_Parse(unittest.TestCase): - def __parsesTo(self, string, expected): - if isinstance(string, str): - string = unicode(string) - checkStream = io.StringIO(string) - return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected) - - def test_NoInput(self): - self.__parsesTo(None, []) - self.__parsesTo("", []) - - def test_SingleGroup(self): - self.__parsesTo("""// CHECK-START: Example Group - // CHECK: foo - // CHECK: bar""", - [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ]) - - def test_MultipleGroups(self): - self.__parsesTo("""// CHECK-START: Example Group1 - // CHECK: foo - // CHECK: bar - // CHECK-START: Example Group2 - // CHECK: abc - // CHECK: def""", - [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])), - checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ]) - - def test_CheckVariants(self): - self.__parsesTo("""// CHECK-START: Example Group - // CHECK: foo - // CHECK-NOT: bar - // CHECK-DAG: abc - // CHECK-DAG: def""", - [ checker.CheckGroup("Example Group", - prepareChecks([ ("foo", CheckVariant.InOrder), - ("bar", CheckVariant.Not), - ("abc", CheckVariant.DAG), - ("def", CheckVariant.DAG) ])) ]) - -if __name__ == '__main__': - checker.Logger.Verbosity = checker.Logger.Level.NoOutput - unittest.main() diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index a3870362c2..a8bc4e129d 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -109,18 +109,6 @@ bug: 19165288 }, { - description: "Bug in libcore", - result: EXEC_FAILED, - names: ["libcore.javax.crypto.ECDHKeyAgreementTest#testInit_withUnsupportedPrivateKeyType"], - bug: 19730263 -}, -{ - description: "Needs to be run as root", - result: EXEC_FAILED, - modes: [host], - names: ["libcore.io.OsTest#test_PacketSocketAddress"] -}, -{ description: "Needs kernel updates on host/device", result: EXEC_FAILED, names: ["libcore.io.OsTest#test_socketPing"] @@ -130,5 +118,11 @@ modes: [device], result: EXEC_FAILED, names: ["org.apache.harmony.tests.java.lang.ProcessManagerTest#testEnvironment"] +}, +{ + description: "Crypto failures", + result: EXEC_FAILED, + names: ["libcore.javax.crypto.CipherTest#testCipher_ShortBlock_Failure", + "libcore.javax.crypto.CipherTest#testCipher_Success"] } ] 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 |