diff options
Diffstat (limited to 'compiler/linker')
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips.cc | 109 | ||||
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips.h | 57 | ||||
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips32r6_test.cc | 68 | ||||
| -rw-r--r-- | compiler/linker/mips/relative_patcher_mips_test.cc | 71 | ||||
| -rw-r--r-- | compiler/linker/relative_patcher.cc | 8 |
5 files changed, 313 insertions, 0 deletions
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc new file mode 100644 index 0000000000..7c0423b635 --- /dev/null +++ b/compiler/linker/mips/relative_patcher_mips.cc @@ -0,0 +1,109 @@ +/* + * 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/mips/relative_patcher_mips.h" + +#include "compiled_method.h" + +namespace art { +namespace linker { + +uint32_t MipsRelativePatcher::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 MipsRelativePatcher::ReserveSpaceEnd(uint32_t offset) { + return offset; // No space reserved; no limit on relative call distance. +} + +uint32_t MipsRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) { + return offset; // No thunks added; no limit on relative call distance. +} + +void MipsRelativePatcher::PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + uint32_t literal_offset ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED, + uint32_t target_offset ATTRIBUTE_UNUSED) { + UNIMPLEMENTED(FATAL) << "PatchCall unimplemented on MIPS"; +} + +void MipsRelativePatcher::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. + if (is_r6) { + 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); + // ADDIU reg, reg, offset_low + DCHECK_EQ((*code)[literal_offset + 4], 0x78); + DCHECK_EQ((*code)[literal_offset + 5], 0x56); + DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x24); + } else { + 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); + // LUI reg, offset_high + DCHECK_EQ((*code)[literal_offset + 0], 0x34); + DCHECK_EQ((*code)[literal_offset + 1], 0x12); + DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00); + DCHECK_EQ((*code)[literal_offset + 3], 0x3C); + // ORI reg, reg, offset_low + 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 + DCHECK_EQ((*code)[literal_offset + 8], 0x21); + DCHECK_EQ(((*code)[literal_offset + 9] & 0x07), 0x00); + 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; + if (is_r6) { + diff += (diff & 0x8000) << 1; // Account for sign extension in ADDIU. + } + + // LUI reg, offset_high / AUIPC reg, offset_high + (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); + (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); + // ORI reg, reg, offset_low / ADDIU reg, reg, 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/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h new file mode 100644 index 0000000000..4ff2f2f24f --- /dev/null +++ b/compiler/linker/mips/relative_patcher_mips.h @@ -0,0 +1,57 @@ +/* + * 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_MIPS_RELATIVE_PATCHER_MIPS_H_ +#define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ + +#include "linker/relative_patcher.h" +#include "arch/mips/instruction_set_features_mips.h" + +namespace art { +namespace linker { + +class MipsRelativePatcher FINAL : public RelativePatcher { + public: + explicit MipsRelativePatcher(const MipsInstructionSetFeatures* features) + : is_r6(features->IsR6()) {} + + 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: + // We'll maximize the range of a single load instruction for dex cache array accesses + // by aligning offset -32768 with the offset of the first used element. + static constexpr uint32_t kDexCacheArrayLwOffset = 0x8000; + bool is_r6; + + DISALLOW_COPY_AND_ASSIGN(MipsRelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc new file mode 100644 index 0000000000..0f1dcbcbf1 --- /dev/null +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -0,0 +1,68 @@ +/* + * 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/mips/relative_patcher_mips.h" + +namespace art { +namespace linker { + +// We'll maximize the range of a single load instruction for dex cache array accesses +// by aligning offset -32768 with the offset of the first used element. +static constexpr uint32_t kDexCacheArrayLwOffset = 0x8000; + +class Mips32r6RelativePatcherTest : public RelativePatcherTest { + public: + Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {} + + protected: + uint32_t GetMethodOffset(uint32_t method_idx) { + auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); + CHECK(result.first); + return result.second; + } +}; + +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)); + 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; + diff += (diff & 0x8000) << 1; // Account for sign extension in addiu. + static 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))); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc new file mode 100644 index 0000000000..8391b5352a --- /dev/null +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -0,0 +1,71 @@ +/* + * 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/mips/relative_patcher_mips.h" + +namespace art { +namespace linker { + +// We'll maximize the range of a single load instruction for dex cache array accesses +// by aligning offset -32768 with the offset of the first used element. +static constexpr uint32_t kDexCacheArrayLwOffset = 0x8000; + +class MipsRelativePatcherTest : public RelativePatcherTest { + public: + MipsRelativePatcherTest() : RelativePatcherTest(kMips, "mips32r2") {} + + protected: + uint32_t GetMethodOffset(uint32_t method_idx) { + auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); + CHECK(result.first); + return result.second; + } +}; + +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)); + 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[] = { + 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, + 0x21, 0x90, 0x5F, 0x02, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 3a229831d0..77655947fd 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -22,6 +22,9 @@ #ifdef ART_ENABLE_CODEGEN_arm64 #include "linker/arm64/relative_patcher_arm64.h" #endif +#ifdef ART_ENABLE_CODEGEN_mips +#include "linker/mips/relative_patcher_mips.h" +#endif #ifdef ART_ENABLE_CODEGEN_x86 #include "linker/x86/relative_patcher_x86.h" #endif @@ -95,6 +98,11 @@ std::unique_ptr<RelativePatcher> RelativePatcher::Create( return std::unique_ptr<RelativePatcher>( new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures())); #endif +#ifdef ART_ENABLE_CODEGEN_mips + case kMips: + return std::unique_ptr<RelativePatcher>( + new MipsRelativePatcher(features->AsMipsInstructionSetFeatures())); +#endif default: return std::unique_ptr<RelativePatcher>(new RelativePatcherNone); } |