Optimizing: Improve const-string code generation.
For strings in the boot image, use either direct pointers
or pc-relative addresses. For other strings, use PC-relative
access to the dex cache arrays for AOT and direct address of
the string's dex cache slot for JIT.
For aosp_flounder-userdebug:
- 32-bit boot.oat: -692KiB (-0.9%)
- 64-bit boot.oat: -948KiB (-1.1%)
- 32-bit dalvik cache total: -900KiB (-0.9%)
- 64-bit dalvik cache total: -3672KiB (-1.5%)
(contains more files than the 32-bit dalvik cache)
For aosp_flounder-userdebug forced to compile PIC:
- 32-bit boot.oat: -380KiB (-0.5%)
- 64-bit boot.oat: -928KiB (-1.0%)
- 32-bit dalvik cache total: -468KiB (-0.4%)
- 64-bit dalvik cache total: -1928KiB (-0.8%)
(contains more files than the 32-bit dalvik cache)
Bug: 26884697
Change-Id: Iec7266ce67e6fedc107be78fab2e742a8dab2696
diff --git a/compiler/Android.mk b/compiler/Android.mk
index e6ff8f6..9a416e2 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -148,6 +148,7 @@
LIBART_COMPILER_CFLAGS :=
LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
+ compiled_method.h \
dex/compiler_enums.h \
dex/dex_to_dex_compiler.h \
driver/compiler_driver.h \
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 5887620..acd5089 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_COMPILED_METHOD_H_
#include <memory>
+#include <iosfwd>
#include <string>
#include <vector>
@@ -160,15 +161,23 @@
enum LinkerPatchType {
+ kLinkerPatchRecordPosition, // Just record patch position for patchoat.
kLinkerPatchMethod,
kLinkerPatchCall,
- kLinkerPatchCallRelative, // NOTE: Actual patching is instruction_set-dependent.
+ kLinkerPatchCallRelative, // NOTE: Actual patching is instruction_set-dependent.
kLinkerPatchType,
- kLinkerPatchDexCacheArray, // NOTE: Actual patching is instruction_set-dependent.
+ kLinkerPatchString,
+ kLinkerPatchStringRelative, // NOTE: Actual patching is instruction_set-dependent.
+ kLinkerPatchDexCacheArray, // NOTE: Actual patching is instruction_set-dependent.
};
+std::ostream& operator<<(std::ostream& os, const LinkerPatchType& type);
class LinkerPatch {
public:
+ static LinkerPatch RecordPosition(size_t literal_offset) {
+ return LinkerPatch(literal_offset, kLinkerPatchRecordPosition, /* target_dex_file */ nullptr);
+ }
+
static LinkerPatch MethodPatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t target_method_idx) {
@@ -201,6 +210,24 @@
return patch;
}
+ static LinkerPatch StringPatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t target_string_idx) {
+ LinkerPatch patch(literal_offset, kLinkerPatchString, target_dex_file);
+ patch.string_idx_ = target_string_idx;
+ return patch;
+ }
+
+ static LinkerPatch RelativeStringPatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t pc_insn_offset,
+ uint32_t target_string_idx) {
+ LinkerPatch patch(literal_offset, kLinkerPatchStringRelative, target_dex_file);
+ patch.string_idx_ = target_string_idx;
+ patch.pc_insn_offset_ = pc_insn_offset;
+ return patch;
+ }
+
static LinkerPatch DexCacheArrayPatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t pc_insn_offset,
@@ -224,7 +251,14 @@
}
bool IsPcRelative() const {
- return Type() == kLinkerPatchCallRelative || Type() == kLinkerPatchDexCacheArray;
+ switch (Type()) {
+ case kLinkerPatchCallRelative:
+ case kLinkerPatchStringRelative:
+ case kLinkerPatchDexCacheArray:
+ return true;
+ default:
+ return false;
+ }
}
MethodReference TargetMethod() const {
@@ -243,6 +277,16 @@
return type_idx_;
}
+ const DexFile* TargetStringDexFile() const {
+ DCHECK(patch_type_ == kLinkerPatchString || patch_type_ == kLinkerPatchStringRelative);
+ return target_dex_file_;
+ }
+
+ uint32_t TargetStringIndex() const {
+ DCHECK(patch_type_ == kLinkerPatchString || patch_type_ == kLinkerPatchStringRelative);
+ return string_idx_;
+ }
+
const DexFile* TargetDexCacheDexFile() const {
DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
return target_dex_file_;
@@ -254,7 +298,7 @@
}
uint32_t PcInsnOffset() const {
- DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+ DCHECK(patch_type_ == kLinkerPatchStringRelative || patch_type_ == kLinkerPatchDexCacheArray);
return pc_insn_offset_;
}
@@ -277,9 +321,11 @@
uint32_t cmp1_; // Used for relational operators.
uint32_t method_idx_; // Method index for Call/Method patches.
uint32_t type_idx_; // Type index for Type patches.
+ uint32_t string_idx_; // String index for String patches.
uint32_t element_offset_; // Element offset in the dex cache arrays.
static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
+ static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators");
static_assert(sizeof(element_offset_) == sizeof(cmp1_), "needed by relational operators");
};
union {
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index c090dff..582ecb3 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -56,10 +56,10 @@
SetInsn32(code, literal_offset, value);
}
-void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
+void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
uint32_t literal_offset = patch.LiteralOffset();
uint32_t pc_literal_offset = patch.PcInsnOffset();
uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */;
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 0d903c0..d85739c 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -30,10 +30,10 @@
uint32_t literal_offset,
uint32_t patch_offset,
uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- 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:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index a259cda..a8078e3 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -30,6 +30,9 @@
static const ArrayRef<const uint8_t> kCallCode;
static const uint8_t kNopRawCode[];
static const ArrayRef<const uint8_t> kNopCode;
+ static const uint8_t kUnpatchedPcRelativeRawCode[];
+ static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
+ static const uint32_t kPcInsnOffset;
// Branches within range [-256, 256) can be created from these by adding the low 8 bits.
static constexpr uint32_t kBlPlus0 = 0xf000f800;
@@ -123,47 +126,9 @@
return result;
}
- void TestDexCachereference(uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
- dex_cache_arrays_begin_ = dex_cache_arrays_begin;
- static const uint8_t raw_code[] = {
- 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
- 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
- 0x78, 0x44, // ADD r0, pc
- };
- constexpr uint32_t pc_insn_offset = 8u;
- const ArrayRef<const uint8_t> code(raw_code);
- LinkerPatch patches[] = {
- LinkerPatch::DexCacheArrayPatch(0u, nullptr, pc_insn_offset, element_offset),
- LinkerPatch::DexCacheArrayPatch(4u, nullptr, pc_insn_offset, element_offset),
- };
- AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t pc_base_offset = method1_offset + pc_insn_offset + 4u /* PC adjustment */;
- uint32_t diff = dex_cache_arrays_begin_ + element_offset - pc_base_offset;
- // Distribute the bits of the diff between the MOVW and MOVT:
- uint32_t diffw = diff & 0xffffu;
- uint32_t difft = diff >> 16;
- uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
- ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
- ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
- ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
- ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
- uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
- ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
- ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
- ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
- ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
- const uint8_t expected_code[] = {
- static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
- static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
- static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
- static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
- 0x78, 0x44,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- }
+ void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset);
+ void TestStringReference(uint32_t string_offset);
+ void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
};
const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
@@ -178,6 +143,67 @@
const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
+const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
+ 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
+ 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
+ 0x78, 0x44, // ADD r0, pc
+};
+const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode(
+ kUnpatchedPcRelativeRawCode);
+const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u;
+
+void Thumb2RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
+ dex_cache_arrays_begin_ = dex_cache_arrays_begin;
+ LinkerPatch patches[] = {
+ LinkerPatch::DexCacheArrayPatch(0u, nullptr, kPcInsnOffset, element_offset),
+ LinkerPatch::DexCacheArrayPatch(4u, nullptr, kPcInsnOffset, element_offset),
+ };
+ CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
+ dex_cache_arrays_begin_ + element_offset);
+}
+
+void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) {
+ constexpr uint32_t kStringIndex = 1u;
+ string_index_to_offset_map_.Put(kStringIndex, string_offset);
+ LinkerPatch patches[] = {
+ LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
+ LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
+ };
+ CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
+}
+
+void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
+ uint32_t target_offset) {
+ AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */;
+ uint32_t diff = target_offset - pc_base_offset;
+ // Distribute the bits of the diff between the MOVW and MOVT:
+ uint32_t diffw = diff & 0xffffu;
+ uint32_t difft = diff >> 16;
+ uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
+ ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
+ uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
+ ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
+ const uint8_t expected_code[] = {
+ static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
+ static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
+ static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
+ static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
+ 0x78, 0x44,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
TEST_F(Thumb2RelativePatcherTest, CallSelf) {
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
@@ -366,23 +392,43 @@
EXPECT_TRUE(CheckThunk(thunk_offset));
}
-TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm8) {
- TestDexCachereference(0x00ff0000u, 0x00fcu);
+TEST_F(Thumb2RelativePatcherTest, DexCacheReference1) {
+ TestDexCacheReference(0x00ff0000u, 0x00fcu);
ASSERT_LT(GetMethodOffset(1u), 0xfcu);
}
-TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm3) {
- TestDexCachereference(0x02ff0000u, 0x05fcu);
+TEST_F(Thumb2RelativePatcherTest, DexCacheReference2) {
+ TestDexCacheReference(0x02ff0000u, 0x05fcu);
ASSERT_LT(GetMethodOffset(1u), 0xfcu);
}
-TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm) {
- TestDexCachereference(0x08ff0000u, 0x08fcu);
+TEST_F(Thumb2RelativePatcherTest, DexCacheReference3) {
+ TestDexCacheReference(0x08ff0000u, 0x08fcu);
ASSERT_LT(GetMethodOffset(1u), 0xfcu);
}
-TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceimm4) {
- TestDexCachereference(0xd0ff0000u, 0x60fcu);
+TEST_F(Thumb2RelativePatcherTest, DexCacheReference4) {
+ TestDexCacheReference(0xd0ff0000u, 0x60fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, StringReference1) {
+ TestStringReference(0x00ff00fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, StringReference2) {
+ TestStringReference(0x02ff05fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, StringReference3) {
+ TestStringReference(0x08ff08fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, StringReference4) {
+ TestStringReference(0xd0ff60fcu);
ASSERT_LT(GetMethodOffset(1u), 0xfcu);
}
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index a81c85c..0549327 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -28,6 +28,16 @@
namespace art {
namespace linker {
+namespace {
+
+inline bool IsAdrpPatch(const LinkerPatch& patch) {
+ LinkerPatchType type = patch.Type();
+ return (type == kLinkerPatchStringRelative || type == kLinkerPatchDexCacheArray) &&
+ patch.LiteralOffset() == patch.PcInsnOffset();
+}
+
+} // anonymous namespace
+
Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider,
const Arm64InstructionSetFeatures* features)
: ArmBaseRelativePatcher(provider, kArm64, CompileThunkCode(),
@@ -61,8 +71,7 @@
size_t num_adrp = 0u;
DCHECK(compiled_method != nullptr);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.Type() == kLinkerPatchDexCacheArray &&
- patch.LiteralOffset() == patch.PcInsnOffset()) { // ADRP patch
+ if (IsAdrpPatch(patch)) {
++num_adrp;
}
}
@@ -78,8 +87,7 @@
uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
DCHECK(compiled_method != nullptr);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.Type() == kLinkerPatchDexCacheArray &&
- patch.LiteralOffset() == patch.PcInsnOffset()) { // ADRP patch
+ if (IsAdrpPatch(patch)) {
uint32_t patch_offset = quick_code_offset + patch.LiteralOffset();
if (NeedsErratum843419Thunk(code, patch.LiteralOffset(), patch_offset)) {
adrp_thunk_locations_.emplace_back(patch_offset, thunk_offset);
@@ -151,10 +159,10 @@
SetInsn(code, literal_offset, insn);
}
-void Arm64RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
+void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_EQ(patch_offset & 3u, 0u);
DCHECK_EQ(target_offset & 3u, 0u);
uint32_t literal_offset = patch.LiteralOffset();
@@ -199,8 +207,15 @@
// Write the new ADRP (or B to the erratum 843419 thunk).
SetInsn(code, literal_offset, insn);
} else {
- // LDR 32-bit or 64-bit with imm12 == 0 (unset).
- DCHECK_EQ(insn & 0xbffffc00, 0xb9400000) << insn;
+ if ((insn & 0xfffffc00) == 0x91000000) {
+ // ADD immediate, 64-bit with imm12 == 0 (unset).
+ DCHECK(patch.Type() == kLinkerPatchStringRelative) << patch.Type();
+ shift = 0u; // No shift for ADD.
+ } else {
+ // LDR 32-bit or 64-bit with imm12 == 0 (unset).
+ DCHECK(patch.Type() == kLinkerPatchDexCacheArray) << patch.Type();
+ DCHECK_EQ(insn & 0xbffffc00, 0xb9400000) << std::hex << insn;
+ }
if (kIsDebugBuild) {
uint32_t adrp = GetInsn(code, pc_insn_offset);
if ((adrp & 0x9f000000u) != 0x90000000u) {
@@ -263,7 +278,7 @@
DCHECK_EQ(patch_offset & 0x3u, 0u);
if ((patch_offset & 0xff8) == 0xff8) { // ...ff8 or ...ffc
uint32_t adrp = GetInsn(code, literal_offset);
- DCHECK_EQ(adrp & 0xff000000, 0x90000000);
+ DCHECK_EQ(adrp & 0x9f000000, 0x90000000);
uint32_t next_offset = patch_offset + 4u;
uint32_t next_insn = GetInsn(code, literal_offset + 4u);
@@ -277,6 +292,14 @@
return false;
}
+ // And since kLinkerPatchStringRelative is using the result of the ADRP for an ADD immediate,
+ // check for that as well. We generalize a bit to include ADD/ADDS/SUB/SUBS immediate that
+ // either uses the ADRP destination or stores the result to a different register.
+ if ((next_insn & 0x1f000000) == 0x11000000 &&
+ ((((next_insn >> 5) ^ adrp) & 0x1f) == 0 || ((next_insn ^ adrp) & 0x1f) != 0)) {
+ return false;
+ }
+
// LDR <Wt>, <label> is always aligned and thus it doesn't cause boundary crossing.
if ((next_insn & 0xff000000) == 0x18000000) {
return false;
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index f9b76e6..48ad105 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -37,10 +37,10 @@
uint32_t literal_offset,
uint32_t patch_offset,
uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- 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:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 0bfef5e..09729fd 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -40,6 +40,15 @@
static constexpr uint32_t kBlPlusMax = 0x95ffffffu;
static constexpr uint32_t kBlMinusMax = 0x96000000u;
+ // LDR immediate, 32-bit.
+ static constexpr uint32_t kLdrWInsn = 0xb9400000u;
+
+ // ADD/ADDS/SUB/SUBS immediate, 64-bit.
+ static constexpr uint32_t kAddXInsn = 0x91000000u;
+ static constexpr uint32_t kAddsXInsn = 0xb1000000u;
+ static constexpr uint32_t kSubXInsn = 0xd1000000u;
+ static constexpr uint32_t kSubsXInsn = 0xf1000000u;
+
// LDUR x2, [sp, #4], i.e. unaligned load crossing 64-bit boundary (assuming aligned sp).
static constexpr uint32_t kLdurInsn = 0xf840405fu;
@@ -109,7 +118,7 @@
uint32_t GetMethodOffset(uint32_t method_idx) {
auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
CHECK(result.first);
- CHECK_EQ(result.second & 3u, 0u);
+ CHECK_ALIGNED(result.second, 4u);
return result.second;
}
@@ -147,20 +156,29 @@
return result;
}
- std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops,
- uint32_t method_offset, uint32_t target_offset) {
+ std::vector<uint8_t> GenNopsAndAdrpAndUse(size_t num_nops,
+ uint32_t method_offset,
+ uint32_t target_offset,
+ uint32_t use_insn) {
std::vector<uint8_t> result;
result.reserve(num_nops * 4u + 8u);
for (size_t i = 0; i != num_nops; ++i) {
result.insert(result.end(), kNopCode.begin(), kNopCode.end());
}
- DCHECK_EQ(method_offset & 3u, 0u);
- DCHECK_EQ(target_offset & 3u, 0u);
+ CHECK_ALIGNED(method_offset, 4u);
+ CHECK_ALIGNED(target_offset, 4u);
uint32_t adrp_offset = method_offset + num_nops * 4u;
uint32_t disp = target_offset - (adrp_offset & ~0xfffu);
- DCHECK_EQ(disp & 3u, 0u);
- uint32_t ldr = 0xb9400001 | // LDR w1, [x0, #(imm12 * 2)]
- ((disp & 0xfffu) << (10 - 2)); // imm12 = ((disp & 0xfffu) >> 2) is at bit 10.
+ if (use_insn == kLdrWInsn) {
+ DCHECK_ALIGNED(disp, 1u << 2);
+ use_insn |= 1 | // LDR x1, [x0, #(imm12 << 2)]
+ ((disp & 0xfffu) << (10 - 2)); // imm12 = ((disp & 0xfffu) >> 2) is at bit 10.
+ } else if (use_insn == kAddXInsn) {
+ use_insn |= 1 | // ADD x1, x0, #imm
+ (disp & 0xfffu) << 10; // imm12 = (disp & 0xfffu) is at bit 10.
+ } else {
+ LOG(FATAL) << "Unexpected instruction: 0x" << std::hex << use_insn;
+ }
uint32_t adrp = 0x90000000 | // ADRP x0, +SignExtend(immhi:immlo:Zeros(12), 64)
((disp & 0x3000u) << (29 - 12)) | // immlo = ((disp & 0x3000u) >> 12) is at bit 29,
((disp & 0xffffc000) >> (14 - 5)) | // immhi = (disp >> 14) is at bit 5,
@@ -170,13 +188,19 @@
result.push_back(static_cast<uint8_t>(adrp >> 8));
result.push_back(static_cast<uint8_t>(adrp >> 16));
result.push_back(static_cast<uint8_t>(adrp >> 24));
- result.push_back(static_cast<uint8_t>(ldr));
- result.push_back(static_cast<uint8_t>(ldr >> 8));
- result.push_back(static_cast<uint8_t>(ldr >> 16));
- result.push_back(static_cast<uint8_t>(ldr >> 24));
+ result.push_back(static_cast<uint8_t>(use_insn));
+ result.push_back(static_cast<uint8_t>(use_insn >> 8));
+ result.push_back(static_cast<uint8_t>(use_insn >> 16));
+ result.push_back(static_cast<uint8_t>(use_insn >> 24));
return result;
}
+ std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops,
+ uint32_t method_offset,
+ uint32_t target_offset) {
+ return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kLdrWInsn);
+ }
+
void TestNopsAdrpLdr(size_t num_nops, uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
dex_cache_arrays_begin_ = dex_cache_arrays_begin;
auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched.
@@ -184,7 +208,8 @@
LinkerPatch::DexCacheArrayPatch(num_nops * 4u , nullptr, num_nops * 4u, element_offset),
LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, element_offset),
};
- AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code),
+ AddCompiledMethod(MethodRef(1u),
+ ArrayRef<const uint8_t>(code),
ArrayRef<const LinkerPatch>(patches));
Link();
@@ -194,6 +219,30 @@
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+ std::vector<uint8_t> GenNopsAndAdrpAdd(size_t num_nops,
+ uint32_t method_offset,
+ uint32_t target_offset) {
+ return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kAddXInsn);
+ }
+
+ void TestNopsAdrpAdd(size_t num_nops, uint32_t string_offset) {
+ constexpr uint32_t kStringIndex = 1u;
+ string_index_to_offset_map_.Put(kStringIndex, string_offset);
+ auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched.
+ LinkerPatch patches[] = {
+ LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
+ LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
+ };
+ AddCompiledMethod(MethodRef(1u),
+ ArrayRef<const uint8_t>(code),
+ ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ uint32_t method1_offset = GetMethodOffset(1u);
+ auto expected_code = GenNopsAndAdrpAdd(num_nops, method1_offset, string_offset);
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+ }
+
void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
CHECK_LE(pos, code->size());
const uint8_t insn_code[] = {
@@ -204,8 +253,10 @@
code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
}
- void PrepareNopsAdrpInsn2Ldr(size_t num_nops, uint32_t insn2,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ void PrepareNopsAdrpInsn2Ldr(size_t num_nops,
+ uint32_t insn2,
+ uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
dex_cache_arrays_begin_ = dex_cache_arrays_begin;
auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched.
InsertInsn(&code, num_nops * 4u + 4u, insn2);
@@ -213,26 +264,41 @@
LinkerPatch::DexCacheArrayPatch(num_nops * 4u , nullptr, num_nops * 4u, element_offset),
LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, element_offset),
};
- AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code),
+ AddCompiledMethod(MethodRef(1u),
+ ArrayRef<const uint8_t>(code),
ArrayRef<const LinkerPatch>(patches));
Link();
}
- void TestNopsAdrpInsn2Ldr(size_t num_nops, uint32_t insn2,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
- PrepareNopsAdrpInsn2Ldr(num_nops, insn2, dex_cache_arrays_begin, element_offset);
+ void PrepareNopsAdrpInsn2Add(size_t num_nops, uint32_t insn2, uint32_t string_offset) {
+ constexpr uint32_t kStringIndex = 1u;
+ string_index_to_offset_map_.Put(kStringIndex, string_offset);
+ auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched.
+ InsertInsn(&code, num_nops * 4u + 4u, insn2);
+ LinkerPatch patches[] = {
+ LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
+ LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
+ };
+ AddCompiledMethod(MethodRef(1u),
+ ArrayRef<const uint8_t>(code),
+ ArrayRef<const LinkerPatch>(patches));
+ Link();
+ }
+ void TestNopsAdrpInsn2AndUse(size_t num_nops,
+ uint32_t insn2,
+ uint32_t target_offset,
+ uint32_t use_insn) {
uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t target_offset = dex_cache_arrays_begin_ + element_offset;
- auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset);
+ auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
- void TestNopsAdrpInsn2LdrHasThunk(size_t num_nops, uint32_t insn2,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
- PrepareNopsAdrpInsn2Ldr(num_nops, insn2, dex_cache_arrays_begin, element_offset);
-
+ void TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,
+ uint32_t insn2,
+ uint32_t target_offset,
+ uint32_t use_insn) {
uint32_t method1_offset = GetMethodOffset(1u);
CHECK(!compiled_method_refs_.empty());
CHECK_EQ(compiled_method_refs_[0].dex_method_index, 1u);
@@ -240,13 +306,12 @@
uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size();
uint32_t thunk_offset = CompiledCode::AlignCode(method1_offset + method1_size, kArm64);
uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u);
- ASSERT_EQ(b_diff & 3u, 0u);
+ CHECK_ALIGNED(b_diff, 4u);
ASSERT_LT(b_diff, 128 * MB);
uint32_t b_out = kBPlus0 + ((b_diff >> 2) & 0x03ffffffu);
uint32_t b_in = kBPlus0 + ((-b_diff >> 2) & 0x03ffffffu);
- uint32_t target_offset = dex_cache_arrays_begin_ + element_offset;
- auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset);
+ auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
// Replace adrp with bl.
expected_code.erase(expected_code.begin() + num_nops * 4u,
@@ -270,29 +335,39 @@
}
}
- void TestAdrpInsn2Ldr(uint32_t insn2, uint32_t adrp_offset, bool has_thunk,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ void TestAdrpInsn2Ldr(uint32_t insn2,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
uint32_t method1_offset =
CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
ASSERT_LT(method1_offset, adrp_offset);
- ASSERT_EQ(adrp_offset & 3u, 0u);
+ CHECK_ALIGNED(adrp_offset, 4u);
uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
+ PrepareNopsAdrpInsn2Ldr(num_nops, insn2, dex_cache_arrays_begin, element_offset);
+ uint32_t target_offset = dex_cache_arrays_begin_ + element_offset;
if (has_thunk) {
- TestNopsAdrpInsn2LdrHasThunk(num_nops, insn2, dex_cache_arrays_begin, element_offset);
+ TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, target_offset, kLdrWInsn);
} else {
- TestNopsAdrpInsn2Ldr(num_nops, insn2, dex_cache_arrays_begin, element_offset);
+ TestNopsAdrpInsn2AndUse(num_nops, insn2, target_offset, kLdrWInsn);
}
ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong.
}
- void TestAdrpLdurLdr(uint32_t adrp_offset, bool has_thunk,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ void TestAdrpLdurLdr(uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
TestAdrpInsn2Ldr(kLdurInsn, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
}
- void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn, int32_t pcrel_disp,
- uint32_t adrp_offset, bool has_thunk,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,
+ int32_t pcrel_disp,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
ASSERT_LT(pcrel_disp, 0x100000);
ASSERT_GE(pcrel_disp, -0x100000);
ASSERT_EQ(pcrel_disp & 0x3, 0);
@@ -300,13 +375,60 @@
TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
}
- void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn, uint32_t sprel_disp_in_load_units,
- uint32_t adrp_offset, bool has_thunk,
- uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,
+ uint32_t sprel_disp_in_load_units,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, dex_cache_arrays_begin, element_offset);
}
+
+ void TestAdrpInsn2Add(uint32_t insn2,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t string_offset) {
+ uint32_t method1_offset =
+ CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
+ ASSERT_LT(method1_offset, adrp_offset);
+ CHECK_ALIGNED(adrp_offset, 4u);
+ uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
+ PrepareNopsAdrpInsn2Add(num_nops, insn2, string_offset);
+ if (has_thunk) {
+ TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, string_offset, kAddXInsn);
+ } else {
+ TestNopsAdrpInsn2AndUse(num_nops, insn2, string_offset, kAddXInsn);
+ }
+ ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong.
+ }
+
+ void TestAdrpLdurAdd(uint32_t adrp_offset, bool has_thunk, uint32_t string_offset) {
+ TestAdrpInsn2Add(kLdurInsn, adrp_offset, has_thunk, string_offset);
+ }
+
+ void TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,
+ int32_t pcrel_disp,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t string_offset) {
+ ASSERT_LT(pcrel_disp, 0x100000);
+ ASSERT_GE(pcrel_disp, -0x100000);
+ ASSERT_EQ(pcrel_disp & 0x3, 0);
+ uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
+ TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
+ }
+
+ void TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,
+ uint32_t sprel_disp_in_load_units,
+ uint32_t adrp_offset,
+ bool has_thunk,
+ uint32_t string_offset) {
+ ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
+ uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
+ TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
+ }
};
const uint8_t Arm64RelativePatcherTest::kCallRawCode[] = {
@@ -358,14 +480,14 @@
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t method2_offset = GetMethodOffset(2u);
uint32_t diff_after = method2_offset - method1_offset;
- ASSERT_EQ(diff_after & 3u, 0u);
+ CHECK_ALIGNED(diff_after, 4u);
ASSERT_LT(diff_after >> 2, 1u << 8); // Simple encoding, (diff_after >> 2) fits into 8 bits.
static const uint8_t method1_expected_code[] = {
static_cast<uint8_t>(diff_after >> 2), 0x00, 0x00, 0x94
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
uint32_t diff_before = method1_offset - method2_offset;
- ASSERT_EQ(diff_before & 3u, 0u);
+ CHECK_ALIGNED(diff_before, 4u);
ASSERT_GE(diff_before, -1u << 27);
auto method2_expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff_before >> 2) & 0x03ffffffu));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
@@ -411,7 +533,7 @@
uint32_t thunk_offset =
CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- ASSERT_EQ(diff & 3u, 0u);
+ CHECK_ALIGNED(diff, 4u);
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
@@ -497,7 +619,7 @@
uint32_t thunk_offset = last_method_header_offset - CompiledCode::AlignCode(ThunkSize(), kArm64);
ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
- ASSERT_EQ(diff & 3u, 0u);
+ CHECK_ALIGNED(diff, 4u);
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
@@ -527,7 +649,7 @@
uint32_t thunk_offset =
CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- ASSERT_EQ(diff & 3u, 0u);
+ CHECK_ALIGNED(diff, 4u);
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
@@ -551,74 +673,158 @@
TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u);
}
-TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference0xff4) {
- TestAdrpLdurLdr(0xff4u, false, 0x12345678u, 0x1234u);
+TEST_F(Arm64RelativePatcherTestDefault, StringReference1) {
+ TestNopsAdrpAdd(0u, 0x12345678u);
}
-TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference0xff8) {
- TestAdrpLdurLdr(0xff8u, true, 0x12345678u, 0x1234u);
+TEST_F(Arm64RelativePatcherTestDefault, StringReference2) {
+ TestNopsAdrpAdd(0u, -0x12345678u);
}
-TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference0xffc) {
- TestAdrpLdurLdr(0xffcu, true, 0x12345678u, 0x1234u);
+TEST_F(Arm64RelativePatcherTestDefault, StringReference3) {
+ TestNopsAdrpAdd(0u, 0x12345000u);
}
-TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference0x1000) {
- TestAdrpLdurLdr(0x1000u, false, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference0xff4) {
- TestAdrpLdurLdr(0xff4u, false, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference0xff8) {
- TestAdrpLdurLdr(0xff8u, false, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference0xffc) {
- TestAdrpLdurLdr(0xffcu, false, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference0x1000) {
- TestAdrpLdurLdr(0x1000u, false, 0x12345678u, 0x1234u);
+TEST_F(Arm64RelativePatcherTestDefault, StringReference4) {
+ TestNopsAdrpAdd(0u, 0x12345ffcu);
}
#define TEST_FOR_OFFSETS(test, disp1, disp2) \
test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \
test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2)
+#define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## Ldur ## disp) { \
+ bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
+ TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238)
+
+#define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDenver64, DexCacheReference ## adrp_offset ## Ldur ## disp) { \
+ TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \
+ }
+
+TEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238)
+
// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
-#define LDRW_PCREL_TEST(adrp_offset, disp) \
+#define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \
TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## WPcRel ## disp) { \
TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \
}
-TEST_FOR_OFFSETS(LDRW_PCREL_TEST, 0x1234, 0x1238)
+TEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238)
// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
-#define LDRX_PCREL_TEST(adrp_offset, disp) \
+#define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \
TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## XPcRel ## disp) { \
- bool unaligned = ((adrp_offset + 4u + static_cast<uint32_t>(disp)) & 7u) != 0; \
+ bool unaligned = !IsAligned<8u>(adrp_offset + 4u + static_cast<uint32_t>(disp)); \
bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu) && unaligned; \
TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \
}
-TEST_FOR_OFFSETS(LDRX_PCREL_TEST, 0x1234, 0x1238)
+TEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238)
// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
-#define LDRW_SPREL_TEST(adrp_offset, disp) \
+#define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \
TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## WSpRel ## disp) { \
TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, disp >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \
}
-TEST_FOR_OFFSETS(LDRW_SPREL_TEST, 0, 4)
+TEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4)
-#define LDRX_SPREL_TEST(adrp_offset, disp) \
+#define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \
TEST_F(Arm64RelativePatcherTestDefault, DexCacheReference ## adrp_offset ## XSpRel ## disp) { \
TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, disp >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \
}
-TEST_FOR_OFFSETS(LDRX_SPREL_TEST, 0, 8)
+TEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8)
+
+#define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \
+ bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
+ TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
+
+#define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \
+ TestAdrpLdurAdd(adrp_offset, false, disp); \
+ }
+
+TEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
+
+#define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \
+ /* SUB unrelated to "ADRP x0, addr". */ \
+ uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ \
+ TestAdrpInsn2Add(sub, adrp_offset, false, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840)
+
+#define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \
+ /* SUBS that uses the result of "ADRP x0, addr". */ \
+ uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ \
+ TestAdrpInsn2Add(subs, adrp_offset, false, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840)
+
+#define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \
+ /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \
+ uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ \
+ TestAdrpInsn2Add(add, adrp_offset, false, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840)
+
+#define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \
+ /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \
+ uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ \
+ bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu); \
+ TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \
+ }
+
+TEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840)
+
+// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
+#define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \
+ TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \
+ }
+
+TEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238)
+
+// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
+#define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \
+ bool unaligned = !IsAligned<8u>(adrp_offset + 4u + static_cast<uint32_t>(disp)); \
+ bool has_thunk = (adrp_offset == 0xff8u || adrp_offset == 0xffcu) && unaligned; \
+ TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \
+ }
+
+TEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238)
+
+// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
+#define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \
+ TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, disp >> 2, adrp_offset, false, 0x12345678u); \
+ }
+
+TEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4)
+
+#define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \
+ TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \
+ TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, disp >> 3, adrp_offset, false, 0x12345678u); \
+ }
+
+TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8)
} // namespace linker
} // namespace art
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
index 1727d52..dbda03f 100644
--- a/compiler/linker/multi_oat_relative_patcher.h
+++ b/compiler/linker/multi_oat_relative_patcher.h
@@ -103,13 +103,13 @@
}
// Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment.
- void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
+ void PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
patch_offset += adjustment_;
target_offset += adjustment_;
- relative_patcher_->PatchDexCacheReference(code, patch, patch_offset, target_offset);
+ relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
}
// Wrappers around RelativePatcher for statistics retrieval.
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
index 792cdfe..92a96a0 100644
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -86,10 +86,10 @@
last_target_offset_ = target_offset;
}
- void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
+ void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
last_literal_offset_ = patch.LiteralOffset();
last_patch_offset_ = patch_offset;
last_target_offset_ = target_offset;
@@ -277,7 +277,7 @@
uint32_t method2_target_offset = 0xccccu;
LinkerPatch method2_patch =
LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u);
- patcher_.PatchDexCacheReference(
+ patcher_.PatchPcRelativeReference(
&code, method2_patch, method2_patch_offset, method2_target_offset);
DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 6727c17..3a22983 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -62,10 +62,10 @@
LOG(FATAL) << "Unexpected relative call patch.";
}
- virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) {
+ void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
LOG(FATAL) << "Unexpected relative dex cache array patch.";
}
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index ba37451..a22b9f2 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -104,10 +104,10 @@
uint32_t target_offset) = 0;
// Patch a reference to a dex cache location.
- virtual void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) = 0;
+ virtual void PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) = 0;
protected:
RelativePatcher()
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 704135a..c9fb543 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -150,12 +150,19 @@
offset + patch.LiteralOffset(), target_offset);
} else if (patch.Type() == kLinkerPatchDexCacheArray) {
uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset();
- patcher_->PatchDexCacheReference(&patched_code_,
- patch,
- offset + patch.LiteralOffset(),
- target_offset);
+ patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset + patch.LiteralOffset(),
+ target_offset);
+ } else if (patch.Type() == kLinkerPatchStringRelative) {
+ uint32_t target_offset = string_index_to_offset_map_.Get(patch.TargetStringIndex());
+ patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset + patch.LiteralOffset(),
+ target_offset);
} else {
- LOG(FATAL) << "Bad patch type.";
+ LOG(FATAL) << "Bad patch type. " << patch.Type();
+ UNREACHABLE();
}
}
}
@@ -257,6 +264,7 @@
MethodOffsetMap method_offset_map_;
std::unique_ptr<RelativePatcher> patcher_;
uint32_t dex_cache_arrays_begin_;
+ SafeMap<uint32_t, uint32_t> string_index_to_offset_map_;
std::vector<MethodReference> compiled_method_refs_;
std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
std::vector<uint8_t> patched_code_;
diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc
index 24b1481..768d31a 100644
--- a/compiler/linker/x86/relative_patcher_x86.cc
+++ b/compiler/linker/x86/relative_patcher_x86.cc
@@ -21,10 +21,10 @@
namespace art {
namespace linker {
-void X86RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
+void X86RelativePatcher::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();
diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h
index ddc244c..fbf9ad4 100644
--- a/compiler/linker/x86/relative_patcher_x86.h
+++ b/compiler/linker/x86/relative_patcher_x86.h
@@ -26,10 +26,10 @@
public:
X86RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- 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;
};
} // namespace linker
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
index 7acc330..2a44b79 100644
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ b/compiler/linker/x86/relative_patcher_x86_test.cc
@@ -70,15 +70,19 @@
uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */);
static const uint8_t method1_expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8),
- static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24)
+ static_cast<uint8_t>(diff_after),
+ static_cast<uint8_t>(diff_after >> 8),
+ static_cast<uint8_t>(diff_after >> 16),
+ static_cast<uint8_t>(diff_after >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */);
static const uint8_t method2_expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8),
- static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24)
+ static_cast<uint8_t>(diff_before),
+ static_cast<uint8_t>(diff_before >> 8),
+ static_cast<uint8_t>(diff_before >> 16),
+ static_cast<uint8_t>(diff_before >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
}
@@ -95,8 +99,10 @@
uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size());
static const uint8_t expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
@@ -125,8 +131,42 @@
0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
0x5b, // pop ebx
0x8b, 0x83, // mov eax, [ebx + diff]
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
+TEST_F(X86RelativePatcherTest, StringReference) {
+ constexpr uint32_t kStringIndex = 1u;
+ constexpr uint32_t kStringOffset = 0x12345678;
+ string_index_to_offset_map_.Put(kStringIndex, kStringOffset);
+ static const uint8_t raw_code[] = {
+ 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
+ 0x5b, // pop ebx
+ 0x8d, 0x83, 0x00, 0x01, 0x00, 0x00, // lea eax, [ebx + 256 (kDummy32BitValue)]
+ };
+ constexpr uint32_t anchor_offset = 5u; // After call +0.
+ ArrayRef<const uint8_t> code(raw_code);
+ LinkerPatch patches[] = {
+ LinkerPatch::RelativeStringPatch(code.size() - 4u, nullptr, anchor_offset, kStringIndex),
+ };
+ AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+ ASSERT_TRUE(result.first);
+ uint32_t diff = kStringOffset - (result.second + anchor_offset);
+ static const uint8_t expected_code[] = {
+ 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
+ 0x5b, // pop ebx
+ 0x8d, 0x83, // lea eax, [ebx + diff]
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc
index e571f50..2ff6930 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc
@@ -21,10 +21,10 @@
namespace art {
namespace linker {
-void X86_64RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
+void X86_64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(patch.LiteralOffset() + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h
index feecb3a..11bb6d5 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.h
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.h
@@ -26,10 +26,10 @@
public:
X86_64RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- 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;
};
} // namespace linker
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
index 36e0f01..2b46453 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
@@ -29,6 +29,8 @@
static const ArrayRef<const uint8_t> kCallCode;
static const uint8_t kDexCacheLoadRawCode[];
static const ArrayRef<const uint8_t> kDexCacheLoadCode;
+ static const uint8_t kStringReferenceRawCode[];
+ static const ArrayRef<const uint8_t> kStringReferenceCode;
uint32_t GetMethodOffset(uint32_t method_idx) {
auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -51,6 +53,14 @@
const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kDexCacheLoadCode(
kDexCacheLoadRawCode);
+const uint8_t X86_64RelativePatcherTest::kStringReferenceRawCode[] = {
+ 0x8d, 0x05, // lea eax, [rip + <offset>]
+ 0x00, 0x01, 0x00, 0x00
+};
+
+const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kStringReferenceCode(
+ kStringReferenceRawCode);
+
TEST_F(X86_64RelativePatcherTest, CallSelf) {
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
@@ -80,15 +90,19 @@
uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */);
static const uint8_t method1_expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8),
- static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24)
+ static_cast<uint8_t>(diff_after),
+ static_cast<uint8_t>(diff_after >> 8),
+ static_cast<uint8_t>(diff_after >> 16),
+ static_cast<uint8_t>(diff_after >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */);
static const uint8_t method2_expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8),
- static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24)
+ static_cast<uint8_t>(diff_before),
+ static_cast<uint8_t>(diff_before >> 8),
+ static_cast<uint8_t>(diff_before >> 16),
+ static_cast<uint8_t>(diff_before >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
}
@@ -105,8 +119,10 @@
uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size());
static const uint8_t expected_code[] = {
0xe8,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
@@ -126,8 +142,34 @@
dex_cache_arrays_begin_ + kElementOffset - (result.second + kDexCacheLoadCode.size());
static const uint8_t expected_code[] = {
0x8b, 0x05,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
+TEST_F(X86_64RelativePatcherTest, StringReference) {
+ constexpr uint32_t kStringIndex = 1u;
+ constexpr uint32_t kStringOffset = 0x12345678;
+ string_index_to_offset_map_.Put(kStringIndex, kStringOffset);
+ LinkerPatch patches[] = {
+ LinkerPatch::RelativeStringPatch(
+ kStringReferenceCode.size() - 4u, nullptr, 0u, kStringIndex),
+ };
+ AddCompiledMethod(MethodRef(1u), kStringReferenceCode, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+ ASSERT_TRUE(result.first);
+ uint32_t diff = kStringOffset - (result.second + kStringReferenceCode.size());
+ static const uint8_t expected_code[] = {
+ 0x8d, 0x05,
+ static_cast<uint8_t>(diff),
+ static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16),
+ static_cast<uint8_t>(diff >> 24)
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index c2f19c9..a7d574c 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1046,6 +1046,7 @@
OatDexMethodVisitor::StartClass(dex_file, class_def_index);
if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
+ DCHECK(dex_cache_ != nullptr);
}
return true;
}
@@ -1115,28 +1116,56 @@
quick_code = ArrayRef<const uint8_t>(patched_code_);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
uint32_t literal_offset = patch.LiteralOffset();
- if (patch.Type() == kLinkerPatchCallRelative) {
- // NOTE: Relative calls across oat files are not supported.
- uint32_t target_offset = GetTargetOffset(patch);
- writer_->relative_patcher_->PatchCall(&patched_code_,
- literal_offset,
- offset_ + literal_offset,
- target_offset);
- } else if (patch.Type() == kLinkerPatchDexCacheArray) {
- uint32_t target_offset = GetDexCacheOffset(patch);
- writer_->relative_patcher_->PatchDexCacheReference(&patched_code_,
- patch,
- offset_ + literal_offset,
- target_offset);
- } else if (patch.Type() == kLinkerPatchCall) {
- uint32_t target_offset = GetTargetOffset(patch);
- PatchCodeAddress(&patched_code_, literal_offset, target_offset);
- } else if (patch.Type() == kLinkerPatchMethod) {
- ArtMethod* method = GetTargetMethod(patch);
- PatchMethodAddress(&patched_code_, literal_offset, method);
- } else if (patch.Type() == kLinkerPatchType) {
- mirror::Class* type = GetTargetType(patch);
- PatchObjectAddress(&patched_code_, literal_offset, type);
+ switch (patch.Type()) {
+ case kLinkerPatchCallRelative: {
+ // NOTE: Relative calls across oat files are not supported.
+ uint32_t target_offset = GetTargetOffset(patch);
+ writer_->relative_patcher_->PatchCall(&patched_code_,
+ literal_offset,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case kLinkerPatchDexCacheArray: {
+ uint32_t target_offset = GetDexCacheOffset(patch);
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case kLinkerPatchStringRelative: {
+ uint32_t target_offset = GetTargetObjectOffset(GetTargetString(patch));
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case kLinkerPatchCall: {
+ uint32_t target_offset = GetTargetOffset(patch);
+ PatchCodeAddress(&patched_code_, literal_offset, target_offset);
+ break;
+ }
+ case kLinkerPatchMethod: {
+ ArtMethod* method = GetTargetMethod(patch);
+ PatchMethodAddress(&patched_code_, literal_offset, method);
+ break;
+ }
+ case kLinkerPatchString: {
+ mirror::String* string = GetTargetString(patch);
+ PatchObjectAddress(&patched_code_, literal_offset, string);
+ break;
+ }
+ case kLinkerPatchType: {
+ mirror::Class* type = GetTargetType(patch);
+ PatchObjectAddress(&patched_code_, literal_offset, type);
+ break;
+ }
+ default: {
+ DCHECK_EQ(patch.Type(), kLinkerPatchRecordPosition);
+ break;
+ }
}
}
}
@@ -1205,15 +1234,23 @@
return target_offset;
}
- mirror::Class* GetTargetType(const LinkerPatch& patch)
- SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Class* GetTargetType(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
- ? dex_cache_ : class_linker_->FindDexCache(Thread::Current(), *patch.TargetTypeDexFile());
+ ? dex_cache_
+ : class_linker_->FindDexCache(Thread::Current(), *patch.TargetTypeDexFile());
mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
CHECK(type != nullptr);
return type;
}
+ mirror::String* GetTargetString(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::String* string = dex_cache_->GetResolvedString(patch.TargetStringIndex());
+ DCHECK(string != nullptr);
+ DCHECK(writer_->HasBootImage() ||
+ Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string));
+ return string;
+ }
+
uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
if (writer_->HasBootImage()) {
uintptr_t element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<uintptr_t>(
@@ -1227,6 +1264,15 @@
}
}
+ uint32_t GetTargetObjectOffset(mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(writer_->HasBootImage());
+ object = writer_->image_writer_->GetImageAddress(object);
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_);
+ uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index);
+ // TODO: Clean up offset types. The target offset must be treated as signed.
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object) - oat_data_begin);
+ }
+
void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (writer_->HasBootImage()) {
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 1b62531..b6b8322 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -2745,20 +2745,16 @@
case Instruction::CONST_STRING: {
uint32_t string_index = instruction.VRegB_21c();
- bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
- *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::CONST_STRING_JUMBO: {
uint32_t string_index = instruction.VRegB_31c();
- bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
- *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index e56323f..cad5529 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -443,6 +443,11 @@
uint32_t dex_pc,
SlowPathCode* slow_path) = 0;
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ virtual HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) = 0;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -471,6 +476,18 @@
LabelType label;
};
+ // String patch info used for recording locations of required linker patches and
+ // target strings. The actual string address can be absolute or PC-relative.
+ template <typename LabelType>
+ struct StringPatchInfo {
+ StringPatchInfo(const DexFile& df, uint32_t index)
+ : dex_file(df), string_index(index), label() { }
+
+ const DexFile& dex_file;
+ uint32_t string_index;
+ LabelType label;
+ };
+
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 3a18a0d..98577d6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -779,13 +779,19 @@
move_resolver_(graph->GetArena(), this),
assembler_(),
isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
call_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- dex_cache_arrays_base_labels_(std::less<HArmDexCacheArraysBase*>(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Always save the LR register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(LR));
}
@@ -5221,12 +5227,57 @@
__ Bind(slow_path->GetExitLabel());
}
+HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5234,16 +5285,73 @@
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
+ __ BindTrackedLabel(&labels->movw_label);
+ __ movw(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->movt_label);
+ __ movt(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->add_pc_label);
+ __ add(out, out, ShifterOperand(PC));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives
+ // a 128B range. To try and reduce the number of literals if we load multiple strings,
+ // simply split the dex cache address to a 128B aligned base loaded from a literal
+ // and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(load->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2;
+ uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits);
+ uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
+ __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ GenerateGcRootFieldLoad(load, out_loc, out, offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase();
+ int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset();
+ GenerateGcRootFieldLoad(load, out_loc, base_reg, offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
@@ -6220,6 +6328,8 @@
HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
if (GetGraph()->HasIrreducibleLoops() &&
(dispatch_info.method_load_kind ==
HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
@@ -6399,13 +6509,49 @@
__ blx(LR);
}
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch(
+ const DexFile& dex_file, uint32_t string_index) {
+ return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch(
+ const DexFile& dex_file, uint32_t element_offset) {
+ return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::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* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ uint32_t string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) {
+ return DeduplicateUint32Literal(address, &uint32_literals_);
+}
+
void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
call_patches_.size() +
relative_call_patches_.size() +
- /* MOVW+MOVT for each base */ 2u * dex_cache_arrays_base_labels_.size();
+ /* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() +
+ boot_image_string_patches_.size() +
+ /* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
const MethodReference& target_method = entry.first;
@@ -6431,41 +6577,75 @@
info.target_method.dex_file,
info.target_method.dex_method_index));
}
- for (const auto& pair : dex_cache_arrays_base_labels_) {
- HArmDexCacheArraysBase* base = pair.first;
- const DexCacheArraysBaseLabels* labels = &pair.second;
- const DexFile& dex_file = base->GetDexFile();
- size_t base_element_offset = base->GetElementOffset();
- DCHECK(labels->add_pc_label.IsBound());
- uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(labels->add_pc_label.Position());
+ for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
+ const DexFile& dex_file = info.target_dex_file;
+ size_t base_element_offset = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
// Add MOVW patch.
- DCHECK(labels->movw_label.IsBound());
- uint32_t movw_offset = dchecked_integral_cast<uint32_t>(labels->movw_label.Position());
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset,
&dex_file,
add_pc_offset,
base_element_offset));
// Add MOVT patch.
- DCHECK(labels->movt_label.IsBound());
- uint32_t movt_offset = dchecked_integral_cast<uint32_t>(labels->movt_label.Position());
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset,
&dex_file,
add_pc_offset,
base_element_offset));
}
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = literal->GetLabel()->Position();
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ target_string.dex_file,
+ target_string.string_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_string_patches_) {
+ const DexFile& dex_file = info.target_dex_file;
+ uint32_t string_index = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
+ // Add MOVW patch.
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(movw_offset,
+ &dex_file,
+ add_pc_offset,
+ string_index));
+ // Add MOVT patch.
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(movt_offset,
+ &dex_file,
+ add_pc_offset,
+ string_index));
+ }
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = literal->GetLabel()->Position();
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+}
+
+Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ NewLiteral<uint32_t>(value); });
}
Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method,
MethodToLiteralMap* map) {
- // Look up the literal for target_method.
- auto lb = map->lower_bound(target_method);
- if (lb != map->end() && !map->key_comp()(target_method, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this method yet, insert a new one.
- Literal* literal = __ NewLiteral<uint32_t>(0u);
- map->PutBefore(lb, target_method, literal);
- return literal;
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
}
Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) {
@@ -6600,16 +6780,16 @@
void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
locations->SetOut(Location::RequiresRegister());
- codegen_->AddDexCacheArraysBase(base);
}
void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
Register base_reg = base->GetLocations()->Out().AsRegister<Register>();
- CodeGeneratorARM::DexCacheArraysBaseLabels* labels = codegen_->GetDexCacheArraysBaseLabels(base);
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
__ BindTrackedLabel(&labels->movw_label);
- __ movw(base_reg, 0u);
+ __ movw(base_reg, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(base_reg, 0u);
+ __ movt(base_reg, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
__ add(base_reg, base_reg, ShifterOperand(PC));
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index cc4aa14..8434128 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -23,6 +23,7 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm/assembler_thumb2.h"
+#include "utils/string_reference.h"
namespace art {
namespace arm {
@@ -403,6 +404,11 @@
Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -414,32 +420,34 @@
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
- void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
-
- // The PC-relative base address is loaded with three instructions, MOVW+MOVT
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+ // and boot image strings. The only difference is the interpretation of the offset_or_index.
+ // The PC-relative address is loaded with three instructions, MOVW+MOVT
// to load the offset to base_reg and then ADD base_reg, PC. The offset is
// calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
// currently emit these 3 instructions together, instruction scheduling could
// split this sequence apart, so we keep separate labels for each of them.
- struct DexCacheArraysBaseLabels {
- DexCacheArraysBaseLabels() = default;
- DexCacheArraysBaseLabels(DexCacheArraysBaseLabels&& other) = default;
+ 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 index.
+ uint32_t offset_or_index;
Label movw_label;
Label movt_label;
Label add_pc_label;
};
- void AddDexCacheArraysBase(HArmDexCacheArraysBase* base) {
- DexCacheArraysBaseLabels labels;
- dex_cache_arrays_base_labels_.Put(base, std::move(labels));
- }
+ PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset);
+ Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index);
+ Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
+ Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
- DexCacheArraysBaseLabels* GetDexCacheArraysBaseLabels(HArmDexCacheArraysBase* base) {
- auto it = dex_cache_arrays_base_labels_.find(base);
- DCHECK(it != dex_cache_arrays_base_labels_.end());
- return &it->second;
- }
+ void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
@@ -525,14 +533,19 @@
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
- using DexCacheArraysBaseToLabelsMap = ArenaSafeMap<HArmDexCacheArraysBase*,
- DexCacheArraysBaseLabels,
- std::less<HArmDexCacheArraysBase*>>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ Literal*,
+ StringReferenceValueComparator>;
+ Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
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);
// Labels for each block that will be compiled.
Label* block_labels_; // Indexed by block id.
@@ -543,14 +556,22 @@
Thumb2Assembler assembler_;
const ArmInstructionSetFeatures& isa_features_;
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
// Method patch info, map MethodReference to a literal for method address and method code.
MethodToLiteralMap method_patches_;
MethodToLiteralMap call_patches_;
// Relative call patch info.
// Using ArenaDeque<> which retains element addresses on push/emplace_back().
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
-
- DexCacheArraysBaseToLabelsMap dex_cache_arrays_base_labels_;
+ // PC-relative patch info for each HArmDexCacheArraysBase.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
};
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 1f577b3..491014d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -905,6 +905,8 @@
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
uint64_literals_(std::less<uint64_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_patches_(MethodReferenceComparator(),
@@ -912,7 +914,12 @@
call_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save the link register (containing the return address) to mimic Quick.
AddAllocatedRegister(LocationFrom(lr));
}
@@ -3662,23 +3669,21 @@
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
// Add ADRP with its PC-relative DexCache access patch.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
- vixl::Label* pc_insn_label = &pc_relative_dex_cache_patches_.back().label;
+ const DexFile& dex_file = *invoke->GetTargetMethod().dex_file;
+ uint32_t element_offset = invoke->GetDexCacheArrayOffset();
+ vixl::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
{
vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
- __ Bind(pc_insn_label);
- __ adrp(XRegisterFrom(temp), 0);
+ __ Bind(adrp_label);
+ __ adrp(XRegisterFrom(temp), /* offset placeholder */ 0);
}
- pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
// Add LDR with its PC-relative DexCache access patch.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
+ vixl::Label* ldr_label =
+ NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
{
vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
- __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), 0));
- pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
+ __ Bind(ldr_label);
+ __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), /* offset placeholder */ 0));
}
break;
}
@@ -3772,13 +3777,58 @@
__ Blr(lr);
}
+vixl::Label* CodeGeneratorARM64::NewPcRelativeStringPatch(const DexFile& dex_file,
+ uint32_t string_index,
+ vixl::Label* adrp_label) {
+ return NewPcRelativePatch(dex_file, string_index, adrp_label, &pc_relative_string_patches_);
+}
+
+vixl::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset,
+ vixl::Label* adrp_label) {
+ return NewPcRelativePatch(dex_file, element_offset, adrp_label, &pc_relative_dex_cache_patches_);
+}
+
+vixl::Label* CodeGeneratorARM64::NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ vixl::Label* adrp_label,
+ ArenaDeque<PcRelativePatchInfo>* patches) {
+ // Add a patch entry and return the label.
+ patches->emplace_back(dex_file, offset_or_index);
+ PcRelativePatchInfo* info = &patches->back();
+ vixl::Label* label = &info->label;
+ // If adrp_label is null, this is the ADRP patch and needs to point to its own label.
+ info->pc_insn_label = (adrp_label != nullptr) ? adrp_label : label;
+ return label;
+}
+
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageStringLiteral(
+ const DexFile& dex_file, uint32_t string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
+}
+
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddressLiteral(uint64_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddressLiteral(uint64_t address) {
+ return DeduplicateUint64Literal(address);
+}
+
void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
call_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ boot_image_string_patches_.size() +
+ pc_relative_string_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
const MethodReference& target_method = entry.first;
@@ -3799,38 +3849,51 @@
info.target_method.dex_file,
info.target_method.dex_method_index));
}
- for (const PcRelativeDexCacheAccessInfo& info : pc_relative_dex_cache_patches_) {
+ for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.location(),
&info.target_dex_file,
info.pc_insn_label->location(),
- info.element_offset));
+ info.offset_or_index));
+ }
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ vixl::Literal<uint32_t>* literal = entry.second;
+ linker_patches->push_back(LinkerPatch::StringPatch(literal->offset(),
+ target_string.dex_file,
+ target_string.string_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_string_patches_) {
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(info.label.location(),
+ &info.target_dex_file,
+ info.pc_insn_label->location(),
+ info.offset_or_index));
+ }
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ vixl::Literal<uint32_t>* literal = entry.second;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal->offset()));
}
}
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value,
+ Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(value); });
+}
+
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateUint64Literal(uint64_t value) {
- // Look up the literal for value.
- auto lb = uint64_literals_.lower_bound(value);
- if (lb != uint64_literals_.end() && !uint64_literals_.key_comp()(value, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this value, insert a new one.
- vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(value);
- uint64_literals_.PutBefore(lb, value, literal);
- return literal;
+ return uint64_literals_.GetOrCreate(
+ value,
+ [this, value]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(value); });
}
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodLiteral(
MethodReference target_method,
MethodToLiteralMap* map) {
- // Look up the literal for target_method.
- auto lb = map->lower_bound(target_method);
- if (lb != map->end() && !map->key_comp()(target_method, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this method yet, insert a new one.
- vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(0u);
- map->PutBefore(lb, target_method, literal);
- return literal;
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(/* placeholder */ 0u); });
}
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral(
@@ -3955,28 +4018,135 @@
// Nothing to do, this is driven by the code generator.
}
+HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
Location out_loc = load->GetLocations()->Out();
Register out = OutputRegister(load);
- Register current_method = InputRegisterAt(load, 0);
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ // Add ADRP with its PC-relative String patch.
+ const DexFile& dex_file = load->GetDexFile();
+ uint32_t string_index = load->GetStringIndex();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
+ }
+ // Add ADD with its PC-relative String patch.
+ vixl::Label* add_label =
+ codegen_->NewPcRelativeStringPatch(dex_file, string_index, adrp_label);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(add_label);
+ __ add(out.X(), out.X(), Operand(/* offset placeholder */ 0));
+ }
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK(load->GetAddress() != 0u && IsUint<32>(load->GetAddress()));
+ __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(load->GetAddress()));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ // LDR immediate has a 12-bit offset multiplied by the size and for 32-bit loads
+ // that gives a 16KiB range. To try and reduce the number of literals if we load
+ // multiple strings, simply split the dex cache address to a 16KiB aligned base
+ // loaded from a literal and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(load->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 12 + /* scale */ 2;
+ uint64_t base_address = load->GetAddress() & ~MaxInt<uint64_t>(offset_bits);
+ uint32_t offset = load->GetAddress() & MaxInt<uint64_t>(offset_bits);
+ __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ GenerateGcRootFieldLoad(load, out_loc, out.X(), offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ // Add ADRP with its PC-relative DexCache access patch.
+ const DexFile& dex_file = load->GetDexFile();
+ uint32_t element_offset = load->GetDexCacheElementOffset();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
+ }
+ // Add LDR with its PC-relative DexCache access patch.
+ vixl::Label* ldr_label =
+ codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
+ GenerateGcRootFieldLoad(load, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = InputRegisterAt(load, 0);
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
@@ -4791,7 +4961,8 @@
void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::Register obj,
- uint32_t offset) {
+ uint32_t offset,
+ vixl::Label* fixup_label) {
Register root_reg = RegisterFrom(root, Primitive::kPrimNot);
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
@@ -4804,7 +4975,13 @@
// }
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ Ldr(root_reg, MemOperand(obj, offset));
+ if (fixup_label == nullptr) {
+ __ Ldr(root_reg, MemOperand(obj, offset));
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ ldr(root_reg, MemOperand(obj, offset));
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -4829,14 +5006,26 @@
// GC root loaded through a slow path for read barriers other
// than Baker's.
// /* GcRoot<mirror::Object>* */ root = obj + offset
- __ Add(root_reg.X(), obj.X(), offset);
+ if (fixup_label == nullptr) {
+ __ Add(root_reg.X(), obj.X(), offset);
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ add(root_reg.X(), obj.X(), offset);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ Ldr(root_reg, MemOperand(obj, offset));
+ if (fixup_label == nullptr) {
+ __ Ldr(root_reg, MemOperand(obj, offset));
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ ldr(root_reg, MemOperand(obj, offset));
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index cf9dc1b..8ec7531 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
+#include "arch/arm64/quick_method_frame_info_arm64.h"
#include "code_generator.h"
#include "common_arm64.h"
#include "dex/compiler_enums.h"
@@ -24,9 +25,9 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm64/assembler_arm64.h"
+#include "utils/string_reference.h"
#include "vixl/a64/disasm-a64.h"
#include "vixl/a64/macro-assembler-a64.h"
-#include "arch/arm64/quick_method_frame_info_arm64.h"
namespace art {
namespace arm64 {
@@ -255,7 +256,8 @@
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::Register obj,
- uint32_t offset);
+ uint32_t offset,
+ vixl::Label* fixup_label = nullptr);
// Generate a floating-point comparison.
void GenerateFcmp(HInstruction* instruction);
@@ -453,6 +455,11 @@
return false;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -467,6 +474,27 @@
UNIMPLEMENTED(FATAL);
}
+ // Add a new PC-relative string patch for an instruction and return the label
+ // to be bound before the instruction. The instruction will be either the
+ // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
+ // to the associated ADRP patch label).
+ vixl::Label* NewPcRelativeStringPatch(const DexFile& dex_file,
+ uint32_t string_index,
+ vixl::Label* adrp_label = nullptr);
+
+ // Add a new PC-relative dex cache array patch for an instruction and return
+ // the label to be bound before the instruction. The instruction will be
+ // either the ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label`
+ // pointing to the associated ADRP patch label).
+ vixl::Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset,
+ vixl::Label* adrp_label = nullptr);
+
+ vixl::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ uint32_t string_index);
+ vixl::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address);
+ vixl::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address);
+
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
// Fast path implementation of ReadBarrier::Barrier for a heap
@@ -554,26 +582,39 @@
bool use_load_acquire);
using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>;
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference,
vixl::Literal<uint64_t>*,
MethodReferenceComparator>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ vixl::Literal<uint32_t>*,
+ StringReferenceValueComparator>;
+ vixl::Literal<uint32_t>* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
vixl::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value);
vixl::Literal<uint64_t>* DeduplicateMethodLiteral(MethodReference target_method,
MethodToLiteralMap* map);
vixl::Literal<uint64_t>* DeduplicateMethodAddressLiteral(MethodReference target_method);
vixl::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method);
- struct PcRelativeDexCacheAccessInfo {
- PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
- : target_dex_file(dex_file), element_offset(element_off), label(), pc_insn_label() { }
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+ // and boot image strings. 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), label(), pc_insn_label() { }
const DexFile& target_dex_file;
- uint32_t element_offset;
+ // Either the dex cache array element offset or the string index.
+ uint32_t offset_or_index;
vixl::Label label;
vixl::Label* pc_insn_label;
};
+ vixl::Label* NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ vixl::Label* adrp_label,
+ ArenaDeque<PcRelativePatchInfo>* patches);
+
void EmitJumpTables();
// Labels for each block that will be compiled.
@@ -587,7 +628,10 @@
Arm64Assembler assembler_;
const Arm64InstructionSetFeatures& isa_features_;
- // Deduplication map for 64-bit literals, used for non-patchable method address and method code.
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
+ // Deduplication map for 64-bit literals, used for non-patchable method address, method code
+ // or string dex cache address.
Uint64ToLiteralMap uint64_literals_;
// Method patch info, map MethodReference to a literal for method address and method code.
MethodToLiteralMap method_patches_;
@@ -596,7 +640,13 @@
// Using ArenaDeque<> which retains element addresses on push/emplace_back().
ArenaDeque<MethodPatchInfo<vixl::Label>> relative_call_patches_;
// PC-relative DexCache access info.
- ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
};
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index a29d839..8b19f84 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -3816,6 +3816,12 @@
return false;
}
+HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
+ // TODO: Implement other kinds.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
@@ -4066,9 +4072,9 @@
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->IsInDexCache()
- ? LocationSummary::kNoCall
- : LocationSummary::kCallOnSlowPath;
+ LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index b720573..afe7917 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -345,6 +345,11 @@
return type == Primitive::kPrimLong;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 72ef499..2f9eca6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3030,6 +3030,12 @@
return false;
}
+HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
+ // TODO: Implement other kinds.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
@@ -3284,9 +3290,9 @@
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->IsInDexCache()
- ? LocationSummary::kNoCall
- : LocationSummary::kCallOnSlowPath;
+ LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 9464a14..94767cb 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -337,6 +337,11 @@
bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const { return false; }
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 394f4ee..715b5be 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -799,6 +799,8 @@
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Use a fake return address register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
@@ -4340,6 +4342,8 @@
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
+ // TODO: Create as many X86ComputeBaseMethodAddress instructions
+ // as needed for methods with irreducible loops.
if (GetGraph()->HasIrreducibleLoops() &&
(dispatch_info.method_load_kind ==
HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
@@ -4401,18 +4405,17 @@
__ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress()));
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ movl(temp.AsRegister<Register>(), Immediate(0)); // Placeholder.
+ __ movl(temp.AsRegister<Register>(), Immediate(/* placeholder */ 0));
method_patches_.emplace_back(invoke->GetTargetMethod());
__ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
temp.AsRegister<Register>());
- uint32_t offset = invoke->GetDexCacheArrayOffset();
__ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
- // Add the patch entry and bind its label at the end of the instruction.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, offset);
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
+ // Bind a new fixup label at the end of the "movl" insn.
+ uint32_t offset = invoke->GetDexCacheArrayOffset();
+ __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
break;
}
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -4494,12 +4497,33 @@
temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
}
+void CodeGeneratorX86::RecordSimplePatch() {
+ if (GetCompilerOptions().GetIncludePatchInformation()) {
+ simple_patches_.emplace_back();
+ __ Bind(&simple_patches_.back());
+ }
+}
+
+void CodeGeneratorX86::RecordStringPatch(HLoadString* load_string) {
+ string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex());
+ __ Bind(&string_patches_.back().label);
+}
+
+Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset) {
+ // Add the patch entry and bind its label at the end of the instruction.
+ pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset);
+ return &pc_relative_dex_cache_patches_.back().label;
+}
+
void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ simple_patches_.size() +
+ string_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -4523,6 +4547,26 @@
GetMethodAddressOffset(),
info.element_offset));
}
+ for (const Label& label : simple_patches_) {
+ uint32_t literal_offset = label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+ if (GetCompilerOptions().GetCompilePic()) {
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(literal_offset,
+ &info.dex_file,
+ GetMethodAddressOffset(),
+ info.string_index));
+ }
+ } else {
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ &info.dex_file,
+ info.string_index));
+ }
+ }
}
void CodeGeneratorX86::MarkGCCard(Register temp,
@@ -5916,14 +5960,15 @@
DCHECK(!cls->MustGenerateClinitCheck());
// /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
__ movl(out, Address(current_method,
ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
// /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5972,12 +6017,58 @@
// No need for memory fence, thanks to the X86 memory model.
}
+HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ FALLTHROUGH_INTENDED;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit()); // Note: boot image is also non-JIT.
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5985,16 +6076,61 @@
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ movl(out, Immediate(/* placeholder */ 0));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ Register method_address = locations->InAt(0).AsRegister<Register>();
+ __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ movl(out, Immediate(address));
+ codegen_->RecordSimplePatch();
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ GenerateGcRootFieldLoad(load, out_loc, Address::Absolute(address));
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ uint32_t offset = load->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
@@ -6692,21 +6828,24 @@
void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- Register obj,
- uint32_t offset) {
+ const Address& address,
+ Label* fixup_label) {
Register root_reg = root.AsRegister<Register>();
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = obj.field;
+ // root = *address;
// if (Thread::Current()->GetIsGcMarking()) {
// root = ReadBarrier::Mark(root)
// }
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -6727,15 +6866,21 @@
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
- // /* GcRoot<mirror::Object>* */ root = obj + offset
- __ leal(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object>* */ root = address
+ __ leal(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index c397899..1fa22fc 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -258,13 +258,13 @@
Location maybe_temp);
// Generate a GC root reference load:
//
- // root <- *(obj + offset)
+ // root <- *address
//
// while honoring read barriers (if any).
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- Register obj,
- uint32_t offset);
+ const Address& address,
+ Label* fixup_label = nullptr);
// Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
// `is_wide` specifies whether it is long/double or not.
@@ -388,6 +388,11 @@
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -399,6 +404,10 @@
// Generate a call to a virtual method.
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+ void RecordSimplePatch();
+ void RecordStringPatch(HLoadString* load_string);
+ Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
// Emit linker patches.
@@ -542,6 +551,10 @@
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+ // The correct value will be inserted when processing Assembler fixups.
+ static constexpr int32_t kDummy32BitOffset = 256;
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
@@ -578,6 +591,10 @@
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
+ // Patch locations for patchoat where the linker doesn't do any other work.
+ ArenaDeque<Label> simple_patches_;
+ // String patch locations.
+ ArenaDeque<StringPatchInfo<Label>> string_patches_;
// Offset to the start of the constant area in the assembled code.
// Used for fixups to the constant area.
@@ -592,10 +609,6 @@
// instruction gives the address of the start of this method.
int32_t method_address_offset_;
- // When we don't know the proper offset for the value, we use kDummy32BitOffset.
- // The correct value will be inserted when processing Assembler fixups.
- static constexpr int32_t kDummy32BitOffset = 256;
-
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
};
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index d24b5bb..504eaa8 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -784,14 +784,14 @@
method_patches_.emplace_back(invoke->GetTargetMethod());
__ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
__ movq(temp.AsRegister<CpuRegister>(),
Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
- // Bind the label at the end of the "movl" insn.
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
+ // Bind a new fixup label at the end of the "movl" insn.
+ uint32_t offset = invoke->GetDexCacheArrayOffset();
+ __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
break;
+ }
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
Register method_reg;
@@ -873,12 +873,33 @@
kX86_64WordSize).SizeValue()));
}
+void CodeGeneratorX86_64::RecordSimplePatch() {
+ if (GetCompilerOptions().GetIncludePatchInformation()) {
+ simple_patches_.emplace_back();
+ __ Bind(&simple_patches_.back());
+ }
+}
+
+void CodeGeneratorX86_64::RecordStringPatch(HLoadString* load_string) {
+ string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex());
+ __ Bind(&string_patches_.back().label);
+}
+
+Label* CodeGeneratorX86_64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset) {
+ // Add a patch entry and return the label.
+ pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset);
+ return &pc_relative_dex_cache_patches_.back().label;
+}
+
void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ simple_patches_.size() +
+ string_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -902,6 +923,18 @@
info.label.Position(),
info.element_offset));
}
+ for (const Label& label : simple_patches_) {
+ uint32_t literal_offset = label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ // These are always PC-relative, see GetSupportedLoadStringKind().
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(literal_offset,
+ &info.dex_file,
+ info.label.Position(),
+ info.string_index));
+ }
}
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -978,6 +1011,8 @@
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -5365,14 +5400,15 @@
DCHECK(!cls->MustGenerateClinitCheck());
// /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
__ movq(out, Address(current_method,
ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
// /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5410,12 +5446,49 @@
check->GetLocations()->InAt(0).AsRegister<CpuRegister>());
}
+HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ // We prefer the always-available RIP-relative address for the x86-64 boot image.
+ return HLoadString::LoadKind::kBootImageLinkTimePcRelative;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5423,16 +5496,59 @@
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
- CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ movl(out, Immediate(address)); // Zero-extended.
+ codegen_->RecordSimplePatch();
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ if (IsUint<32>(load->GetAddress())) {
+ Address address = Address::Absolute(load->GetAddress(), /* no_rip */ true);
+ GenerateGcRootFieldLoad(load, out_loc, address);
+ } else {
+ // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
+ __ movq(out, Immediate(load->GetAddress()));
+ GenerateGcRootFieldLoad(load, out_loc, Address(out, 0));
+ }
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ uint32_t offset = load->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
+ Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
+ /* no_rip */ false);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
@@ -6171,21 +6287,24 @@
void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- CpuRegister obj,
- uint32_t offset) {
+ const Address& address,
+ Label* fixup_label) {
CpuRegister root_reg = root.AsRegister<CpuRegister>();
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = obj.field;
+ // root = *address;
// if (Thread::Current()->GetIsGcMarking()) {
// root = ReadBarrier::Mark(root)
// }
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -6207,15 +6326,21 @@
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
- // /* GcRoot<mirror::Object>* */ root = obj + offset
- __ leaq(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object>* */ root = address
+ __ leaq(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index c3fce6e..fdcf3c3 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -252,13 +252,13 @@
Location maybe_temp);
// Generate a GC root reference load:
//
- // root <- *(obj + offset)
+ // root <- *address
//
// while honoring read barriers (if any).
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- CpuRegister obj,
- uint32_t offset);
+ const Address& address,
+ Label* fixup_label = nullptr);
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_float);
@@ -384,6 +384,11 @@
return false;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -393,6 +398,10 @@
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+ void RecordSimplePatch();
+ void RecordStringPatch(HLoadString* load_string);
+ Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
@@ -515,6 +524,10 @@
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+ // We will fix this up in the linker later to have the right value.
+ static constexpr int32_t kDummy32BitOffset = 256;
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
@@ -552,10 +565,10 @@
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
-
- // When we don't know the proper offset for the value, we use kDummy32BitOffset.
- // We will fix this up in the linker later to have the right value.
- static constexpr int32_t kDummy32BitOffset = 256;
+ // Patch locations for patchoat where the linker doesn't do any other work.
+ ArenaDeque<Label> simple_patches_;
+ // String patch locations.
+ ArenaDeque<StringPatchInfo<Label>> string_patches_;
// Fixups for jump tables need to be handled specially.
ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 3db254a..e9072b9 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -44,6 +44,21 @@
}
private:
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ // If this is a load with PC-relative access to the dex cache methods array,
+ // we need to add the dex cache arrays base as the special input.
+ if (load_string->GetLoadKind() == HLoadString::LoadKind::kDexCachePcRelative) {
+ // Initialize base for target dex file if needed.
+ const DexFile& dex_file = load_string->GetDexFile();
+ HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file);
+ // Update the element offset in base.
+ DexCacheArraysLayout layout(kArmPointerSize, &dex_file);
+ base->UpdateElementOffset(layout.StringOffset(load_string->GetStringIndex()));
+ // Add the special argument base to the load.
+ load_string->AddSpecialInput(base);
+ }
+ }
+
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
// If this is an invoke with PC-relative access to the dex cache methods array,
// we need to add the dex cache arrays base as the special input.
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4b5b919..bcdd3e7 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -367,6 +367,10 @@
<< load_class->NeedsAccessCheck() << std::noboolalpha;
}
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ StartAttributeStream("load_kind") << load_string->GetLoadKind();
+ }
+
void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
StartAttributeStream("must_do_null_check") << std::boolalpha
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1a426d5..9504481 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -86,11 +86,7 @@
}
}
-static void RemoveAsUser(HInstruction* instruction) {
- for (size_t i = 0; i < instruction->InputCount(); i++) {
- instruction->RemoveAsUserOfInput(i);
- }
-
+static void RemoveEnvironmentUses(HInstruction* instruction) {
for (HEnvironment* environment = instruction->GetEnvironment();
environment != nullptr;
environment = environment->GetParent()) {
@@ -102,6 +98,14 @@
}
}
+static void RemoveAsUser(HInstruction* instruction) {
+ for (size_t i = 0; i < instruction->InputCount(); i++) {
+ instruction->RemoveAsUserOfInput(i);
+ }
+
+ RemoveEnvironmentUses(instruction);
+}
+
void HGraph::RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
@@ -1007,6 +1011,11 @@
}
}
+void HInstruction::RemoveEnvironment() {
+ RemoveEnvironmentUses(this);
+ environment_ = nullptr;
+}
+
void HInstruction::ReplaceWith(HInstruction* other) {
DCHECK(other != nullptr);
for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
@@ -2387,6 +2396,59 @@
}
}
+bool HLoadString::InstructionDataEquals(HInstruction* other) const {
+ HLoadString* other_load_string = other->AsLoadString();
+ if (string_index_ != other_load_string->string_index_ ||
+ GetPackedFields() != other_load_string->GetPackedFields()) {
+ return false;
+ }
+ LoadKind load_kind = GetLoadKind();
+ if (HasAddress(load_kind)) {
+ return GetAddress() == other_load_string->GetAddress();
+ } else if (HasStringReference(load_kind)) {
+ return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
+ } else {
+ DCHECK(HasDexCacheReference(load_kind)) << load_kind;
+ // If the string indexes and dex files are the same, dex cache element offsets
+ // must also be the same, so we don't need to compare them.
+ return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
+ }
+}
+
+void HLoadString::SetLoadKindInternal(LoadKind load_kind) {
+ // Once sharpened, the load kind should not be changed again.
+ DCHECK_EQ(GetLoadKind(), LoadKind::kDexCacheViaMethod);
+ SetPackedField<LoadKindField>(load_kind);
+
+ if (load_kind != LoadKind::kDexCacheViaMethod) {
+ RemoveAsUserOfInput(0u);
+ SetRawInputAt(0u, nullptr);
+ }
+ if (!NeedsEnvironment()) {
+ RemoveEnvironment();
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) {
+ switch (rhs) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ return os << "BootImageLinkTimeAddress";
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ return os << "BootImageLinkTimePcRelative";
+ case HLoadString::LoadKind::kBootImageAddress:
+ return os << "BootImageAddress";
+ case HLoadString::LoadKind::kDexCacheAddress:
+ return os << "DexCacheAddress";
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ return os << "DexCachePcRelative";
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ return os << "DexCacheViaMethod";
+ default:
+ LOG(FATAL) << "Unknown HLoadString::LoadKind: " << static_cast<int>(rhs);
+ UNREACHABLE();
+ }
+}
+
void HInstruction::RemoveEnvironmentUsers() {
for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
HUseListNode<HEnvironment*>* user_node = use_it.Current();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index e9a42cb..ba42421 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1995,6 +1995,8 @@
environment_ = environment;
}
+ void RemoveEnvironment();
+
// Set the environment of this instruction, copying it from `environment`. While
// copying, the uses lists are being updated.
void CopyEnvironmentFrom(HEnvironment* environment) {
@@ -5557,32 +5559,117 @@
class HLoadString : public HExpression<1> {
public:
+ // Determines how to load the String.
+ enum class LoadKind {
+ // Use boot image String* address that will be known at link time.
+ // Used for boot image strings referenced by boot image code in non-PIC mode.
+ kBootImageLinkTimeAddress,
+
+ // Use PC-relative boot image String* address that will be known at link time.
+ // Used for boot image strings referenced by boot image code in PIC mode.
+ kBootImageLinkTimePcRelative,
+
+ // Use a known boot image String* address, embedded in the code by the codegen.
+ // Used for boot image strings referenced by apps in AOT- and JIT-compiled code.
+ // Note: codegen needs to emit a linker patch if indicated by compiler options'
+ // GetIncludePatchInformation().
+ kBootImageAddress,
+
+ // Load from the resolved strings array at an absolute address.
+ // Used for strings outside the boot image referenced by JIT-compiled code.
+ kDexCacheAddress,
+
+ // Load from resolved strings array in the dex cache using a PC-relative load.
+ // Used for strings outside boot image when we know that we can access
+ // the dex cache arrays using a PC-relative load.
+ kDexCachePcRelative,
+
+ // Load from resolved strings array accessed through the class loaded from
+ // the compiled method's own ArtMethod*. This is the default access type when
+ // all other types are unavailable.
+ kDexCacheViaMethod,
+
+ kLast = kDexCacheViaMethod
+ };
+
HLoadString(HCurrentMethod* current_method,
uint32_t string_index,
- uint32_t dex_pc,
- bool is_in_dex_cache)
+ const DexFile& dex_file,
+ uint32_t dex_pc)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
string_index_(string_index) {
- SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+ SetPackedFlag<kFlagIsInDexCache>(false);
+ SetPackedField<LoadKindField>(LoadKind::kDexCacheViaMethod);
+ load_data_.ref.dex_file = &dex_file;
SetRawInputAt(0, current_method);
}
+ void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
+ DCHECK(HasAddress(load_kind));
+ load_data_.address = address;
+ SetLoadKindInternal(load_kind);
+ }
+
+ void SetLoadKindWithStringReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t string_index) {
+ DCHECK(HasStringReference(load_kind));
+ load_data_.ref.dex_file = &dex_file;
+ string_index_ = string_index;
+ SetLoadKindInternal(load_kind);
+ }
+
+ void SetLoadKindWithDexCacheReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t element_index) {
+ DCHECK(HasDexCacheReference(load_kind));
+ load_data_.ref.dex_file = &dex_file;
+ load_data_.ref.dex_cache_element_index = element_index;
+ SetLoadKindInternal(load_kind);
+ }
+
+ LoadKind GetLoadKind() const {
+ return GetPackedField<LoadKindField>();
+ }
+
+ const DexFile& GetDexFile() const;
+
+ uint32_t GetStringIndex() const {
+ DCHECK(HasStringReference(GetLoadKind()) || /* For slow paths. */ !IsInDexCache());
+ return string_index_;
+ }
+
+ uint32_t GetDexCacheElementOffset() const;
+
+ uint64_t GetAddress() const {
+ DCHECK(HasAddress(GetLoadKind()));
+ return load_data_.address;
+ }
+
bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return other->AsLoadString()->string_index_ == string_index_;
- }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE;
size_t ComputeHashCode() const OVERRIDE { return string_index_; }
- uint32_t GetStringIndex() const { return string_index_; }
+ // Will call the runtime if we need to load the string through
+ // the dex cache and the string is not guaranteed to be there yet.
+ bool NeedsEnvironment() const OVERRIDE {
+ LoadKind load_kind = GetLoadKind();
+ if (load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kBootImageAddress) {
+ return false;
+ }
+ return !IsInDexCache();
+ }
- // Will call the runtime if the string is not already in the dex cache.
- bool NeedsEnvironment() const OVERRIDE { return !IsInDexCache(); }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE {
+ return GetLoadKind() == LoadKind::kDexCacheViaMethod;
+ }
- bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
- bool CanThrow() const OVERRIDE { return !IsInDexCache(); }
+ bool CanThrow() const OVERRIDE { return NeedsEnvironment(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -5590,17 +5677,83 @@
bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+ void MarkInDexCache() {
+ SetPackedFlag<kFlagIsInDexCache>(true);
+ DCHECK(!NeedsEnvironment());
+ RemoveEnvironment();
+ }
+
+ size_t InputCount() const OVERRIDE {
+ return (InputAt(0) != nullptr) ? 1u : 0u;
+ }
+
+ void AddSpecialInput(HInstruction* special_input);
+
DECLARE_INSTRUCTION(LoadString);
private:
static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits;
- static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1;
+ static constexpr size_t kFieldLoadKind = kFlagIsInDexCache + 1;
+ static constexpr size_t kFieldLoadKindSize =
+ MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
+ static constexpr size_t kNumberOfLoadStringPackedBits = kFieldLoadKind + kFieldLoadKindSize;
static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
- const uint32_t string_index_;
+ static bool HasStringReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kDexCacheViaMethod;
+ }
+
+ static bool HasAddress(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress;
+ }
+
+ static bool HasDexCacheReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kDexCachePcRelative;
+ }
+
+ void SetLoadKindInternal(LoadKind load_kind);
+
+ // String index serves also as the hash code and it's also needed for slow-paths,
+ // so it must not be overwritten with other load data.
+ uint32_t string_index_;
+
+ union {
+ struct {
+ const DexFile* dex_file; // For string reference and dex cache reference.
+ uint32_t dex_cache_element_index; // Only for dex cache reference.
+ } ref;
+ uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets.
+ } load_data_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
+std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs);
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline const DexFile& HLoadString::GetDexFile() const {
+ DCHECK(HasStringReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind()))
+ << GetLoadKind();
+ return *load_data_.ref.dex_file;
+}
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline uint32_t HLoadString::GetDexCacheElementOffset() const {
+ DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind();
+ return load_data_.ref.dex_cache_element_index;
+}
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline void HLoadString::AddSpecialInput(HInstruction* special_input) {
+ // The special input is used for PC-relative loads on some architectures.
+ DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+ GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind();
+ DCHECK(InputAt(0) == nullptr);
+ SetRawInputAt(0u, special_input);
+ special_input->AddUseAt(this, 0);
+}
/**
* Performs an initialization check on its Class object input.
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index d281a9f..dafbd3d 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -80,6 +80,15 @@
HandleInvoke(invoke);
}
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ HLoadString::LoadKind load_kind = load_string->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ InitializePCRelativeBasePointer();
+ load_string->AddSpecialInput(base_);
+ }
+ }
+
void BinaryFP(HBinaryOperation* bin) {
HConstant* rhs = bin->InputAt(1)->AsConstant();
if (rhs != nullptr && Primitive::IsFloatingPointType(rhs->GetType())) {
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 5e1d1d9..45ae336 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -16,11 +16,19 @@
#include "sharpening.h"
+#include "class_linker.h"
#include "code_generator.h"
+#include "driver/dex_compilation_unit.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "driver/compiler_driver.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "handle_scope-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/string.h"
#include "nodes.h"
#include "runtime.h"
+#include "scoped_thread_state_change.h"
namespace art {
@@ -31,12 +39,13 @@
HInstruction* instruction = it.Current();
if (instruction->IsInvokeStaticOrDirect()) {
ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect());
+ } else if (instruction->IsLoadString()) {
+ ProcessLoadString(instruction->AsLoadString());
}
// TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder
// here. Rewrite it to avoid the CompilerDriver's reliance on verifier data
// because we know the type better when inlining.
- // TODO: HLoadClass, HLoadString - select PC relative dex cache array access if
- // available.
+ // TODO: HLoadClass - select better load kind if available.
}
}
}
@@ -143,4 +152,105 @@
invoke->SetDispatchInfo(dispatch_info);
}
+void HSharpening::ProcessLoadString(HLoadString* load_string) {
+ DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
+ DCHECK(!load_string->IsInDexCache());
+
+ const DexFile& dex_file = load_string->GetDexFile();
+ uint32_t string_index = load_string->GetStringIndex();
+
+ bool is_in_dex_cache = false;
+ HLoadString::LoadKind desired_load_kind;
+ uint64_t address = 0u; // String or dex cache element address.
+ {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+ ? compilation_unit_.GetDexCache()
+ : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+
+ if (compiler_driver_->IsBootImage()) {
+ // Compiling boot image. Resolve the string and allocate it if needed.
+ DCHECK(!runtime->UseJit());
+ mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
+ CHECK(string != nullptr);
+ if (!compiler_driver_->GetSupportBootImageFixup()) {
+ // MIPS/MIPS64 or compiler_driver_test. Do not sharpen.
+ desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
+ } else {
+ DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(),
+ &load_string->GetDexFile()));
+ is_in_dex_cache = true;
+ desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+ ? HLoadString::LoadKind::kBootImageLinkTimePcRelative
+ : HLoadString::LoadKind::kBootImageLinkTimeAddress;
+ }
+ } else {
+ // Not compiling boot image. Try to lookup the string without allocating if not found.
+ mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
+ if (runtime->UseJit()) {
+ // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+ // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+ is_in_dex_cache = (string != nullptr);
+ if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+ desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+ // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed."
+ address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(string));
+ } else {
+ // Note: If the string is not in the dex cache, the instruction needs environment
+ // and will not be inlined across dex files. Within a dex file, the slow-path helper
+ // loads the correct string and inlined frames are used correctly for OOM stack trace.
+ // TODO: Write a test for this.
+ desired_load_kind = HLoadString::LoadKind::kDexCacheAddress;
+ void* dex_cache_element_address = &dex_cache->GetStrings()[string_index];
+ // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed."
+ address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(dex_cache_element_address));
+ }
+ } else if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+ if (codegen_->GetCompilerOptions().GetCompilePic()) {
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind =
+ ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_string->GetDexFile())
+ ? HLoadString::LoadKind::kDexCachePcRelative
+ : HLoadString::LoadKind::kDexCacheViaMethod;
+ } else {
+ desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+ // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed."
+ address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(string));
+ }
+ } else {
+ // Not JIT and the string is not in boot image.
+ desired_load_kind = HLoadString::LoadKind::kDexCachePcRelative;
+ }
+ }
+ }
+ if (is_in_dex_cache) {
+ load_string->MarkInDexCache();
+ }
+
+ HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind);
+ switch (load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index);
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK_NE(address, 0u);
+ load_string->SetLoadKindWithAddress(load_kind, address);
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ DexCacheArraysLayout layout(pointer_size, &dex_file);
+ size_t element_index = layout.StringOffset(string_index);
+ load_string->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
+ break;
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index adae700..24152f6 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,7 @@
private:
void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
+ void ProcessLoadString(HLoadString* load_string);
CodeGenerator* codegen_;
const DexCompilationUnit& compilation_unit_;
diff --git a/compiler/utils/string_reference.h b/compiler/utils/string_reference.h
new file mode 100644
index 0000000..72552f2
--- /dev/null
+++ b/compiler/utils/string_reference.h
@@ -0,0 +1,64 @@
+/*
+ * 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_UTILS_STRING_REFERENCE_H_
+#define ART_COMPILER_UTILS_STRING_REFERENCE_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "utf-inl.h"
+
+namespace art {
+
+class DexFile;
+
+// A string is uniquely located by its DexFile and the string_ids_ table index into that DexFile.
+struct StringReference {
+ StringReference(const DexFile* file, uint32_t index) : dex_file(file), string_index(index) { }
+
+ const DexFile* dex_file;
+ uint32_t string_index;
+};
+
+// Compare the actual referenced string values. Used for string reference deduplication.
+struct StringReferenceValueComparator {
+ bool operator()(StringReference sr1, StringReference sr2) const {
+ // Note that we want to deduplicate identical strings even if they are referenced
+ // by different dex files, so we need some (any) total ordering of strings, rather
+ // than references. However, the references should usually be from the same dex file,
+ // so we choose the dex file string ordering so that we can simply compare indexes
+ // and avoid the costly string comparison in the most common case.
+ if (sr1.dex_file == sr2.dex_file) {
+ // Use the string order enforced by the dex file verifier.
+ DCHECK_EQ(
+ sr1.string_index < sr2.string_index,
+ CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
+ sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
+ sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0);
+ return sr1.string_index < sr2.string_index;
+ } else {
+ // Cannot compare indexes, so do the string comparison.
+ return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
+ sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
+ sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0;
+ }
+ }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_STRING_REFERENCE_H_