diff options
author | 2016-11-30 19:19:55 -0800 | |
---|---|---|
committer | 2016-12-13 14:07:16 -0800 | |
commit | 19f6c696bbb7a17d8ac521b316c40f9cbef32151 (patch) | |
tree | 6ce87f3ba9f224efc0036d3ab99e4272c48eeddb | |
parent | aea9ffece7eb32f3884a4ad0553e1df4d90fd9e4 (diff) |
MIPS64: Improve method invocation.
Improvements include:
- support for all kinds of method loads and static/direct calls
- 32-bit and 64-bit literals for the above and future work
- shorter instruction sequences for recursive static/direct calls
Also:
- include the MIPS64 dinsu instruction (missed earlier) and minor
clean-up in the disassembler
- properly prefix constant names with 'k' in relative patcher tests
Test: test-art-host-gtest
Test: booted MIPS64 (with 2nd arch MIPS32R6) in QEMU
Test: "make -j1 ART_TEST_DEFAULT_COMPILER=false ART_TEST_OPTIMIZING=true
ART_TEST_INTERPRETER=false ART_TEST_JIT=false
ART_TEST_PIC_TEST=true test-art-target-run-test64"
Change-Id: I19876fa5316b68531af7dfddfce90d2068433116
-rw-r--r-- | compiler/Android.bp | 6 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 2 | ||||
-rw-r--r-- | compiler/linker/mips/relative_patcher_mips32r6_test.cc | 27 | ||||
-rw-r--r-- | compiler/linker/mips/relative_patcher_mips_test.cc | 26 | ||||
-rw-r--r-- | compiler/linker/mips64/relative_patcher_mips64.cc | 111 | ||||
-rw-r--r-- | compiler/linker/mips64/relative_patcher_mips64.h | 50 | ||||
-rw-r--r-- | compiler/linker/mips64/relative_patcher_mips64_test.cc | 127 | ||||
-rw-r--r-- | compiler/linker/relative_patcher.cc | 7 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 208 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips64.h | 50 | ||||
-rw-r--r-- | compiler/utils/mips64/assembler_mips64.cc | 301 | ||||
-rw-r--r-- | compiler/utils/mips64/assembler_mips64.h | 126 | ||||
-rw-r--r-- | compiler/utils/mips64/assembler_mips64_test.cc | 327 | ||||
-rw-r--r-- | disassembler/disassembler_mips.cc | 37 |
14 files changed, 1221 insertions, 184 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp index db55ea0ef7..2eb6fba674 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -145,6 +145,7 @@ art_cc_defaults { mips64: { srcs: [ "jni/quick/mips64/calling_convention_mips64.cc", + "linker/mips64/relative_patcher_mips64.cc", "optimizing/code_generator_mips64.cc", "optimizing/intrinsics_mips64.cc", "utils/mips64/assembler_mips64.cc", @@ -381,6 +382,11 @@ art_cc_test { "linker/mips/relative_patcher_mips32r6_test.cc", ], }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64_test.cc", + ], + }, x86: { srcs: [ "linker/x86/relative_patcher_x86_test.cc", diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a2bab80b85..ccf396abe1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -299,7 +299,7 @@ CompilerDriver::CompilerDriver( dump_passes_(dump_passes), timings_logger_(timer), compiler_context_(nullptr), - support_boot_image_fixup_(instruction_set != kMips64), + support_boot_image_fixup_(true), dex_files_for_oat_file_(nullptr), compiled_method_storage_(swap_fd), profile_compilation_info_(profile_compilation_info), diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc index a16aaca545..4f9a3a050a 100644 --- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -29,10 +29,10 @@ 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; + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); @@ -45,24 +45,25 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); }; -const uint8_t Mips32r6RelativePatcherTest::UnpatchedPcRelativeRawCode[] = { +const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 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); +const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0; // At auipc (where + // patching starts). +const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points). +const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset) { - AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = target_offset - (result.second + AnchorOffset); + uint32_t diff = target_offset - (result.second + kAnchorOffset); if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { diff += kDexCacheArrayLwOffset; } @@ -79,7 +80,7 @@ void Mips32r6RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_array uint32_t element_offset) { dex_cache_arrays_begin_ = dex_cache_arrays_begin; LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), dex_cache_arrays_begin_ + element_offset); @@ -89,7 +90,7 @@ 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) + LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); } diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc index 335ce2e476..faeb92a315 100644 --- a/compiler/linker/mips/relative_patcher_mips_test.cc +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -29,10 +29,10 @@ 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; + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); @@ -45,26 +45,26 @@ class MipsRelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); }; -const uint8_t MipsRelativePatcherTest::UnpatchedPcRelativeRawCode[] = { +const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 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); +const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4; // At lui (where patching starts). +const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At ori (where PC+0 points). +const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset) { - AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = target_offset - (result.second + AnchorOffset); + uint32_t diff = target_offset - (result.second + kAnchorOffset); if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { diff += kDexCacheArrayLwOffset; } @@ -82,7 +82,7 @@ void MipsRelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_be uint32_t element_offset) { dex_cache_arrays_begin_ = dex_cache_arrays_begin; LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), dex_cache_arrays_begin_ + element_offset); @@ -92,7 +92,7 @@ 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) + LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); } diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc new file mode 100644 index 0000000000..c47971635b --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64.cc @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "linker/mips64/relative_patcher_mips64.h" + +#include "compiled_method.h" + +namespace art { +namespace linker { + +uint32_t Mips64RelativePatcher::ReserveSpace( + uint32_t offset, + const CompiledMethod* compiled_method ATTRIBUTE_UNUSED, + MethodReference method_ref ATTRIBUTE_UNUSED) { + return offset; // No space reserved; no limit on relative call distance. +} + +uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) { + return offset; // No space reserved; no limit on relative call distance. +} + +uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) { + return offset; // No thunks added; no limit on relative call distance. +} + +void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { + // Basic sanity checks. + DCHECK_GE(code->size(), 8u); + DCHECK_LE(literal_offset, code->size() - 8u); + // auipc reg, offset_high + DCHECK_EQ((*code)[literal_offset + 0], 0x34); + DCHECK_EQ((*code)[literal_offset + 1], 0x12); + DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E); + DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC); + // jialc reg, offset_low + DCHECK_EQ((*code)[literal_offset + 4], 0x78); + DCHECK_EQ((*code)[literal_offset + 5], 0x56); + DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00); + DCHECK_EQ((*code)[literal_offset + 7], 0xF8); + + // Apply patch. + uint32_t diff = target_offset - patch_offset; + // Note that a combination of auipc with an instruction that adds a sign-extended + // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of + // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end + // by 32KB. + diff += (diff & 0x8000) << 1; // Account for sign extension in jialc. + + // auipc reg, offset_high + (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); + (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); + // jialc reg, offset_low + (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0); + (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8); +} + +void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + uint32_t anchor_literal_offset = patch.PcInsnOffset(); + uint32_t literal_offset = patch.LiteralOffset(); + + // Basic sanity checks. + DCHECK_GE(code->size(), 8u); + DCHECK_LE(literal_offset, code->size() - 8u); + DCHECK_EQ(literal_offset, anchor_literal_offset); + // auipc reg, offset_high + DCHECK_EQ((*code)[literal_offset + 0], 0x34); + DCHECK_EQ((*code)[literal_offset + 1], 0x12); + DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E); + DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC); + // instr reg(s), offset_low + DCHECK_EQ((*code)[literal_offset + 4], 0x78); + DCHECK_EQ((*code)[literal_offset + 5], 0x56); + + // Apply patch. + uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset; + uint32_t diff = target_offset - anchor_offset; + // Note that a combination of auipc with an instruction that adds a sign-extended + // 16-bit immediate operand (e.g. ld) provides a PC-relative range of + // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end + // by 32KB. + diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc. + + // auipc reg, offset_high + (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); + (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); + // instr reg(s), offset_low + (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0); + (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h new file mode 100644 index 0000000000..8ef8cebe2f --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ + +#include "linker/relative_patcher.h" + +namespace art { +namespace linker { + +class Mips64RelativePatcher FINAL : public RelativePatcher { + public: + explicit Mips64RelativePatcher() {} + + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref) OVERRIDE; + uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; + uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchPcRelativeReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc new file mode 100644 index 0000000000..9e37f6bbea --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "linker/relative_patcher_test.h" +#include "linker/mips64/relative_patcher_mips64.h" + +namespace art { +namespace linker { + +class Mips64RelativePatcherTest : public RelativePatcherTest { + public: + Mips64RelativePatcherTest() : RelativePatcherTest(kMips64, "default") {} + + protected: + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint8_t kUnpatchedPcRelativeCallRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCallCode; + + 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); +}; + +const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { + 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678 +}; +const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = { + 0x34, 0x12, 0x3E, 0xEC, // auipc at, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x01, 0xF8, // jialc at, low(diff); placeholder = 0x5678 +}; +const uint32_t Mips64RelativePatcherTest::kLiteralOffset = 0; // At auipc (where patching starts). +const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points). +const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); +const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCallCode( + kUnpatchedPcRelativeCallRawCode); + +void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, + uint32_t target_offset) { + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + Link(); + + auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); + ASSERT_TRUE(result.first); + + uint32_t diff = target_offset - (result.second + kAnchorOffset); + diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc. + + 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 Mips64RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin, + uint32_t element_offset) { + dex_cache_arrays_begin_ = dex_cache_arrays_begin; + LinkerPatch patches[] = { + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), + dex_cache_arrays_begin_ + element_offset); +} + +TEST_F(Mips64RelativePatcherTest, DexCacheReference) { + TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234); +} + +TEST_F(Mips64RelativePatcherTest, CallOther) { + LinkerPatch method1_patches[] = { + LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 2u), + }; + AddCompiledMethod(MethodRef(1u), + kUnpatchedPcRelativeCallCode, + ArrayRef<const LinkerPatch>(method1_patches)); + LinkerPatch method2_patches[] = { + LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 1u), + }; + AddCompiledMethod(MethodRef(2u), + kUnpatchedPcRelativeCallCode, + ArrayRef<const LinkerPatch>(method2_patches)); + Link(); + + uint32_t method1_offset = GetMethodOffset(1u); + uint32_t method2_offset = GetMethodOffset(2u); + uint32_t diff_after = method2_offset - (method1_offset + kAnchorOffset /* PC adjustment */); + diff_after += (diff_after & 0x8000) << 1; // Account for sign extension in jialc. + static const uint8_t method1_expected_code[] = { + static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24), 0x3E, 0xEC, + static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8), 0x01, 0xF8, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code))); + uint32_t diff_before = method1_offset - (method2_offset + kAnchorOffset /* PC adjustment */); + diff_before += (diff_before & 0x8000) << 1; // Account for sign extension in jialc. + static const uint8_t method2_expected_code[] = { + static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24), 0x3E, 0xEC, + static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8), 0x01, 0xF8, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code))); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 77655947fd..f1538b10cc 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -25,6 +25,9 @@ #ifdef ART_ENABLE_CODEGEN_mips #include "linker/mips/relative_patcher_mips.h" #endif +#ifdef ART_ENABLE_CODEGEN_mips64 +#include "linker/mips64/relative_patcher_mips64.h" +#endif #ifdef ART_ENABLE_CODEGEN_x86 #include "linker/x86/relative_patcher_x86.h" #endif @@ -103,6 +106,10 @@ std::unique_ptr<RelativePatcher> RelativePatcher::Create( return std::unique_ptr<RelativePatcher>( new MipsRelativePatcher(features->AsMipsInstructionSetFeatures())); #endif +#ifdef ART_ENABLE_CODEGEN_mips64 + case kMips64: + return std::unique_ptr<RelativePatcher>(new Mips64RelativePatcher()); +#endif default: return std::unique_ptr<RelativePatcher>(new RelativePatcherNone); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index b1f9b1db53..08086820d1 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -18,6 +18,7 @@ #include "art_method.h" #include "code_generator_utils.h" +#include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" @@ -399,7 +400,15 @@ CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), assembler_(graph->GetArena()), - isa_features_(isa_features) { + isa_features_(isa_features), + uint64_literals_(std::less<uint64_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)), + relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save RA (containing the return address) to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(RA)); } @@ -510,8 +519,6 @@ void CodeGeneratorMIPS64::GenerateFrameEntry() { RecordPcInfo(nullptr, 0); } - // TODO: anything related to T9/GP/GOT/PIC/.so's? - if (HasEmptyFrame()) { return; } @@ -567,8 +574,6 @@ void CodeGeneratorMIPS64::GenerateFrameEntry() { void CodeGeneratorMIPS64::GenerateFrameExit() { __ cfi().RememberState(); - // TODO: anything related to T9/GP/GOT/PIC/.so's? - if (!HasEmptyFrame()) { // Deallocate the rest of the frame. @@ -878,6 +883,103 @@ void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, } } +template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( + const ArenaDeque<PcRelativePatchInfo>& infos, + ArenaVector<LinkerPatch>* linker_patches) { + for (const PcRelativePatchInfo& info : infos) { + const DexFile& dex_file = info.target_dex_file; + size_t offset_or_index = info.offset_or_index; + DCHECK(info.pc_rel_label.IsBound()); + uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label); + linker_patches->push_back(Factory(pc_rel_offset, &dex_file, pc_rel_offset, offset_or_index)); + } +} + +void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { + DCHECK(linker_patches->empty()); + size_t size = + method_patches_.size() + + call_patches_.size() + + pc_relative_dex_cache_patches_.size() + + relative_call_patches_.size(); + linker_patches->reserve(size); + for (const auto& entry : method_patches_) { + const MethodReference& target_method = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, + target_method.dex_file, + target_method.dex_method_index)); + } + for (const auto& entry : call_patches_) { + const MethodReference& target_method = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, + target_method.dex_file, + target_method.dex_method_index)); + } + EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, + linker_patches); + for (const PcRelativePatchInfo& info : relative_call_patches_) { + const DexFile& dex_file = info.target_dex_file; + uint32_t method_index = info.offset_or_index; + DCHECK(info.pc_rel_label.IsBound()); + uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label); + linker_patches->push_back( + LinkerPatch::RelativeCodePatch(pc_rel_offset, &dex_file, method_index)); + } +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch( + const DexFile& dex_file, uint32_t element_offset) { + return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeCallPatch( + const DexFile& dex_file, uint32_t method_index) { + return NewPcRelativePatch(dex_file, method_index, &relative_call_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch( + const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) { + patches->emplace_back(dex_file, offset_or_index); + return &patches->back(); +} + +Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) { + return uint64_literals_.GetOrCreate( + value, + [this, value]() { return __ NewLiteral<uint64_t>(value); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateMethodLiteral(MethodReference target_method, + MethodToLiteralMap* map) { + return map->GetOrCreate( + target_method, + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateMethodAddressLiteral(MethodReference target_method) { + return DeduplicateMethodLiteral(target_method, &method_patches_); +} + +Literal* CodeGeneratorMIPS64::DeduplicateMethodCodeLiteral(MethodReference target_method) { + return DeduplicateMethodLiteral(target_method, &call_patches_); +} + +void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, + GpuRegister out) { + __ Bind(&info->pc_rel_label); + // Add the high half of a 32-bit offset to PC. + __ Auipc(out, /* placeholder */ 0x1234); + // The immediately following instruction will add the sign-extended low half of the 32-bit + // offset to `out` (e.g. ld, jialc, addiu). +} + void CodeGeneratorMIPS64::SetupBlockedRegisters() const { // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated. blocked_core_registers_[ZERO] = true; @@ -946,7 +1048,6 @@ void CodeGeneratorMIPS64::InvokeRuntime(QuickEntrypointEnum entrypoint, uint32_t dex_pc, SlowPathCode* slow_path) { ValidateInvokeRuntime(entrypoint, instruction, slow_path); - // TODO: anything related to T9/GP/GOT/PIC/.so's? __ LoadFromOffset(kLoadDoubleword, T9, TR, @@ -2986,39 +3087,33 @@ HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - switch (desired_dispatch_info.method_load_kind) { - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: - // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod. - return HInvokeStaticOrDirect::DispatchInfo { - HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u, - 0u - }; - default: - break; - } - switch (desired_dispatch_info.code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement these types. For the moment, we fall back to kCallArtMethod. - return HInvokeStaticOrDirect::DispatchInfo { - desired_dispatch_info.method_load_kind, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - desired_dispatch_info.method_load_data, - 0u - }; - default: - return desired_dispatch_info; - } + // On MIPS64 we support all dispatch types. + return desired_dispatch_info; } void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { // All registers are assumed to be correctly set up per the calling convention. - Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. - switch (invoke->GetMethodLoadKind()) { + HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); + HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); + + // For better instruction scheduling we load the direct code pointer before the method pointer. + switch (code_ptr_location) { + case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: + // T9 = invoke->GetDirectCodePtr(); + __ LoadLiteral(T9, kLoadDoubleword, DeduplicateUint64Literal(invoke->GetDirectCodePtr())); + break; + case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: + // T9 = code address from literal pool with link-time patch. + __ LoadLiteral(T9, + kLoadUnsignedWord, + DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); + break; + default: + break; + } + + switch (method_load_kind) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { // temp = thread->string_init_entrypoint uint32_t offset = @@ -3033,14 +3128,23 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); break; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: - __ LoadConst64(temp.AsRegister<GpuRegister>(), invoke->GetMethodAddress()); + __ LoadLiteral(temp.AsRegister<GpuRegister>(), + kLoadDoubleword, + DeduplicateUint64Literal(invoke->GetMethodAddress())); break; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: - // TODO: Implement these types. - // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); + __ LoadLiteral(temp.AsRegister<GpuRegister>(), + kLoadUnsignedWord, + DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); + break; + case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { + uint32_t offset = invoke->GetDexCacheArrayOffset(); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset); + EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); GpuRegister reg = temp.AsRegister<GpuRegister>(); @@ -3071,23 +3175,25 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo } } - switch (invoke->GetCodePtrLocation()) { + switch (code_ptr_location) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: - __ Jialc(&frame_entry_label_, T9); + __ Balc(&frame_entry_label_); break; case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR = invoke->GetDirectCodePtr(); - __ LoadConst64(T9, invoke->GetDirectCodePtr()); - // LR() + case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: + // T9 prepared above for better instruction scheduling. + // T9() __ Jalr(T9); __ Nop(); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement these types. - // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); + case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: { + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + NewPcRelativeCallPatch(*invoke->GetTargetMethod().dex_file, + invoke->GetTargetMethod().dex_method_index); + EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Jialc(AT, /* placeholder */ 0x5678); + break; + } case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // T9 = callee_method->entry_point_from_quick_compiled_code_; __ LoadFromOffset(kLoadDoubleword, diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 690eccb7d8..067c1f940f 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -279,6 +279,9 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; } const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; } + // Emit linker patches. + void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void MarkGCCard(GpuRegister object, GpuRegister value, bool value_can_be_null); // Register allocation. @@ -357,7 +360,44 @@ class CodeGeneratorMIPS64 : public CodeGenerator { void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE; void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; + // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays, + // boot image strings and method calls. The only difference is the interpretation of + // the offset_or_index. + struct PcRelativePatchInfo { + PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx) + : target_dex_file(dex_file), offset_or_index(off_or_idx) { } + PcRelativePatchInfo(PcRelativePatchInfo&& other) = default; + + const DexFile& target_dex_file; + // Either the dex cache array element offset or the string/type/method index. + uint32_t offset_or_index; + // Label for the auipc instruction. + Mips64Label pc_rel_label; + }; + + PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, + uint32_t element_offset); + PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file, + uint32_t method_index); + + void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out); + private: + using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, Literal*>; + using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>; + Literal* DeduplicateUint64Literal(uint64_t value); + Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); + Literal* DeduplicateMethodAddressLiteral(MethodReference target_method); + Literal* DeduplicateMethodCodeLiteral(MethodReference target_method); + + PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, + uint32_t offset_or_index, + ArenaDeque<PcRelativePatchInfo>* patches); + + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, + ArenaVector<LinkerPatch>* linker_patches); + // Labels for each block that will be compiled. Mips64Label* block_labels_; // Indexed by block id. Mips64Label frame_entry_label_; @@ -367,6 +407,16 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Assembler assembler_; const Mips64InstructionSetFeatures& isa_features_; + // Deduplication map for 64-bit literals, used for non-patchable method address or method code + // address. + Uint64ToLiteralMap uint64_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. + ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; + ArenaDeque<PcRelativePatchInfo> relative_call_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64); }; diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 1a21df939e..84280b9c98 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -35,6 +35,7 @@ void Mips64Assembler::FinalizeCode() { for (auto& exception_block : exception_blocks_) { EmitExceptionPoll(&exception_block); } + EmitLiterals(); PromoteBranches(); } @@ -450,6 +451,21 @@ void Mips64Assembler::Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16) { EmitI(0x27, rs, rt, imm16); } +void Mips64Assembler::Lwpc(GpuRegister rs, uint32_t imm19) { + CHECK(IsUint<19>(imm19)) << imm19; + EmitI21(0x3B, rs, (0x01 << 19) | imm19); +} + +void Mips64Assembler::Lwupc(GpuRegister rs, uint32_t imm19) { + CHECK(IsUint<19>(imm19)) << imm19; + EmitI21(0x3B, rs, (0x02 << 19) | imm19); +} + +void Mips64Assembler::Ldpc(GpuRegister rs, uint32_t imm18) { + CHECK(IsUint<18>(imm18)) << imm18; + EmitI21(0x3B, rs, (0x06 << 18) | imm18); +} + void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) { EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16); } @@ -548,6 +564,10 @@ void Mips64Assembler::Bc(uint32_t imm26) { EmitI26(0x32, imm26); } +void Mips64Assembler::Balc(uint32_t imm26) { + EmitI26(0x3A, imm26); +} + void Mips64Assembler::Jic(GpuRegister rt, uint16_t imm16) { EmitI(0x36, static_cast<GpuRegister>(0), rt, imm16); } @@ -1064,19 +1084,37 @@ void Mips64Assembler::Branch::InitShortOrLong(Mips64Assembler::Branch::OffsetBit type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type; } -void Mips64Assembler::Branch::InitializeType(bool is_call) { +void Mips64Assembler::Branch::InitializeType(Type initial_type) { OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_); - if (is_call) { - InitShortOrLong(offset_size, kCall, kLongCall); - } else if (condition_ == kUncond) { - InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); - } else { - if (condition_ == kCondEQZ || condition_ == kCondNEZ) { - // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. - type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch; - } else { - InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); - } + switch (initial_type) { + case kLabel: + case kLiteral: + case kLiteralUnsigned: + case kLiteralLong: + CHECK(!IsResolved()); + type_ = initial_type; + break; + case kCall: + InitShortOrLong(offset_size, kCall, kLongCall); + break; + case kCondBranch: + switch (condition_) { + case kUncond: + InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); + break; + case kCondEQZ: + case kCondNEZ: + // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. + type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch; + break; + default: + InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); + break; + } + break; + default: + LOG(FATAL) << "Unexpected branch type " << initial_type; + UNREACHABLE(); } old_type_ = type_; } @@ -1109,14 +1147,14 @@ bool Mips64Assembler::Branch::IsUncond(BranchCondition condition, } } -Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target) +Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call) : old_location_(location), location_(location), target_(target), lhs_reg_(ZERO), rhs_reg_(ZERO), condition_(kUncond) { - InitializeType(false); + InitializeType(is_call ? kCall : kCondBranch); } Mips64Assembler::Branch::Branch(uint32_t location, @@ -1164,19 +1202,18 @@ Mips64Assembler::Branch::Branch(uint32_t location, // Branch condition is always true, make the branch unconditional. condition_ = kUncond; } - InitializeType(false); + InitializeType(kCondBranch); } -Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg) +Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type) : old_location_(location), location_(location), - target_(target), - lhs_reg_(indirect_reg), + target_(kUnresolved), + lhs_reg_(dest_reg), rhs_reg_(ZERO), condition_(kUncond) { - CHECK_NE(indirect_reg, ZERO); - CHECK_NE(indirect_reg, AT); - InitializeType(true); + CHECK_NE(dest_reg, ZERO); + InitializeType(label_or_literal_type); } Mips64Assembler::BranchCondition Mips64Assembler::Branch::OppositeCondition( @@ -1278,11 +1315,23 @@ bool Mips64Assembler::Branch::IsLong() const { case kUncondBranch: case kCondBranch: case kCall: + // Near label. + case kLabel: + // Near literals. + case kLiteral: + case kLiteralUnsigned: + case kLiteralLong: return false; // Long branches. case kLongUncondBranch: case kLongCondBranch: case kLongCall: + // Far label. + case kFarLabel: + // Far literals. + case kFarLiteral: + case kFarLiteralUnsigned: + case kFarLiteralLong: return true; } UNREACHABLE(); @@ -1351,6 +1400,20 @@ void Mips64Assembler::Branch::PromoteToLong() { case kCall: type_ = kLongCall; break; + // Near label. + case kLabel: + type_ = kFarLabel; + break; + // Near literals. + case kLiteral: + type_ = kFarLiteral; + break; + case kLiteralUnsigned: + type_ = kFarLiteralUnsigned; + break; + case kLiteralLong: + type_ = kFarLiteralLong; + break; default: // Note: 'type_' is already long. break; @@ -1397,7 +1460,15 @@ uint32_t Mips64Assembler::Branch::GetOffset() const { uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize()); // Calculate the byte distance between instructions and also account for // different PC-relative origins. - uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t); + uint32_t offset_location = GetOffsetLocation(); + if (type_ == kLiteralLong) { + // Special case for the ldpc instruction, whose address (PC) is rounded down to + // a multiple of 8 before adding the offset. + // Note, branch promotion has already taken care of aligning `target_` to an + // address that's a multiple of 8. + offset_location = RoundDown(offset_location, sizeof(uint64_t)); + } + uint32_t offset = target_ - offset_location - branch_info_[type_].pc_org * sizeof(uint32_t); // Prepare the offset for encoding into the instruction(s). offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift; return offset; @@ -1444,7 +1515,7 @@ void Mips64Assembler::Bind(Mips64Label* label) { label->BindTo(bound_pc); } -uint32_t Mips64Assembler::GetLabelLocation(Mips64Label* label) const { +uint32_t Mips64Assembler::GetLabelLocation(const Mips64Label* label) const { CHECK(label->IsBound()); uint32_t target = label->Position(); if (label->prev_branch_id_plus_one_) { @@ -1500,7 +1571,7 @@ void Mips64Assembler::FinalizeLabeledBranch(Mips64Label* label) { void Mips64Assembler::Buncond(Mips64Label* label) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ false); FinalizeLabeledBranch(label); } @@ -1517,12 +1588,87 @@ void Mips64Assembler::Bcond(Mips64Label* label, FinalizeLabeledBranch(label); } -void Mips64Assembler::Call(Mips64Label* label, GpuRegister indirect_reg) { +void Mips64Assembler::Call(Mips64Label* label) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target, indirect_reg); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ true); FinalizeLabeledBranch(label); } +void Mips64Assembler::LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label) { + // Label address loads are treated as pseudo branches since they require very similar handling. + DCHECK(!label->IsBound()); + branches_.emplace_back(buffer_.Size(), dest_reg, Branch::kLabel); + FinalizeLabeledBranch(label); +} + +Literal* Mips64Assembler::NewLiteral(size_t size, const uint8_t* data) { + // We don't support byte and half-word literals. + if (size == 4u) { + literals_.emplace_back(size, data); + return &literals_.back(); + } else { + DCHECK_EQ(size, 8u); + long_literals_.emplace_back(size, data); + return &long_literals_.back(); + } +} + +void Mips64Assembler::LoadLiteral(GpuRegister dest_reg, + LoadOperandType load_type, + Literal* literal) { + // Literal loads are treated as pseudo branches since they require very similar handling. + Branch::Type literal_type; + switch (load_type) { + case kLoadWord: + DCHECK_EQ(literal->GetSize(), 4u); + literal_type = Branch::kLiteral; + break; + case kLoadUnsignedWord: + DCHECK_EQ(literal->GetSize(), 4u); + literal_type = Branch::kLiteralUnsigned; + break; + case kLoadDoubleword: + DCHECK_EQ(literal->GetSize(), 8u); + literal_type = Branch::kLiteralLong; + break; + default: + LOG(FATAL) << "Unexpected literal load type " << load_type; + UNREACHABLE(); + } + Mips64Label* label = literal->GetLabel(); + DCHECK(!label->IsBound()); + branches_.emplace_back(buffer_.Size(), dest_reg, literal_type); + FinalizeLabeledBranch(label); +} + +void Mips64Assembler::EmitLiterals() { + if (!literals_.empty()) { + for (Literal& literal : literals_) { + Mips64Label* label = literal.GetLabel(); + Bind(label); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + DCHECK_EQ(literal.GetSize(), 4u); + for (size_t i = 0, size = literal.GetSize(); i != size; ++i) { + buffer_.Emit<uint8_t>(literal.GetData()[i]); + } + } + } + if (!long_literals_.empty()) { + // Reserve 4 bytes for potential alignment. If after the branch promotion the 64-bit + // literals don't end up 8-byte-aligned, they will be moved down 4 bytes. + Emit(0); // NOP. + for (Literal& literal : long_literals_) { + Mips64Label* label = literal.GetLabel(); + Bind(label); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + DCHECK_EQ(literal.GetSize(), 8u); + for (size_t i = 0, size = literal.GetSize(); i != size; ++i) { + buffer_.Emit<uint8_t>(literal.GetData()[i]); + } + } + } +} + void Mips64Assembler::PromoteBranches() { // Promote short branches to long as necessary. bool changed; @@ -1561,6 +1707,35 @@ void Mips64Assembler::PromoteBranches() { end = branch.GetOldLocation(); } } + + // Align 64-bit literals by moving them down by 4 bytes if needed. + // This will reduce the PC-relative distance, which should be safe for both near and far literals. + if (!long_literals_.empty()) { + uint32_t first_literal_location = GetLabelLocation(long_literals_.front().GetLabel()); + size_t lit_size = long_literals_.size() * sizeof(uint64_t); + size_t buf_size = buffer_.Size(); + // 64-bit literals must be at the very end of the buffer. + CHECK_EQ(first_literal_location + lit_size, buf_size); + if (!IsAligned<sizeof(uint64_t)>(first_literal_location)) { + buffer_.Move(first_literal_location - sizeof(uint32_t), first_literal_location, lit_size); + // The 4 reserved bytes proved useless, reduce the buffer size. + buffer_.Resize(buf_size - sizeof(uint32_t)); + // Reduce target addresses in literal and address loads by 4 bytes in order for correct + // offsets from PC to be generated. + for (auto& branch : branches_) { + uint32_t target = branch.GetTarget(); + if (target >= first_literal_location) { + branch.Resolve(target - sizeof(uint32_t)); + } + } + // If after this we ever call GetLabelLocation() to get the location of a 64-bit literal, + // we need to adjust the location of the literal's label as well. + for (Literal& literal : long_literals_) { + // Bound label's position is negative, hence incrementing it instead of decrementing. + literal.GetLabel()->position_ += sizeof(uint32_t); + } + } + } } // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. @@ -1569,11 +1744,23 @@ const Mips64Assembler::Branch::BranchInfo Mips64Assembler::Branch::branch_info_[ { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kUncondBranch { 2, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kCondBranch // Exception: kOffset23 for beqzc/bnezc - { 2, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kCall + { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kCall + // Near label. + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLabel + // Near literals. + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteral + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteralUnsigned + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 3 }, // kLiteralLong // Long branches. { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongUncondBranch { 3, 1, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCondBranch - { 3, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall + // Far label. + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLabel + // Far literals. + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteral + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralUnsigned + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralLong }; // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. @@ -1597,8 +1784,26 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { break; case Branch::kCall: CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Balc(offset); + break; + + // Near label. + case Branch::kLabel: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); Addiupc(lhs, offset); - Jialc(lhs, 0); + break; + // Near literals. + case Branch::kLiteral: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lwpc(lhs, offset); + break; + case Branch::kLiteralUnsigned: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lwupc(lhs, offset); + break; + case Branch::kLiteralLong: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Ldpc(lhs, offset); break; // Long branches. @@ -1616,11 +1821,37 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { Jic(AT, Low16Bits(offset)); break; case Branch::kLongCall: - offset += (offset & 0x8000) << 1; // Account for sign extension in daddiu. + offset += (offset & 0x8000) << 1; // Account for sign extension in jialc. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Jialc(AT, Low16Bits(offset)); + break; + + // Far label. + case Branch::kFarLabel: + offset += (offset & 0x8000) << 1; // Account for sign extension in addiu. CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); - Auipc(lhs, High16Bits(offset)); - Daddiu(lhs, lhs, Low16Bits(offset)); - Jialc(lhs, 0); + Auipc(AT, High16Bits(offset)); + Addiu(lhs, AT, Low16Bits(offset)); + break; + // Far literals. + case Branch::kFarLiteral: + offset += (offset & 0x8000) << 1; // Account for sign extension in lw. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Lw(lhs, AT, Low16Bits(offset)); + break; + case Branch::kFarLiteralUnsigned: + offset += (offset & 0x8000) << 1; // Account for sign extension in lwu. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Lwu(lhs, AT, Low16Bits(offset)); + break; + case Branch::kFarLiteralLong: + offset += (offset & 0x8000) << 1; // Account for sign extension in ld. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Ld(lhs, AT, Low16Bits(offset)); break; } CHECK_EQ(overwrite_location_, branch->GetEndLocation()); @@ -1631,8 +1862,8 @@ void Mips64Assembler::Bc(Mips64Label* label) { Buncond(label); } -void Mips64Assembler::Jialc(Mips64Label* label, GpuRegister indirect_reg) { - Call(label, indirect_reg); +void Mips64Assembler::Balc(Mips64Label* label) { + Call(label); } void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 238cb9d765..08a55ed41f 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -17,9 +17,11 @@ #ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_ #define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_ +#include <deque> #include <utility> #include <vector> +#include "base/arena_containers.h" #include "base/enums.h" #include "base/macros.h" #include "constants_mips64.h" @@ -312,6 +314,49 @@ class Mips64Label : public Label { DISALLOW_COPY_AND_ASSIGN(Mips64Label); }; +// Assembler literal is a value embedded in code, retrieved using a PC-relative load. +class Literal { + public: + static constexpr size_t kMaxSize = 8; + + Literal(uint32_t size, const uint8_t* data) + : label_(), size_(size) { + DCHECK_LE(size, Literal::kMaxSize); + memcpy(data_, data, size); + } + + template <typename T> + T GetValue() const { + DCHECK_EQ(size_, sizeof(T)); + T value; + memcpy(&value, data_, sizeof(T)); + return value; + } + + uint32_t GetSize() const { + return size_; + } + + const uint8_t* GetData() const { + return data_; + } + + Mips64Label* GetLabel() { + return &label_; + } + + const Mips64Label* GetLabel() const { + return &label_; + } + + private: + Mips64Label label_; + const uint32_t size_; + uint8_t data_[kMaxSize]; + + DISALLOW_COPY_AND_ASSIGN(Literal); +}; + // Slowpath entered when Thread::Current()->_exception is non-null. class Mips64ExceptionSlowPath { public: @@ -341,6 +386,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer : Assembler(arena), overwriting_(false), overwrite_location_(0), + literals_(arena->Adapter(kArenaAllocAssembler)), + long_literals_(arena->Adapter(kArenaAllocAssembler)), last_position_adjustment_(0), last_old_position_(0), last_branch_id_(0) { @@ -386,18 +433,18 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt); void Bitswap(GpuRegister rd, GpuRegister rt); - void Dbitswap(GpuRegister rd, GpuRegister rt); + void Dbitswap(GpuRegister rd, GpuRegister rt); // MIPS64 void Seb(GpuRegister rd, GpuRegister rt); void Seh(GpuRegister rd, GpuRegister rt); - void Dsbh(GpuRegister rd, GpuRegister rt); - void Dshd(GpuRegister rd, GpuRegister rt); + void Dsbh(GpuRegister rd, GpuRegister rt); // MIPS64 + void Dshd(GpuRegister rd, GpuRegister rt); // MIPS64 void Dext(GpuRegister rs, GpuRegister rt, int pos, int size); // MIPS64 void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 void Wsbh(GpuRegister rd, GpuRegister rt); void Sc(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); - void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); + void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64 void Ll(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); - void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); + void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64 void Sll(GpuRegister rd, GpuRegister rt, int shamt); void Srl(GpuRegister rd, GpuRegister rt, int shamt); @@ -409,7 +456,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs); void Dsll(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 - void Drotr(GpuRegister rd, GpuRegister rt, int shamt); + void Drotr(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsra(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsll32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 @@ -427,6 +474,9 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16); void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16); void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16); // MIPS64 + void Lwpc(GpuRegister rs, uint32_t imm19); + void Lwupc(GpuRegister rs, uint32_t imm19); // MIPS64 + void Ldpc(GpuRegister rs, uint32_t imm18); // MIPS64 void Lui(GpuRegister rt, uint16_t imm16); void Dahi(GpuRegister rs, uint16_t imm16); // MIPS64 void Dati(GpuRegister rs, uint16_t imm16); // MIPS64 @@ -445,8 +495,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Selnez(GpuRegister rd, GpuRegister rs, GpuRegister rt); void Clz(GpuRegister rd, GpuRegister rs); void Clo(GpuRegister rd, GpuRegister rs); - void Dclz(GpuRegister rd, GpuRegister rs); - void Dclo(GpuRegister rd, GpuRegister rs); + void Dclz(GpuRegister rd, GpuRegister rs); // MIPS64 + void Dclo(GpuRegister rd, GpuRegister rs); // MIPS64 void Jalr(GpuRegister rd, GpuRegister rs); void Jalr(GpuRegister rs); @@ -454,6 +504,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Auipc(GpuRegister rs, uint16_t imm16); void Addiupc(GpuRegister rs, uint32_t imm19); void Bc(uint32_t imm26); + void Balc(uint32_t imm26); void Jic(GpuRegister rt, uint16_t imm16); void Jialc(GpuRegister rt, uint16_t imm16); void Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16); @@ -605,8 +656,26 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer UNREACHABLE(); } + // Create a new literal with a given value. + // NOTE: Force the template parameter to be explicitly specified. + template <typename T> + Literal* NewLiteral(typename Identity<T>::type value) { + static_assert(std::is_integral<T>::value, "T must be an integral type."); + return NewLiteral(sizeof(value), reinterpret_cast<const uint8_t*>(&value)); + } + + // Load label address using PC-relative loads. To be used with data labels in the literal / + // jump table area only and not with regular code labels. + void LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label); + + // Create a new literal with the given data. + Literal* NewLiteral(size_t size, const uint8_t* data); + + // Load literal using PC-relative loads. + void LoadLiteral(GpuRegister dest_reg, LoadOperandType load_type, Literal* literal); + void Bc(Mips64Label* label); - void Jialc(Mips64Label* label, GpuRegister indirect_reg); + void Balc(Mips64Label* label); void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label); void Bltzc(GpuRegister rt, Mips64Label* label); void Bgtzc(GpuRegister rt, Mips64Label* label); @@ -756,12 +825,15 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // Returns the (always-)current location of a label (can be used in class CodeGeneratorMIPS64, // must be used instead of Mips64Label::GetPosition()). - uint32_t GetLabelLocation(Mips64Label* label) const; + uint32_t GetLabelLocation(const Mips64Label* label) const; // Get the final position of a label after local fixup based on the old position // recorded before FinalizeCode(). uint32_t GetAdjustedPosition(uint32_t old_position); + // 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. enum BranchCondition { kCondLT, kCondGE, @@ -791,10 +863,22 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer kUncondBranch, kCondBranch, kCall, + // Near label. + kLabel, + // Near literals. + kLiteral, + kLiteralUnsigned, + kLiteralLong, // Long branches. kLongUncondBranch, kLongCondBranch, kLongCall, + // Far label. + kFarLabel, + // Far literals. + kFarLiteral, + kFarLiteralUnsigned, + kFarLiteralLong, }; // Bit sizes of offsets defined as enums to minimize chance of typos. @@ -830,16 +914,16 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer }; static const BranchInfo branch_info_[/* Type */]; - // Unconditional branch. - Branch(uint32_t location, uint32_t target); + // Unconditional branch or call. + Branch(uint32_t location, uint32_t target, bool is_call); // Conditional branch. Branch(uint32_t location, uint32_t target, BranchCondition condition, GpuRegister lhs_reg, - GpuRegister rhs_reg = ZERO); - // Call (branch and link) that stores the target address in a given register (i.e. T9). - Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg); + GpuRegister rhs_reg); + // Label address (in literal area) or literal. + Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type); // Some conditional branches with lhs = rhs are effectively NOPs, while some // others are effectively unconditional. MIPSR6 conditional branches require lhs != rhs. @@ -923,7 +1007,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer private: // Completes branch construction by determining and recording its type. - void InitializeType(bool is_call); + void InitializeType(Type initial_type); // Helper for the above. void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type); @@ -932,7 +1016,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer uint32_t target_; // Offset into assembler buffer in bytes. GpuRegister lhs_reg_; // Left-hand side register in conditional branches or - // indirect call register. + // destination register in literals. GpuRegister rhs_reg_; // Right-hand side register in conditional branches. BranchCondition condition_; // Condition for conditional branches. @@ -957,12 +1041,13 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer BranchCondition condition, GpuRegister lhs, GpuRegister rhs = ZERO); - void Call(Mips64Label* label, GpuRegister indirect_reg); + void Call(Mips64Label* label); void FinalizeLabeledBranch(Mips64Label* label); Branch* GetBranch(uint32_t branch_id); const Branch* GetBranch(uint32_t branch_id) const; + void EmitLiterals(); void PromoteBranches(); void EmitBranch(Branch* branch); void EmitBranches(); @@ -981,6 +1066,11 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // The current overwrite location. uint32_t overwrite_location_; + // Use std::deque<> for literal labels to allow insertions at the end + // without invalidating pointers and references to existing elements. + ArenaDeque<Literal> literals_; + ArenaDeque<Literal> long_literals_; // 64-bit literals separated for alignment reasons. + // Data for AdjustedPosition(), see the description there. uint32_t last_position_adjustment_; uint32_t last_old_position_; diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index ba8f25ea77..31d3e4c33b 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -576,83 +576,80 @@ TEST_F(AssemblerMIPS64Test, Jalr) { RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr"); } -TEST_F(AssemblerMIPS64Test, Jialc) { +TEST_F(AssemblerMIPS64Test, Balc) { mips64::Mips64Label label1, label2; - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } __ Bind(&label1); - __ Jialc(&label2, mips64::T9); + __ Balc(&label2); constexpr size_t kAdduCount2 = 64; for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } __ Bind(&label2); - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); std::string expected = ".set noreorder\n" - "lapc $t9, 1f\n" - "jialc $t9, 0\n" + + "balc 1f\n" + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" - "lapc $t9, 2f\n" - "jialc $t9, 0\n" + + "balc 2f\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + "2:\n" - "lapc $t9, 1b\n" - "jialc $t9, 0\n"; - DriverStr(expected, "Jialc"); + "balc 1b\n"; + DriverStr(expected, "Balc"); } -TEST_F(AssemblerMIPS64Test, LongJialc) { +TEST_F(AssemblerMIPS64Test, LongBalc) { mips64::Mips64Label label1, label2; - __ Jialc(&label1, mips64::T9); - constexpr uint32_t kAdduCount1 = (1u << 18) + 1; - for (uint32_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + __ Balc(&label1); + constexpr uint32_t kNopCount1 = (1u << 25) + 1; + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); } __ Bind(&label1); - __ Jialc(&label2, mips64::T9); - constexpr uint32_t kAdduCount2 = (1u << 18) + 1; - for (uint32_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + __ Balc(&label2); + constexpr uint32_t kNopCount2 = (1u << 25) + 1; + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); } __ Bind(&label2); - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); - uint32_t offset_forward1 = 3 + kAdduCount1; // 3: account for auipc, daddiu and jic. + uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jialc. offset_forward1 <<= 2; - offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in daddiu. + offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jialc. - uint32_t offset_forward2 = 3 + kAdduCount2; // 3: account for auipc, daddiu and jic. + uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jialc. offset_forward2 <<= 2; - offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in daddiu. + offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jialc. - uint32_t offset_back = -(3 + kAdduCount2); // 3: account for auipc, daddiu and jic. + uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jialc. offset_back <<= 2; - offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in daddiu. + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jialc. + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves a few minutes + // of test time. std::ostringstream oss; oss << ".set noreorder\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_forward1) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" - "jialc $t9, 0\n" << - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") << + "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" "1:\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_forward2) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" - "jialc $t9, 0\n" << - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") << + "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" "2:\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_back) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_back) << "\n" - "jialc $t9, 0\n"; + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n"; std::string expected = oss.str(); - DriverStr(expected, "LongJialc"); + DriverStr(expected, "LongBalc"); } TEST_F(AssemblerMIPS64Test, Bc) { @@ -827,6 +824,258 @@ TEST_F(AssemblerMIPS64Test, LongBeqc) { // MISC // ////////// +TEST_F(AssemblerMIPS64Test, Lwpc) { + // Lwpc() takes an unsigned 19-bit immediate, while the GNU assembler needs a signed offset, + // hence the sign extension from bit 18 with `imm - ((imm & 0x40000) << 1)`. + // The GNU assembler also wants the offset to be a multiple of 4, which it will shift right + // by 2 positions when encoding, hence `<< 2` to compensate for that shift. + // We capture the value of the immediate with `.set imm, {imm}` because the value is needed + // twice for the sign extension, but `{imm}` is substituted only once. + const char* code = ".set imm, {imm}\nlw ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwpc, 19, code), "Lwpc"); +} + +TEST_F(AssemblerMIPS64Test, Lwupc) { + // The comment for the Lwpc test applies here as well. + const char* code = ".set imm, {imm}\nlwu ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwupc, 19, code), "Lwupc"); +} + +TEST_F(AssemblerMIPS64Test, Ldpc) { + // The comment for the Lwpc test applies here as well. + const char* code = ".set imm, {imm}\nld ${reg}, ((imm - ((imm & 0x20000) << 1)) << 3)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc"); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "lapc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n"; + DriverStr(expected, "LoadFarthestNearLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "addiu $v0, $at, %lo(2f - 1b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n"; + DriverStr(expected, "LoadNearestFarLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lw $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwupc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lwu $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDD; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "ldpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadFarthestNearLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "ld $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadNearestFarLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + // A nop will be inserted here before the 64-bit literals. + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80004\n" + "ldpc $a3, 3f\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "nop\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n" + "3:\n" + ".dword 0xAAAAAAAAAAAAAAAA\n"; + DriverStr(expected, "LongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80003\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n"; + DriverStr(expected, "LongLiteralAlignmentNoNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u); +} + +TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + __ LoadLabelAddress(mips64::V1, literal->GetLabel()); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + // A nop will be inserted here before the 64-bit literal. + + std::string expected = + "1:\n" + "auipc $at, %hi(3f - 1b)\n" + "ld $v0, %lo(3f - 1b)($at)\n" + "2:\n" + "auipc $at, %hi(3f - 2b)\n" + "addiu $v1, $at, %lo(3f - 2b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "nop\n" + "3:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "FarLongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4); +} + TEST_F(AssemblerMIPS64Test, Bitswap) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap"); } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 47873951fe..c82600b109 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -22,6 +22,8 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "base/bit_utils.h" + using android::base::StringPrintf; namespace art { @@ -154,6 +156,7 @@ static const MipsInstruction gMipsInstructions[] = { { kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", }, + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 6, "dinsu", "TSFz", }, { kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f, (31 << kOpcodeShift) | (16 << 6) | 32, "seb", @@ -218,8 +221,8 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", }, { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", }, { kITypeMask, 14 << kOpcodeShift, "xori", "TSi", }, - { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", }, - { kITypeMask, 15 << kOpcodeShift, "aui", "TSI", }, + { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "Ti", }, + { kITypeMask, 15 << kOpcodeShift, "aui", "TSi", }, { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21), "bc1f", "cB" }, { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21) | (1 << 16), "bc1t", "cB" }, @@ -335,6 +338,8 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" }, { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" }, { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (1 << 19), "lwpc", "So" }, + { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (2 << 19), "lwupc", "So" }, + { kITypeMask | (0x7 << 18), (59u << kOpcodeShift) | (6 << 18), "ldpc", "S0" }, { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", }, { kITypeMask | (0x1f << 21), 62u << kOpcodeShift, "jialc", "Ti" }, { kITypeMask | (1 << 21), (62u << kOpcodeShift) | (1 << 21), "bnezc", "Sb" }, // TODO: de-dup? @@ -468,6 +473,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 'D': args << 'r' << rd; break; case 'd': args << 'f' << rd; break; case 'a': args << 'f' << sa; break; + case 'F': args << (sa + 32); break; // dinsu position. case 'f': // Floating point "fmt". { size_t fmt = (instruction >> 21) & 0x7; // TODO: other fmts? @@ -481,9 +487,6 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } continue; // No ", ". } - case 'I': // Upper 16-bit immediate. - args << reinterpret_cast<void*>((instruction & 0xffff) << 16); - break; case 'i': // Sign-extended lower 16-bit immediate. args << static_cast<int16_t>(instruction & 0xffff); break; @@ -512,7 +515,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } } break; - case 'o': // 19-bit offset in lwpc. + case 'o': // 19-bit offset in lwpc and lwupc. { int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1); offset <<= 2; @@ -520,6 +523,15 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { args << StringPrintf(" ; %+d", offset); } break; + case '0': // 18-bit offset in ldpc. + { + int32_t offset = (instruction & 0x3ffff) - ((instruction & 0x20000) << 1); + offset <<= 3; + uintptr_t ptr = RoundDown(reinterpret_cast<uintptr_t>(instr_ptr), 8); + args << FormatInstructionPointer(reinterpret_cast<const uint8_t*>(ptr + offset)); + args << StringPrintf(" ; %+d", offset); + } + break; case 'P': // 26-bit offset in bc and balc. { int32_t offset = (instruction & 0x3ffffff) - ((instruction & 0x2000000) << 1); @@ -541,7 +553,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 'T': args << 'r' << rt; break; case 't': args << 'f' << rt; break; case 'Z': args << (rd + 1); break; // sz ([d]ext size). - case 'z': args << (rd - sa + 1); break; // sz ([d]ins size). + case 'z': args << (rd - sa + 1); break; // sz ([d]ins, dinsu size). } if (*(args_fmt + 1)) { args << ", "; @@ -551,17 +563,14 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } } - // TODO: Simplify this once these sequences are simplified in the compiler. // Special cases for sequences of: // pc-relative +/- 2GB branch: // auipc reg, imm // jic reg, imm // pc-relative +/- 2GB branch and link: // auipc reg, imm - // daddiu reg, reg, imm - // jialc reg, 0 - if (((op == 0x36 && rs == 0 && rt != 0) || // jic - (op == 0x19 && rs == rt && rt != 0)) && // daddiu + // jialc reg, imm + if (((op == 0x36 || op == 0x3E) && rs == 0 && rt != 0) && // ji[al]c last_ptr_ && (intptr_t)instr_ptr - (intptr_t)last_ptr_ == 4 && (last_instr_ & 0xFC1F0000) == 0xEC1E0000 && // auipc ((last_instr_ >> 21) & 0x1F) == rt) { @@ -569,9 +578,9 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { offset -= (offset & 0x8000) << 1; offset -= 4; if (op == 0x36) { - args << " ; b "; + args << " ; bc "; } else { - args << " ; move r" << rt << ", "; + args << " ; balc "; } args << FormatInstructionPointer(instr_ptr + (int32_t)offset); args << StringPrintf(" ; %+d", (int32_t)offset); |