diff options
| author | 2016-07-19 15:00:40 -0700 | |
|---|---|---|
| committer | 2016-07-27 20:02:48 -0700 | |
| commit | 06a46c44bf1a5cba6c78c3faffc4e7ec1442b210 (patch) | |
| tree | fa82ec7a787200e475e567a51c2839349a513021 | |
| parent | 9755c262df1be7f5d5b98d038c8fd3734e974f9d (diff) | |
MIPS32: Improve string and class loads
Tested:
- MIPS32 Android boots in QEMU
- test-art-host-gtest
- test-art-target-run-test-optimizing in QEMU, on CI20
- test-art-target-gtest on CI20
Change-Id: I70fd5d5267f8594c3b29d5a4ccf66b8ca8b09df3
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips.cc | 29 | ||||
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips32r6_test.cc | 72 | ||||
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips_test.cc | 76 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips.cc | 524 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips.h | 47 | ||||
| -rw-r--r-- | compiler/optimizing/dex_cache_array_fixups_mips.cc | 44 | ||||
| -rw-r--r-- | compiler/optimizing/dex_cache_array_fixups_mips.h | 11 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 14 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/pc_relative_fixups_mips.cc | 37 | ||||
| -rw-r--r-- | compiler/utils/mips/assembler_mips.cc | 4 | ||||
| -rw-r--r-- | compiler/utils/mips/assembler_mips.h | 3 | ||||
| -rw-r--r-- | test/496-checker-inlining-and-class-loader/src/Main.java | 3 | ||||
| -rw-r--r-- | test/552-checker-sharpening/src/Main.java | 79 |
14 files changed, 837 insertions, 108 deletions
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc index 7c0423b635..c09950cd5d 100644 --- a/compiler/linker/mips/relative_patcher_mips.cc +++ b/compiler/linker/mips/relative_patcher_mips.cc @@ -49,6 +49,7 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, uint32_t target_offset) { uint32_t anchor_literal_offset = patch.PcInsnOffset(); uint32_t literal_offset = patch.LiteralOffset(); + bool dex_cache_array = (patch.GetType() == LinkerPatch::Type::kDexCacheArray); // Basic sanity checks. if (is_r6) { @@ -68,12 +69,16 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK_GE(code->size(), 16u); DCHECK_LE(literal_offset, code->size() - 12u); DCHECK_GE(literal_offset, 4u); - DCHECK_EQ(literal_offset + 4u, anchor_literal_offset); - // NAL - DCHECK_EQ((*code)[literal_offset - 4], 0x00); - DCHECK_EQ((*code)[literal_offset - 3], 0x00); - DCHECK_EQ((*code)[literal_offset - 2], 0x10); - DCHECK_EQ((*code)[literal_offset - 1], 0x04); + // The NAL instruction may not precede immediately as the PC+0 value may + // come from HMipsComputeBaseMethodAddress. + if (dex_cache_array) { + DCHECK_EQ(literal_offset + 4u, anchor_literal_offset); + // NAL + DCHECK_EQ((*code)[literal_offset - 4], 0x00); + DCHECK_EQ((*code)[literal_offset - 3], 0x00); + DCHECK_EQ((*code)[literal_offset - 2], 0x10); + DCHECK_EQ((*code)[literal_offset - 1], 0x04); + } // LUI reg, offset_high DCHECK_EQ((*code)[literal_offset + 0], 0x34); DCHECK_EQ((*code)[literal_offset + 1], 0x12); @@ -83,16 +88,22 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK_EQ((*code)[literal_offset + 4], 0x78); DCHECK_EQ((*code)[literal_offset + 5], 0x56); DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x34); - // ADDU reg, reg, RA + // ADDU reg, reg, reg2 DCHECK_EQ((*code)[literal_offset + 8], 0x21); DCHECK_EQ(((*code)[literal_offset + 9] & 0x07), 0x00); - DCHECK_EQ(((*code)[literal_offset + 10] & 0x1F), 0x1F); + if (dex_cache_array) { + // reg2 is either RA or from HMipsComputeBaseMethodAddress. + DCHECK_EQ(((*code)[literal_offset + 10] & 0x1F), 0x1F); + } DCHECK_EQ(((*code)[literal_offset + 11] & 0xFC), 0x00); } // Apply patch. uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset; - uint32_t diff = target_offset - anchor_offset + kDexCacheArrayLwOffset; + uint32_t diff = target_offset - anchor_offset; + if (dex_cache_array) { + diff += kDexCacheArrayLwOffset; + } if (is_r6) { diff += (diff & 0x8000) << 1; // Account for sign extension in ADDIU. } diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc index 0f1dcbcbf1..a16aaca545 100644 --- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -29,40 +29,78 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest { Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {} protected: + static const uint8_t UnpatchedPcRelativeRawCode[]; + static const uint32_t LiteralOffset; + static const uint32_t AnchorOffset; + static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode; + uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); CHECK(result.first); return result.second; } + + void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset); + void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset); + void TestStringReference(uint32_t string_offset); }; -TEST_F(Mips32r6RelativePatcherTest, DexCacheReference) { - dex_cache_arrays_begin_ = 0x12345678; - constexpr size_t kElementOffset = 0x1234; - static const uint8_t raw_code[] = { - 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234 - 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678 - }; - constexpr uint32_t literal_offset = 0; // At auipc (where patching starts). - constexpr uint32_t anchor_offset = literal_offset; // At auipc (where PC+0 points). - ArrayRef<const uint8_t> code(raw_code); - LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(literal_offset, nullptr, anchor_offset, kElementOffset), - }; - AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches)); +const uint8_t Mips32r6RelativePatcherTest::UnpatchedPcRelativeRawCode[] = { + 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678 +}; +const uint32_t Mips32r6RelativePatcherTest::LiteralOffset = 0; // At auipc (where patching starts). +const uint32_t Mips32r6RelativePatcherTest::AnchorOffset = 0; // At auipc (where PC+0 points). +const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::UnpatchedPcRelativeCode( + UnpatchedPcRelativeRawCode); + +void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, + uint32_t target_offset) { + AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = dex_cache_arrays_begin_ + kElementOffset - (result.second + anchor_offset) + - kDexCacheArrayLwOffset; + + uint32_t diff = target_offset - (result.second + AnchorOffset); + if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { + diff += kDexCacheArrayLwOffset; + } diff += (diff & 0x8000) << 1; // Account for sign extension in addiu. - static const uint8_t expected_code[] = { + + const uint8_t expected_code[] = { static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE, static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26, }; EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); } +void Mips32r6RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin, + uint32_t element_offset) { + dex_cache_arrays_begin_ = dex_cache_arrays_begin; + LinkerPatch patches[] = { + LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), + dex_cache_arrays_begin_ + element_offset); +} + +void Mips32r6RelativePatcherTest::TestStringReference(uint32_t string_offset) { + constexpr uint32_t kStringIndex = 1u; + string_index_to_offset_map_.Put(kStringIndex, string_offset); + LinkerPatch patches[] = { + LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); +} + +TEST_F(Mips32r6RelativePatcherTest, DexCacheReference) { + TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234); +} + +TEST_F(Mips32r6RelativePatcherTest, StringReference) { + TestStringReference(/* string_offset*/ 0x87651234); +} + } // namespace linker } // namespace art diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc index 8391b5352a..335ce2e476 100644 --- a/compiler/linker/mips/relative_patcher_mips_test.cc +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -29,36 +29,47 @@ class MipsRelativePatcherTest : public RelativePatcherTest { MipsRelativePatcherTest() : RelativePatcherTest(kMips, "mips32r2") {} protected: + static const uint8_t UnpatchedPcRelativeRawCode[]; + static const uint32_t LiteralOffset; + static const uint32_t AnchorOffset; + static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode; + uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); CHECK(result.first); return result.second; } + + void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset); + void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset); + void TestStringReference(uint32_t string_offset); }; -TEST_F(MipsRelativePatcherTest, DexCacheReference) { - dex_cache_arrays_begin_ = 0x12345678; - constexpr size_t kElementOffset = 0x1234; - static const uint8_t raw_code[] = { - 0x00, 0x00, 0x10, 0x04, // nal - 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234 - 0x78, 0x56, 0x52, 0x36, // ori s2, s2, low(diff); placeholder = 0x5678 - 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra - }; - constexpr uint32_t literal_offset = 4; // At lui (where patching starts). - constexpr uint32_t anchor_offset = 8; // At ori (where PC+0 points). - ArrayRef<const uint8_t> code(raw_code); - LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(literal_offset, nullptr, anchor_offset, kElementOffset), - }; - AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches)); +const uint8_t MipsRelativePatcherTest::UnpatchedPcRelativeRawCode[] = { + 0x00, 0x00, 0x10, 0x04, // nal + 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x52, 0x36, // ori s2, s2, low(diff); placeholder = 0x5678 + 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra +}; +const uint32_t MipsRelativePatcherTest::LiteralOffset = 4; // At lui (where patching starts). +const uint32_t MipsRelativePatcherTest::AnchorOffset = 8; // At ori (where PC+0 points). +const ArrayRef<const uint8_t> MipsRelativePatcherTest::UnpatchedPcRelativeCode( + UnpatchedPcRelativeRawCode); + +void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, + uint32_t target_offset) { + AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = dex_cache_arrays_begin_ + kElementOffset - (result.second + anchor_offset) + - kDexCacheArrayLwOffset; - static const uint8_t expected_code[] = { + + uint32_t diff = target_offset - (result.second + AnchorOffset); + if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { + diff += kDexCacheArrayLwOffset; + } + + const uint8_t expected_code[] = { 0x00, 0x00, 0x10, 0x04, static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x12, 0x3C, static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x36, @@ -67,5 +78,32 @@ TEST_F(MipsRelativePatcherTest, DexCacheReference) { EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); } +void MipsRelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin, + uint32_t element_offset) { + dex_cache_arrays_begin_ = dex_cache_arrays_begin; + LinkerPatch patches[] = { + LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), + dex_cache_arrays_begin_ + element_offset); +} + +void MipsRelativePatcherTest::TestStringReference(uint32_t string_offset) { + constexpr uint32_t kStringIndex = 1u; + string_index_to_offset_map_.Put(kStringIndex, string_offset); + LinkerPatch patches[] = { + LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); +} + +TEST_F(MipsRelativePatcherTest, DexCacheReference) { + TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234); +} + +TEST_F(MipsRelativePatcherTest, StringReference) { + TestStringReference(/* string_offset*/ 0x87651234); +} + } // namespace linker } // namespace art diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 39248aa430..adae3f708d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -482,11 +482,22 @@ CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, move_resolver_(graph->GetArena(), this), assembler_(graph->GetArena(), &isa_features), isa_features_(isa_features), + uint32_literals_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), method_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), call_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_address_patches_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + clobbered_ra_(false) { // Save RA (containing the return address) to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(RA)); } @@ -688,6 +699,16 @@ void CodeGeneratorMIPS::ComputeSpillMask() { if ((fpu_spill_mask_ != 0) && (POPCOUNT(core_spill_mask_) % 2 != 0)) { core_spill_mask_ |= (1 << ZERO); } + // If RA is clobbered by PC-relative operations on R2 and it's the only spilled register + // (this can happen in leaf methods), artificially spill the ZERO register in order to + // force explicit saving and restoring of RA. RA isn't saved/restored when it's the only + // spilled register. + // TODO: Can this be improved? It causes creation of a stack frame (while RA might be + // saved in an unused temporary register) and saving of RA and the current method pointer + // in the frame. + if (clobbered_ra_ && core_spill_mask_ == (1u << RA) && fpu_spill_mask_ == 0) { + core_spill_mask_ |= (1 << ZERO); + } } static dwarf::Reg DWARFReg(Register reg) { @@ -962,7 +983,12 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch size_t size = method_patches_.size() + call_patches_.size() + - pc_relative_dex_cache_patches_.size(); + pc_relative_dex_cache_patches_.size() + + pc_relative_string_patches_.size() + + pc_relative_type_patches_.size() + + boot_image_string_patches_.size() + + boot_image_type_patches_.size() + + boot_image_address_patches_.size(); linker_patches->reserve(size); for (const auto& entry : method_patches_) { const MethodReference& target_method = entry.first; @@ -994,6 +1020,71 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch pc_rel_offset, base_element_offset)); } + for (const PcRelativePatchInfo& info : pc_relative_string_patches_) { + const DexFile& dex_file = info.target_dex_file; + size_t string_index = info.offset_or_index; + DCHECK(info.high_label.IsBound()); + uint32_t high_offset = __ GetLabelLocation(&info.high_label); + // On R2 we use HMipsComputeBaseMethodAddress and patch relative to + // the assembler's base label used for PC-relative literals. + uint32_t pc_rel_offset = info.pc_rel_label.IsBound() + ? __ GetLabelLocation(&info.pc_rel_label) + : __ GetPcRelBaseLabelLocation(); + linker_patches->push_back(LinkerPatch::RelativeStringPatch(high_offset, + &dex_file, + pc_rel_offset, + string_index)); + } + for (const PcRelativePatchInfo& info : pc_relative_type_patches_) { + const DexFile& dex_file = info.target_dex_file; + size_t type_index = info.offset_or_index; + DCHECK(info.high_label.IsBound()); + uint32_t high_offset = __ GetLabelLocation(&info.high_label); + // On R2 we use HMipsComputeBaseMethodAddress and patch relative to + // the assembler's base label used for PC-relative literals. + uint32_t pc_rel_offset = info.pc_rel_label.IsBound() + ? __ GetLabelLocation(&info.pc_rel_label) + : __ GetPcRelBaseLabelLocation(); + linker_patches->push_back(LinkerPatch::RelativeTypePatch(high_offset, + &dex_file, + pc_rel_offset, + type_index)); + } + for (const auto& entry : boot_image_string_patches_) { + const StringReference& target_string = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, + target_string.dex_file, + target_string.string_index)); + } + for (const auto& entry : boot_image_type_patches_) { + const TypeReference& target_type = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, + target_type.dex_file, + target_type.type_index)); + } + for (const auto& entry : boot_image_address_patches_) { + DCHECK(GetCompilerOptions().GetIncludePatchInformation()); + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); + } +} + +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPatch( + const DexFile& dex_file, uint32_t string_index) { + return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); +} + +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch( + const DexFile& dex_file, uint32_t type_index) { + return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch( @@ -1007,6 +1098,12 @@ CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativePatch( return &patches->back(); } +Literal* CodeGeneratorMIPS::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) { + return map->GetOrCreate( + value, + [this, value]() { return __ NewLiteral<uint32_t>(value); }); +} + Literal* CodeGeneratorMIPS::DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map) { return map->GetOrCreate( @@ -1022,6 +1119,26 @@ Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_ return DeduplicateMethodLiteral(target_method, &call_patches_); } +Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex_file, + uint32_t string_index) { + return boot_image_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, + uint32_t type_index) { + return boot_image_type_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address) { + bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); + Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; + return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); +} + void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) { MipsLabel done; Register card = AT; @@ -3696,6 +3813,23 @@ void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* inst HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc()); } +void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad( + HInstruction* instruction ATTRIBUTE_UNUSED, + Location root, + Register obj, + uint32_t offset) { + Register root_reg = root.AsRegister<Register>(); + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } else { + // Plain GC root load with no read barrier. + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadWord, root_reg, obj, offset); + // Note that GC roots are not affected by heap poisoning, thus we + // do not have to unpoison `root_reg` here. + } +} + void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary::CallKind call_kind = instruction->IsExactCheck() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath; @@ -3861,16 +3995,80 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen } HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( - HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) { - // TODO: Implement other kinds. - return HLoadString::LoadKind::kDexCacheViaMethod; + HLoadString::LoadKind desired_string_load_kind) { + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } + // We disable PC-relative load when there is an irreducible loop, as the optimization + // is incompatible with it. + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); + bool fallback_load = has_irreducible_loops; + switch (desired_string_load_kind) { + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadString::LoadKind::kBootImageAddress: + break; + case HLoadString::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + fallback_load = false; + break; + case HLoadString::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + // TODO: Create as many MipsDexCacheArraysBase instructions as needed for methods + // with irreducible loops. + break; + case HLoadString::LoadKind::kDexCacheViaMethod: + fallback_load = false; + break; + } + if (fallback_load) { + desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; + } + return desired_string_load_kind; } HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { - DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass); - // TODO: Implement other kinds. - return HLoadClass::LoadKind::kDexCacheViaMethod; + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } + // We disable pc-relative load when there is an irreducible loop, as the optimization + // is incompatible with it. + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); + bool fallback_load = has_irreducible_loops; + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + fallback_load = false; + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + fallback_load = false; + break; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + // TODO: Create as many MipsDexCacheArraysBase instructions as needed for methods + // with irreducible loops. + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + fallback_load = false; + break; + } + if (fallback_load) { + desired_class_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } + return desired_class_load_kind; } Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, @@ -4107,11 +4305,40 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(V0)); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(V0), + /* code_generator_supports_read_barrier */ false); // TODO: revisit this bool. + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + switch (load_kind) { + // We need an extra register for PC-relative literals on R2. + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + if (codegen_->GetInstructionSetFeatures().IsR6()) { + break; + } + FALLTHROUGH_INTENDED; + // We need an extra register for PC-relative dex cache accesses. + case HLoadClass::LoadKind::kDexCachePcRelative: + case HLoadClass::LoadKind::kReferrersClass: + case HLoadClass::LoadKind::kDexCacheViaMethod: + locations->SetInAt(0, Location::RequiresRegister()); + break; + default: + break; + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { @@ -4127,34 +4354,126 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { return; } - Register out = locations->Out().AsRegister<Register>(); - Register current_method = locations->InAt(0).AsRegister<Register>(); - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - __ LoadFromOffset(kLoadWord, out, current_method, - ArtMethod::DeclaringClassOffset().Int32Value()); - } else { - __ LoadFromOffset(kLoadWord, out, current_method, - ArtMethod::DexCacheResolvedTypesOffset(kMipsPointerSize).Int32Value()); - __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); - - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS( - cls, - cls, - cls->GetDexPc(), - cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ Beqz(out, slow_path->GetEntryLabel()); - } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + Location out_loc = locations->Out(); + Register out = out_loc.AsRegister<Register>(); + Register base_or_current_method_reg; + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (load_kind) { + // We need an extra register for PC-relative literals on R2. + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + base_or_current_method_reg = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + break; + // We need an extra register for PC-relative dex cache accesses. + case HLoadClass::LoadKind::kDexCachePcRelative: + case HLoadClass::LoadKind::kReferrersClass: + case HLoadClass::LoadKind::kDexCacheViaMethod: + base_or_current_method_reg = locations->InAt(0).AsRegister<Register>(); + break; + default: + base_or_current_method_reg = ZERO; + break; + } + + bool generate_null_check = false; + switch (load_kind) { + case HLoadClass::LoadKind::kReferrersClass: { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + GenerateGcRootFieldLoad(cls, + out_loc, + base_or_current_method_reg, + ArtMethod::DeclaringClassOffset().Int32Value()); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!kEmitCompilerReadBarrier); + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), + cls->GetTypeIndex())); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + CodeGeneratorMIPS::PcRelativePatchInfo* info = + codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + if (isR6) { + __ Bind(&info->high_label); + __ Bind(&info->pc_rel_label); + // Add a 32-bit offset to PC. + __ Auipc(out, /* placeholder */ 0x1234); + __ Addiu(out, out, /* placeholder */ 0x5678); } else { - __ Bind(slow_path->GetExitLabel()); + __ Bind(&info->high_label); + __ Lui(out, /* placeholder */ 0x1234); + // We do not bind info->pc_rel_label here, we'll use the assembler's label + // for PC-relative literals and the base from HMipsComputeBaseMethodAddress. + __ Ori(out, out, /* placeholder */ 0x5678); + // Add a 32-bit offset to PC. + __ Addu(out, out, base_or_current_method_reg); } + break; + } + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageAddressLiteral(address)); + break; + } + case HLoadClass::LoadKind::kDexCacheAddress: { + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); + DCHECK_ALIGNED(cls->GetAddress(), 4u); + int16_t offset = Low16Bits(address); + uint32_t base_address = address - offset; // This accounts for offset sign extension. + __ Lui(out, High16Bits(base_address)); + // /* GcRoot<mirror::Class> */ out = *(base_address + offset) + GenerateGcRootFieldLoad(cls, out_loc, out, offset); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + HMipsDexCacheArraysBase* base = cls->InputAt(0)->AsMipsDexCacheArraysBase(); + int32_t offset = + cls->GetDexCacheElementOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset; + // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) + GenerateGcRootFieldLoad(cls, out_loc, base_or_current_method_reg, offset); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + __ LoadFromOffset(kLoadWord, + out, + base_or_current_method_reg, + ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); + // /* GcRoot<mirror::Class> */ out = out[type_index] + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + GenerateGcRootFieldLoad(cls, out_loc, out, offset); + generate_null_check = !cls->IsInDexCache(); + } + } + + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + if (generate_null_check) { + __ Beqz(out, slow_path->GetEntryLabel()); + } + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -4183,21 +4502,132 @@ void InstructionCodeGeneratorMIPS::VisitClearException(HClearException* clear AT } void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() + LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); + HLoadString::LoadKind load_kind = load->GetLoadKind(); + switch (load_kind) { + // We need an extra register for PC-relative literals on R2. + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + case HLoadString::LoadKind::kBootImageAddress: + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: + if (codegen_->GetInstructionSetFeatures().IsR6()) { + break; + } + FALLTHROUGH_INTENDED; + // We need an extra register for PC-relative dex cache accesses. + case HLoadString::LoadKind::kDexCachePcRelative: + case HLoadString::LoadKind::kDexCacheViaMethod: + locations->SetInAt(0, Location::RequiresRegister()); + break; + default: + break; + } locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { + HLoadString::LoadKind load_kind = load->GetLoadKind(); LocationSummary* locations = load->GetLocations(); - Register out = locations->Out().AsRegister<Register>(); - Register current_method = locations->InAt(0).AsRegister<Register>(); - __ LoadFromOffset(kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); - __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); - __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); + Location out_loc = locations->Out(); + Register out = out_loc.AsRegister<Register>(); + Register base_or_current_method_reg; + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (load_kind) { + // We need an extra register for PC-relative literals on R2. + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + case HLoadString::LoadKind::kBootImageAddress: + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: + base_or_current_method_reg = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + break; + // We need an extra register for PC-relative dex cache accesses. + case HLoadString::LoadKind::kDexCachePcRelative: + case HLoadString::LoadKind::kDexCacheViaMethod: + base_or_current_method_reg = locations->InAt(0).AsRegister<Register>(); + break; + default: + base_or_current_method_reg = ZERO; + break; + } + + switch (load_kind) { + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!kEmitCompilerReadBarrier); + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + return; // No dex cache slow path. + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + CodeGeneratorMIPS::PcRelativePatchInfo* info = + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); + if (isR6) { + __ Bind(&info->high_label); + __ Bind(&info->pc_rel_label); + // Add a 32-bit offset to PC. + __ Auipc(out, /* placeholder */ 0x1234); + __ Addiu(out, out, /* placeholder */ 0x5678); + } else { + __ Bind(&info->high_label); + __ Lui(out, /* placeholder */ 0x1234); + // We do not bind info->pc_rel_label here, we'll use the assembler's label + // for PC-relative literals and the base from HMipsComputeBaseMethodAddress. + __ Ori(out, out, /* placeholder */ 0x5678); + // Add a 32-bit offset to PC. + __ Addu(out, out, base_or_current_method_reg); + } + return; // No dex cache slow path. + } + case HLoadString::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(load->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageAddressLiteral(address)); + return; // No dex cache slow path. + } + case HLoadString::LoadKind::kDexCacheAddress: { + DCHECK_NE(load->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); + static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes."); + DCHECK_ALIGNED(load->GetAddress(), 4u); + int16_t offset = Low16Bits(address); + uint32_t base_address = address - offset; // This accounts for offset sign extension. + __ Lui(out, High16Bits(base_address)); + // /* GcRoot<mirror::String> */ out = *(base_address + offset) + GenerateGcRootFieldLoad(load, out_loc, out, offset); + break; + } + case HLoadString::LoadKind::kDexCachePcRelative: { + HMipsDexCacheArraysBase* base = load->InputAt(0)->AsMipsDexCacheArraysBase(); + int32_t offset = + load->GetDexCacheElementOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset; + // /* GcRoot<mirror::String> */ out = *(dex_cache_arrays_base + offset) + GenerateGcRootFieldLoad(load, out_loc, base_or_current_method_reg, offset); + break; + } + case HLoadString::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + GenerateGcRootFieldLoad(load, + out_loc, + base_or_current_method_reg, + ArtMethod::DeclaringClassOffset().Int32Value()); + // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ + __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); + // /* GcRoot<mirror::String> */ out = out[string_index] + GenerateGcRootFieldLoad(load, + out_loc, + out, + CodeGenerator::GetCacheOffset(load->GetStringIndex())); + break; + } + default: + LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind(); + UNREACHABLE(); + } if (!load->IsInDexCache()) { SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load); @@ -5327,6 +5757,7 @@ void InstructionCodeGeneratorMIPS::VisitMipsComputeBaseMethodAddress( __ Nal(); // Grab the return address off RA. __ Move(reg, RA); + // TODO: Can we share this code with that of VisitMipsDexCacheArraysBase()? // Remember this offset (the obtained PC value) for later use with constant area. __ BindPcRelBaseLabel(); @@ -5357,6 +5788,7 @@ void InstructionCodeGeneratorMIPS::VisitMipsDexCacheArraysBase(HMipsDexCacheArra __ Ori(reg, reg, /* placeholder */ 0x5678); // Add a 32-bit offset to PC. __ Addu(reg, reg, RA); + // TODO: Can we share this code with that of VisitMipsComputeBaseMethodAddress()? } } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 08f74c04d1..64431ee8a7 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -22,7 +22,9 @@ #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" +#include "string_reference.h" #include "utils/mips/assembler_mips.h" +#include "utils/type_reference.h" namespace art { namespace mips { @@ -226,6 +228,15 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { void HandleShift(HBinaryOperation* operation); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers (if any). + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + Register obj, + uint32_t offset); void GenerateIntCompare(IfCondition cond, LocationSummary* locations); void GenerateIntCompareAndBranch(IfCondition cond, LocationSummary* locations, @@ -298,6 +309,9 @@ class CodeGeneratorMIPS : public CodeGenerator { size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id); size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id); size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id); + void ClobberRA() { + clobbered_ra_ = true; + } void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; @@ -383,7 +397,7 @@ class CodeGeneratorMIPS : public CodeGenerator { PcRelativePatchInfo(PcRelativePatchInfo&& other) = default; const DexFile& target_dex_file; - // Either the dex cache array element offset or the string index. + // Either the dex cache array element offset or the string/type index. uint32_t offset_or_index; // Label for the instruction loading the most significant half of the offset that's added to PC // to form the base address (the least significant half is loaded with the instruction that @@ -393,14 +407,27 @@ class CodeGeneratorMIPS : public CodeGenerator { MipsLabel pc_rel_label; }; + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); + Literal* DeduplicateBootImageAddressLiteral(uint32_t address); private: Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); + using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>; - + using BootStringToLiteralMap = ArenaSafeMap<StringReference, + Literal*, + StringReferenceValueComparator>; + using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, + Literal*, + TypeReferenceValueComparator>; + + Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); Literal* DeduplicateMethodAddressLiteral(MethodReference target_method); Literal* DeduplicateMethodCodeLiteral(MethodReference target_method); @@ -417,11 +444,27 @@ class CodeGeneratorMIPS : public CodeGenerator { MipsAssembler assembler_; const MipsInstructionSetFeatures& isa_features_; + // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. + Uint32ToLiteralMap uint32_literals_; // Method patch info, map MethodReference to a literal for method address and method code. MethodToLiteralMap method_patches_; MethodToLiteralMap call_patches_; // PC-relative patch info for each HMipsDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; + // Deduplication map for boot string literals for kBootImageLinkTimeAddress. + BootStringToLiteralMap boot_image_string_patches_; + // PC-relative String patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; + // Deduplication map for boot type literals for kBootImageLinkTimeAddress. + BootTypeToLiteralMap boot_image_type_patches_; + // PC-relative type patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // Deduplication map for patchable boot image addresses. + Uint32ToLiteralMap boot_image_address_patches_; + + // PC-relative loads on R2 clobber RA, which may need to be preserved explicitly in leaf methods. + // This is a flag set by pc_relative_fixups_mips and dex_cache_array_fixups_mips optimizations. + bool clobbered_ra_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS); }; diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc index 0f42d9ce0f..19bab08eb4 100644 --- a/compiler/optimizing/dex_cache_array_fixups_mips.cc +++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "code_generator_mips.h" #include "dex_cache_array_fixups_mips.h" #include "base/arena_containers.h" @@ -27,8 +28,9 @@ namespace mips { */ class DexCacheArrayFixupsVisitor : public HGraphVisitor { public: - explicit DexCacheArrayFixupsVisitor(HGraph* graph) + explicit DexCacheArrayFixupsVisitor(HGraph* graph, CodeGenerator* codegen) : HGraphVisitor(graph), + codegen_(down_cast<CodeGeneratorMIPS*>(codegen)), dex_cache_array_bases_(std::less<const DexFile*>(), // Attribute memory use to code generator. graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {} @@ -41,9 +43,45 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { HMipsDexCacheArraysBase* base = entry.second; base->MoveBeforeFirstUserAndOutOfLoops(); } + // Computing the dex cache base for PC-relative accesses will clobber RA with + // the NAL instruction on R2. Take a note of this before generating the method + // entry. + if (!dex_cache_array_bases_.empty() && !codegen_->GetInstructionSetFeatures().IsR6()) { + codegen_->ClobberRA(); + } } private: + void VisitLoadClass(HLoadClass* load_class) OVERRIDE { + // If this is a load with PC-relative access to the dex cache types array, + // we need to add the dex cache arrays base as the special input. + if (load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCachePcRelative) { + // Initialize base for target dex file if needed. + const DexFile& dex_file = load_class->GetDexFile(); + HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file); + // Update the element offset in base. + DexCacheArraysLayout layout(kMipsPointerSize, &dex_file); + base->UpdateElementOffset(layout.TypeOffset(load_class->GetTypeIndex())); + // Add the special argument base to the load. + load_class->AddSpecialInput(base); + } + } + + void VisitLoadString(HLoadString* load_string) OVERRIDE { + // If this is a load with PC-relative access to the dex cache strings array, + // we need to add the dex cache arrays base as the special input. + if (load_string->GetLoadKind() == HLoadString::LoadKind::kDexCachePcRelative) { + // Initialize base for target dex file if needed. + const DexFile& dex_file = load_string->GetDexFile(); + HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file); + // Update the element offset in base. + DexCacheArraysLayout layout(kMipsPointerSize, &dex_file); + base->UpdateElementOffset(layout.StringOffset(load_string->GetStringIndex())); + // Add the special argument base to the load. + load_string->AddSpecialInput(base); + } + } + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { // If this is an invoke with PC-relative access to the dex cache methods array, // we need to add the dex cache arrays base as the special input. @@ -74,6 +112,8 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { }); } + CodeGeneratorMIPS* codegen_; + using DexCacheArraysBaseMap = ArenaSafeMap<const DexFile*, HMipsDexCacheArraysBase*, std::less<const DexFile*>>; DexCacheArraysBaseMap dex_cache_array_bases_; @@ -85,7 +125,7 @@ void DexCacheArrayFixups::Run() { // that can be live-in at the irreducible loop header. return; } - DexCacheArrayFixupsVisitor visitor(graph_); + DexCacheArrayFixupsVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBasesIfNeeded(); } diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.h b/compiler/optimizing/dex_cache_array_fixups_mips.h index c8def2842e..21056e130a 100644 --- a/compiler/optimizing/dex_cache_array_fixups_mips.h +++ b/compiler/optimizing/dex_cache_array_fixups_mips.h @@ -21,14 +21,21 @@ #include "optimization.h" namespace art { + +class CodeGenerator; + namespace mips { class DexCacheArrayFixups : public HOptimization { public: - DexCacheArrayFixups(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, "dex_cache_array_fixups_mips", stats) {} + DexCacheArrayFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) + : HOptimization(graph, "dex_cache_array_fixups_mips", stats), + codegen_(codegen) {} void Run() OVERRIDE; + + private: + CodeGenerator* codegen_; }; } // namespace mips diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 23ac457568..fa22adeeb3 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -5626,9 +5626,12 @@ inline uint32_t HLoadClass::GetDexCacheElementOffset() const { // Note: defined outside class to see operator<<(., HLoadClass::LoadKind). inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { - // The special input is used for PC-relative loads on some architectures. + // The special input is used for PC-relative loads on some architectures, + // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || - GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind(); + GetLoadKind() == LoadKind::kDexCachePcRelative || + GetLoadKind() == LoadKind::kBootImageLinkTimeAddress || + GetLoadKind() == LoadKind::kBootImageAddress) << GetLoadKind(); DCHECK(special_input_.GetInstruction() == nullptr); special_input_ = HUserRecord<HInstruction*>(special_input); special_input->AddUseAt(this, 0); @@ -5836,9 +5839,12 @@ inline uint32_t HLoadString::GetDexCacheElementOffset() const { // Note: defined outside class to see operator<<(., HLoadString::LoadKind). inline void HLoadString::AddSpecialInput(HInstruction* special_input) { - // The special input is used for PC-relative loads on some architectures. + // The special input is used for PC-relative loads on some architectures, + // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || - GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind(); + GetLoadKind() == LoadKind::kDexCachePcRelative || + GetLoadKind() == LoadKind::kBootImageLinkTimeAddress || + GetLoadKind() == LoadKind::kBootImageAddress) << GetLoadKind(); // HLoadString::GetInputRecords() returns an empty array at this point, // so use the GetInputRecords() from the base class to set the input record. DCHECK(special_input_.GetInstruction() == nullptr); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index aedfcb42aa..d5b0d77fe5 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -504,7 +504,7 @@ void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, mips::PcRelativeFixups* pc_relative_fixups = new (arena) mips::PcRelativeFixups(graph, codegen, stats); mips::DexCacheArrayFixups* dex_cache_array_fixups = - new (arena) mips::DexCacheArrayFixups(graph, stats); + new (arena) mips::DexCacheArrayFixups(graph, codegen, stats); HOptimization* mips_optimizations[] = { pc_relative_fixups, dex_cache_array_fixups diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index ba405cdb69..c6acc45581 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -37,6 +37,10 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { // entry block) and relieve some pressure on the register allocator // while avoiding recalculation of the base in a loop. base_->MoveBeforeFirstUserAndOutOfLoops(); + // Computing the base for PC-relative literals will clobber RA with + // the NAL instruction on R2. Take a note of this before generating + // the method entry. + codegen_->ClobberRA(); } } @@ -58,6 +62,36 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { DCHECK(base_ != nullptr); } + void VisitLoadClass(HLoadClass* load_class) OVERRIDE { + HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); + switch (load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + // Add a base register for PC-relative literals on R2. + InitializePCRelativeBasePointer(); + load_class->AddSpecialInput(base_); + break; + default: + break; + } + } + + void VisitLoadString(HLoadString* load_string) OVERRIDE { + HLoadString::LoadKind load_kind = load_string->GetLoadKind(); + switch (load_kind) { + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + case HLoadString::LoadKind::kBootImageAddress: + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: + // Add a base register for PC-relative literals on R2. + InitializePCRelativeBasePointer(); + load_string->AddSpecialInput(base_); + break; + default: + break; + } + } + void HandleInvoke(HInvoke* invoke) { // If this is an invoke-static/-direct with PC-relative dex cache array // addressing, we need the PC-relative address base. @@ -77,7 +111,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { // method pointer from the invoke. if (invoke_static_or_direct->HasCurrentMethodInput()) { DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache()); - CHECK(!has_extra_input); // TODO: review this. + CHECK(!has_extra_input); return; } @@ -116,7 +150,6 @@ void PcRelativeFixups::Run() { CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen_); if (mips_codegen->GetInstructionSetFeatures().IsR6()) { // Do nothing for R6 because it has PC-relative addressing. - // TODO: review. Move this check into RunArchOptimizations()? return; } if (graph_->HasIrreducibleLoops()) { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index ebaf1c0cab..608b3bc23c 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -2024,6 +2024,10 @@ void MipsAssembler::BindPcRelBaseLabel() { Bind(&pc_rel_base_label_); } +uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const { + return GetLabelLocation(&pc_rel_base_label_); +} + void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) { uint32_t length = branches_.back().GetLength(); if (!label->IsBound()) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 1f7781fef9..8367e68ebc 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -646,6 +646,9 @@ class MipsAssembler FINAL : public Assembler { // The assembler then computes literal offsets relative to this label. void BindPcRelBaseLabel(); + // Returns the location of the label bound with BindPcRelBaseLabel(). + uint32_t GetPcRelBaseLabelLocation() const; + // Note that PC-relative literal loads are handled as pseudo branches because they need very // similar relocation and may similarly expand in size to accomodate for larger offsets relative // to PC. diff --git a/test/496-checker-inlining-and-class-loader/src/Main.java b/test/496-checker-inlining-and-class-loader/src/Main.java index 8de6318664..78e8a40399 100644 --- a/test/496-checker-inlining-and-class-loader/src/Main.java +++ b/test/496-checker-inlining-and-class-loader/src/Main.java @@ -107,7 +107,8 @@ class LoadedByMyClassLoader { /* Load and initialize FirstSeenByMyClassLoader */ /// CHECK: LoadClass gen_clinit_check:true /* Load and initialize System */ - /// CHECK-NEXT: LoadClass gen_clinit_check:true + // There may be MipsComputeBaseMethodAddress here. + /// CHECK: LoadClass gen_clinit_check:true /// CHECK-NEXT: StaticFieldGet // There may be HArmDexCacheArraysBase or HX86ComputeBaseMethodAddress here. /// CHECK: LoadString diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 09a77ed285..2232ff43d2 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -51,6 +51,10 @@ public class Main { /// CHECK-START-ARM64: int Main.testSimple(int) sharpening (after) /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-MIPS: int Main.testSimple(int) sharpening (after) + /// CHECK-NOT: MipsDexCacheArraysBase + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-X86: int Main.testSimple(int) sharpening (after) /// CHECK-NOT: X86ComputeBaseMethodAddress /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative @@ -62,6 +66,10 @@ public class Main { /// CHECK: ArmDexCacheArraysBase /// CHECK-NOT: ArmDexCacheArraysBase + /// CHECK-START-MIPS: int Main.testSimple(int) dex_cache_array_fixups_mips (after) + /// CHECK: MipsDexCacheArraysBase + /// CHECK-NOT: MipsDexCacheArraysBase + /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress /// CHECK-NOT: X86ComputeBaseMethodAddress @@ -83,6 +91,11 @@ public class Main { /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK-NOT: MipsDexCacheArraysBase + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after) /// CHECK-NOT: X86ComputeBaseMethodAddress /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative @@ -100,6 +113,14 @@ public class Main { /// CHECK: ArmDexCacheArraysBase /// CHECK-NEXT: If + /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) dex_cache_array_fixups_mips (after) + /// CHECK: MipsDexCacheArraysBase + /// CHECK-NOT: MipsDexCacheArraysBase + + /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) dex_cache_array_fixups_mips (after) + /// CHECK: MipsDexCacheArraysBase + /// CHECK-NEXT: If + /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress /// CHECK-NOT: X86ComputeBaseMethodAddress @@ -110,7 +131,7 @@ public class Main { public static int testDiamond(boolean negate, int x) { // These calls should use PC-relative dex cache array loads to retrieve the target method. - // PC-relative bases used by X86 and ARM should be pulled before the If. + // PC-relative bases used by ARM, MIPS and X86 should be pulled before the If. if (negate) { return $noinline$foo(-x); } else { @@ -154,8 +175,26 @@ public class Main { /// CHECK: begin_block /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (before) + /// CHECK-NOT: MipsDexCacheArraysBase + + /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) + /// CHECK: MipsDexCacheArraysBase + /// CHECK-NOT: MipsDexCacheArraysBase + + /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) + /// CHECK: InvokeStaticOrDirect + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) + /// CHECK: ArrayLength + /// CHECK-NEXT: MipsDexCacheArraysBase + /// CHECK-NEXT: Goto + /// CHECK: begin_block + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + public static int testLoop(int[] array, int x) { - // PC-relative bases used by X86 and ARM should be pulled before the loop. + // PC-relative bases used by ARM, MIPS and X86 should be pulled before the loop. for (int i : array) { x += $noinline$foo(i); } @@ -182,8 +221,18 @@ public class Main { /// CHECK-NEXT: ArmDexCacheArraysBase /// CHECK-NEXT: Goto + /// CHECK-START-MIPS: int Main.testLoopWithDiamond(int[], boolean, int) dex_cache_array_fixups_mips (before) + /// CHECK-NOT: MipsDexCacheArraysBase + + /// CHECK-START-MIPS: int Main.testLoopWithDiamond(int[], boolean, int) dex_cache_array_fixups_mips (after) + /// CHECK: If + /// CHECK: begin_block + /// CHECK: ArrayLength + /// CHECK-NEXT: MipsDexCacheArraysBase + /// CHECK-NEXT: Goto + public static int testLoopWithDiamond(int[] array, boolean negate, int x) { - // PC-relative bases used by X86 and ARM should be pulled before the loop + // PC-relative bases used by ARM, MIPS and X86 should be pulled before the loop // but not outside the if. if (array != null) { for (int i : array) { @@ -220,6 +269,11 @@ public class Main { // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. /// CHECK: LoadString load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} + /// CHECK-START-MIPS: java.lang.String Main.$noinline$getBootImageString() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadString load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} + public static String $noinline$getBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -250,6 +304,13 @@ public class Main { /// CHECK-START-ARM64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) /// CHECK: LoadString load_kind:DexCachePcRelative + /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) + /// CHECK: LoadString load_kind:DexCachePcRelative + + /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() dex_cache_array_fixups_mips (after) + /// CHECK-DAG: MipsDexCacheArraysBase + /// CHECK-DAG: LoadString load_kind:DexCachePcRelative + public static String $noinline$getNonBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -280,6 +341,11 @@ public class Main { // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + public static Class<?> $noinline$getStringClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -310,6 +376,13 @@ public class Main { /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_mips (after) + /// CHECK-DAG: MipsDexCacheArraysBase + /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + public static Class<?> $noinline$getOtherClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } |