MIPS32: Improve method invocation
Improvements include:
- CodeGeneratorMIPS::GenerateStaticOrDirectCall() supports:
- MethodLoadKind::kDirectAddressWithFixup (via literals)
- CodePtrLocation::kCallDirectWithFixup (via literals)
- MethodLoadKind::kDexCachePcRelative
- 32-bit literals to support the above (not ready for general-
purpose applications yet because RA is not saved in leaf
methods, but is clobbered on MIPS32R2 when simulating
PC-relative addressing (MIPS32R6 is OK because it has
PC-relative addressing with the lwpc instruction))
- shorter instruction sequences for recursive static/direct
calls
Tested:
- test-art-host-gtest
- test-art-target-gtest and test-art-target-run-test-optimizing on:
- MIPS32R2 QEMU
- CI20 board
- MIPS32R6 (2nd arch) QEMU
Change-Id: Id5b137ad32d5590487fd154c9a01d3b3e7e044ff
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
new file mode 100644
index 0000000..7c0423b
--- /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 0000000..4ff2f2f
--- /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 0000000..0f1dcbc
--- /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 0000000..8391b53
--- /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