Home | History | Annotate | Download | only in arm
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "linker/arm/relative_patcher_thumb2.h"
     18 
     19 #include "base/casts.h"
     20 #include "linker/relative_patcher_test.h"
     21 #include "lock_word.h"
     22 #include "mirror/array-inl.h"
     23 #include "mirror/object.h"
     24 #include "oat_quick_method_header.h"
     25 
     26 namespace art {
     27 namespace linker {
     28 
     29 class Thumb2RelativePatcherTest : public RelativePatcherTest {
     30  public:
     31   Thumb2RelativePatcherTest() : RelativePatcherTest(InstructionSet::kThumb2, "default") { }
     32 
     33  protected:
     34   static const uint8_t kCallRawCode[];
     35   static const ArrayRef<const uint8_t> kCallCode;
     36   static const uint8_t kNopRawCode[];
     37   static const ArrayRef<const uint8_t> kNopCode;
     38   static const uint8_t kUnpatchedPcRelativeRawCode[];
     39   static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
     40   static const uint32_t kPcInsnOffset;
     41 
     42   // The PC in Thumb mode is 4 bytes after the instruction location.
     43   static constexpr uint32_t kPcAdjustment = 4u;
     44 
     45   // Branches within range [-256, 256) can be created from these by adding the low 8 bits.
     46   static constexpr uint32_t kBlPlus0 = 0xf000f800u;
     47   static constexpr uint32_t kBlMinus256 = 0xf7ffff00u;
     48 
     49   // Special BL values.
     50   static constexpr uint32_t kBlPlusMax = 0xf3ffd7ffu;
     51   static constexpr uint32_t kBlMinusMax = 0xf400d000u;
     52 
     53   // BNE +0, 32-bit, encoding T3. Bits 0-10, 11, 13, 16-21, 26 are placeholder for target offset.
     54   static constexpr uint32_t kBneWPlus0 = 0xf0408000u;
     55 
     56   // LDR immediate, 16-bit, encoding T1. Bits 6-10 are imm5, 0-2 are Rt, 3-5 are Rn.
     57   static constexpr uint32_t kLdrInsn = 0x6800u;
     58 
     59   // LDR immediate, 32-bit, encoding T3. Bits 0-11 are offset, 12-15 are Rt, 16-20 are Rn.
     60   static constexpr uint32_t kLdrWInsn = 0xf8d00000u;
     61 
     62   // LDR immediate, negative offset, encoding T4. Bits 0-7 are the offset to subtract.
     63   static constexpr uint32_t kLdrNegativeOffset = 0xf8500c00u;
     64 
     65   // LDR register, lsl #2. Bits 4-5 are the imm2, i.e. the lsl shift.
     66   static constexpr uint32_t kLdrRegLsl2 = 0xf8500020u;
     67 
     68   // NOP instructions.
     69   static constexpr uint32_t kNopInsn = 0xbf00u;
     70   static constexpr uint32_t kNopWInsn = 0xf3af8000u;
     71 
     72   void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
     73     CHECK_LE(pos, code->size());
     74     if (IsUint<16>(insn)) {
     75       const uint8_t insn_code[] = {
     76           static_cast<uint8_t>(insn),
     77           static_cast<uint8_t>(insn >> 8),
     78       };
     79       static_assert(sizeof(insn_code) == 2u, "Invalid sizeof(insn_code).");
     80       code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
     81     } else {
     82       const uint8_t insn_code[] = {
     83           static_cast<uint8_t>(insn >> 16),
     84           static_cast<uint8_t>(insn >> 24),
     85           static_cast<uint8_t>(insn),
     86           static_cast<uint8_t>(insn >> 8),
     87       };
     88       static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
     89       code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
     90     }
     91   }
     92 
     93   void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
     94     InsertInsn(code, code->size(), insn);
     95   }
     96 
     97   std::vector<uint8_t> GenNops(size_t num_nops) {
     98     std::vector<uint8_t> result;
     99     result.reserve(num_nops * 2u);
    100     for (size_t i = 0; i != num_nops; ++i) {
    101       PushBackInsn(&result, kNopInsn);
    102     }
    103     return result;
    104   }
    105 
    106   std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
    107     std::vector<uint8_t> raw_code;
    108     size_t number_of_16_bit_insns =
    109         std::count_if(insns.begin(), insns.end(), [](uint32_t x) { return IsUint<16>(x); });
    110     raw_code.reserve(insns.size() * 4u - number_of_16_bit_insns * 2u);
    111     for (uint32_t insn : insns) {
    112       PushBackInsn(&raw_code, insn);
    113     }
    114     return raw_code;
    115   }
    116 
    117   uint32_t BneWWithOffset(uint32_t bne_offset, uint32_t target_offset) {
    118     if (!IsAligned<2u>(bne_offset)) {
    119       LOG(ERROR) << "Unaligned bne_offset: " << bne_offset;
    120       return 0xffffffffu;  // Fails code diff later.
    121     }
    122     if (!IsAligned<2u>(target_offset)) {
    123       LOG(ERROR) << "Unaligned target_offset: " << target_offset;
    124       return 0xffffffffu;  // Fails code diff later.
    125     }
    126     uint32_t diff = target_offset - bne_offset - kPcAdjustment;
    127     DCHECK_ALIGNED(diff, 2u);
    128     if ((diff >> 20) != 0 && (diff >> 20) != 0xfffu) {
    129       LOG(ERROR) << "Target out of range: " << diff;
    130       return 0xffffffffu;  // Fails code diff later.
    131     }
    132     return kBneWPlus0 | ((diff >> 1) & 0x7ffu)          // imm11
    133                       | (((diff >> 12) & 0x3fu) << 16)  // imm6
    134                       | (((diff >> 18) & 1) << 13)      // J1
    135                       | (((diff >> 19) & 1) << 11)      // J2
    136                       | (((diff >> 20) & 1) << 26);     // S
    137   }
    138 
    139   bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
    140                              const ArrayRef<const LinkerPatch>& method1_patches,
    141                              const ArrayRef<const uint8_t>& method3_code,
    142                              const ArrayRef<const LinkerPatch>& method3_patches,
    143                              uint32_t distance_without_thunks) {
    144     CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
    145     uint32_t method1_offset =
    146         kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
    147     AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
    148 
    149     // We want to put the method3 at a very precise offset.
    150     const uint32_t method3_offset = method1_offset + distance_without_thunks;
    151     CHECK_ALIGNED(method3_offset, kArmAlignment);
    152 
    153     // Calculate size of method2 so that we put method3 at the correct place.
    154     const uint32_t method1_end = method1_offset + method1_code.size();
    155     const uint32_t method2_offset =
    156         method1_end + CodeAlignmentSize(method1_end) + sizeof(OatQuickMethodHeader);
    157     const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
    158     std::vector<uint8_t> method2_raw_code(method2_size);
    159     ArrayRef<const uint8_t> method2_code(method2_raw_code);
    160     AddCompiledMethod(MethodRef(2u), method2_code);
    161 
    162     AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
    163 
    164     Link();
    165 
    166     // Check assumptions.
    167     CHECK_EQ(GetMethodOffset(1), method1_offset);
    168     CHECK_EQ(GetMethodOffset(2), method2_offset);
    169     auto result3 = method_offset_map_.FindMethodOffset(MethodRef(3));
    170     CHECK(result3.first);
    171     // There may be a thunk before method2.
    172     if (result3.second == method3_offset + 1 /* thumb mode */) {
    173       return false;  // No thunk.
    174     } else {
    175       uint32_t thunk_end =
    176           CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader),
    177                                   InstructionSet::kThumb2) +
    178           MethodCallThunkSize();
    179       uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
    180       CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
    181       return true;   // Thunk present.
    182     }
    183   }
    184 
    185   uint32_t GetMethodOffset(uint32_t method_idx) {
    186     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
    187     CHECK(result.first);
    188     CHECK_NE(result.second & 1u, 0u);
    189     return result.second - 1 /* thumb mode */;
    190   }
    191 
    192   std::vector<uint8_t> CompileMethodCallThunk() {
    193     ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey();
    194     return static_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
    195   }
    196 
    197   uint32_t MethodCallThunkSize() {
    198     return CompileMethodCallThunk().size();
    199   }
    200 
    201   bool CheckThunk(uint32_t thunk_offset) {
    202     const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
    203     if (output_.size() < thunk_offset + expected_code.size()) {
    204       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
    205           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
    206       return false;
    207     }
    208     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
    209     if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
    210       return true;
    211     }
    212     // Log failure info.
    213     DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
    214     return false;
    215   }
    216 
    217   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
    218     std::vector<uint8_t> result;
    219     result.reserve(num_nops * 2u + 4u);
    220     for (size_t i = 0; i != num_nops; ++i) {
    221       PushBackInsn(&result, kNopInsn);
    222     }
    223     PushBackInsn(&result, bl);
    224     return result;
    225   }
    226 
    227   void TestStringBssEntry(uint32_t bss_begin, uint32_t string_entry_offset);
    228   void TestStringReference(uint32_t string_offset);
    229   void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
    230 
    231   std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg,
    232                                                uint32_t holder_reg,
    233                                                bool narrow) {
    234     const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
    235         0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow));
    236     ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
    237     return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
    238   }
    239 
    240   std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
    241     LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
    242         0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg));
    243     ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
    244     return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
    245   }
    246 
    247   std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) {
    248     LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
    249         0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow));
    250     ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
    251     return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
    252   }
    253 
    254   uint32_t GetOutputInsn32(uint32_t offset) {
    255     CHECK_LE(offset, output_.size());
    256     CHECK_GE(output_.size() - offset, 4u);
    257     return (static_cast<uint32_t>(output_[offset]) << 16) |
    258            (static_cast<uint32_t>(output_[offset + 1]) << 24) |
    259            (static_cast<uint32_t>(output_[offset + 2]) << 0) |
    260            (static_cast<uint32_t>(output_[offset + 3]) << 8);
    261   }
    262 
    263   uint16_t GetOutputInsn16(uint32_t offset) {
    264     CHECK_LE(offset, output_.size());
    265     CHECK_GE(output_.size() - offset, 2u);
    266     return (static_cast<uint32_t>(output_[offset]) << 0) |
    267            (static_cast<uint32_t>(output_[offset + 1]) << 8);
    268   }
    269 
    270   void TestBakerFieldWide(uint32_t offset, uint32_t ref_reg);
    271   void TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg);
    272 };
    273 
    274 const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
    275     0x00, 0xf0, 0x00, 0xf8
    276 };
    277 
    278 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode);
    279 
    280 const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = {
    281     0x00, 0xbf
    282 };
    283 
    284 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
    285 
    286 const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
    287     0x40, 0xf2, 0x00, 0x00,   // MOVW r0, #0 (placeholder)
    288     0xc0, 0xf2, 0x00, 0x00,   // MOVT r0, #0 (placeholder)
    289     0x78, 0x44,               // ADD r0, pc
    290 };
    291 const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode(
    292     kUnpatchedPcRelativeRawCode);
    293 const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u;
    294 
    295 void Thumb2RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
    296                                                    uint32_t string_entry_offset) {
    297   constexpr uint32_t kStringIndex = 1u;
    298   string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
    299   bss_begin_ = bss_begin;
    300   const LinkerPatch patches[] = {
    301       LinkerPatch::StringBssEntryPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
    302       LinkerPatch::StringBssEntryPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
    303   };
    304   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
    305 }
    306 
    307 void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) {
    308   constexpr uint32_t kStringIndex = 1u;
    309   string_index_to_offset_map_.Put(kStringIndex, string_offset);
    310   const LinkerPatch patches[] = {
    311       LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
    312       LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
    313   };
    314   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
    315 }
    316 
    317 void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
    318                                                      uint32_t target_offset) {
    319   AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
    320   Link();
    321 
    322   uint32_t method1_offset = GetMethodOffset(1u);
    323   uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */;
    324   uint32_t diff = target_offset - pc_base_offset;
    325   // Distribute the bits of the diff between the MOVW and MOVT:
    326   uint32_t diffw = diff & 0xffffu;
    327   uint32_t difft = diff >> 16;
    328   uint32_t movw = 0xf2400000u |           // MOVW r0, #0 (placeholder),
    329       ((diffw & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
    330       ((diffw & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
    331       ((diffw & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
    332       ((diffw & 0x00ffu));                // keep imm8 at bits 0-7.
    333   uint32_t movt = 0xf2c00000u |           // MOVT r0, #0 (placeholder),
    334       ((difft & 0xf000u) << (16 - 12)) |  // move imm4 from bits 12-15 to bits 16-19,
    335       ((difft & 0x0800u) << (26 - 11)) |  // move imm from bit 11 to bit 26,
    336       ((difft & 0x0700u) << (12 - 8)) |   // move imm3 from bits 8-10 to bits 12-14,
    337       ((difft & 0x00ffu));                // keep imm8 at bits 0-7.
    338   const uint8_t expected_code[] = {
    339       static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
    340       static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
    341       static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
    342       static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
    343       0x78, 0x44,
    344   };
    345   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
    346 }
    347 
    348 TEST_F(Thumb2RelativePatcherTest, CallSelf) {
    349   const LinkerPatch patches[] = {
    350       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
    351   };
    352   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
    353   Link();
    354 
    355   static const uint8_t expected_code[] = {
    356       0xff, 0xf7, 0xfe, 0xff
    357   };
    358   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
    359 }
    360 
    361 TEST_F(Thumb2RelativePatcherTest, CallOther) {
    362   const LinkerPatch method1_patches[] = {
    363       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
    364   };
    365   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
    366   const LinkerPatch method2_patches[] = {
    367       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
    368   };
    369   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
    370   Link();
    371 
    372   uint32_t method1_offset = GetMethodOffset(1u);
    373   uint32_t method2_offset = GetMethodOffset(2u);
    374   uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */);
    375   ASSERT_EQ(diff_after & 1u, 0u);
    376   ASSERT_LT(diff_after >> 1, 1u << 8);  // Simple encoding, (diff_after >> 1) fits into 8 bits.
    377   static const uint8_t method1_expected_code[] = {
    378       0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8
    379   };
    380   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
    381   uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */);
    382   ASSERT_EQ(diff_before & 1u, 0u);
    383   ASSERT_GE(diff_before, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0.
    384   auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu));
    385   EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
    386 }
    387 
    388 TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
    389   const LinkerPatch patches[] = {
    390       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
    391   };
    392   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
    393   Link();
    394 
    395   uint32_t method1_offset = GetMethodOffset(1u);
    396   uint32_t diff = kTrampolineOffset - (method1_offset + 4u);
    397   ASSERT_EQ(diff & 1u, 0u);
    398   ASSERT_GE(diff, -1u << 9);  // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
    399   auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu));
    400   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
    401 }
    402 
    403 TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
    404   constexpr uint32_t missing_method_index = 1024u;
    405   auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
    406   constexpr uint32_t bl_offset_in_method3 = 3u * 2u;  // After NOPs.
    407   ArrayRef<const uint8_t> method3_code(method3_raw_code);
    408   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
    409   const LinkerPatch method3_patches[] = {
    410       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
    411   };
    412 
    413   constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
    414   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
    415                                             ArrayRef<const LinkerPatch>(),
    416                                             method3_code,
    417                                             ArrayRef<const LinkerPatch>(method3_patches),
    418                                             just_over_max_negative_disp - bl_offset_in_method3);
    419   ASSERT_FALSE(thunk_in_gap);  // There should be a thunk but it should be after the method2.
    420   ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
    421 
    422   // Check linked code.
    423   uint32_t method3_offset = GetMethodOffset(3u);
    424   uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
    425                                                   InstructionSet::kThumb2);
    426   uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
    427   ASSERT_EQ(diff & 1u, 0u);
    428   ASSERT_LT(diff >> 1, 1u << 8);  // Simple encoding, (diff >> 1) fits into 8 bits.
    429   auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
    430   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
    431   EXPECT_TRUE(CheckThunk(thunk_offset));
    432 }
    433 
    434 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
    435   auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
    436   constexpr uint32_t bl_offset_in_method1 = 3u * 2u;  // After NOPs.
    437   ArrayRef<const uint8_t> method1_code(method1_raw_code);
    438   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
    439   const LinkerPatch method1_patches[] = {
    440       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
    441   };
    442 
    443   constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
    444   bool thunk_in_gap = Create2MethodsWithGap(method1_code,
    445                                             ArrayRef<const LinkerPatch>(method1_patches),
    446                                             kNopCode,
    447                                             ArrayRef<const LinkerPatch>(),
    448                                             bl_offset_in_method1 + max_positive_disp);
    449   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
    450 
    451   // Check linked code.
    452   auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
    453   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
    454 }
    455 
    456 TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
    457   auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
    458   constexpr uint32_t bl_offset_in_method3 = 2u * 2u;  // After NOPs.
    459   ArrayRef<const uint8_t> method3_code(method3_raw_code);
    460   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
    461   const LinkerPatch method3_patches[] = {
    462       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
    463   };
    464 
    465   constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
    466   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
    467                                             ArrayRef<const LinkerPatch>(),
    468                                             method3_code,
    469                                             ArrayRef<const LinkerPatch>(method3_patches),
    470                                             just_over_max_negative_disp - bl_offset_in_method3);
    471   ASSERT_FALSE(thunk_in_gap);  // There should be no thunk.
    472 
    473   // Check linked code.
    474   auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
    475   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
    476 }
    477 
    478 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
    479   auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0);
    480   constexpr uint32_t bl_offset_in_method1 = 2u * 2u;  // After NOPs.
    481   ArrayRef<const uint8_t> method1_code(method1_raw_code);
    482   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
    483   const LinkerPatch method1_patches[] = {
    484       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
    485   };
    486 
    487   constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
    488   bool thunk_in_gap = Create2MethodsWithGap(method1_code,
    489                                             ArrayRef<const LinkerPatch>(method1_patches),
    490                                             kNopCode,
    491                                             ArrayRef<const LinkerPatch>(),
    492                                             bl_offset_in_method1 + just_over_max_positive_disp);
    493   ASSERT_TRUE(thunk_in_gap);
    494 
    495   uint32_t method1_offset = GetMethodOffset(1u);
    496   uint32_t method3_offset = GetMethodOffset(3u);
    497   ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset));
    498   uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
    499   uint32_t thunk_size = MethodCallThunkSize();
    500   uint32_t thunk_offset = RoundDown(method3_header_offset - thunk_size, kArmAlignment);
    501   DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
    502             method3_header_offset);
    503   ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
    504   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
    505   ASSERT_EQ(diff & 1u, 0u);
    506   ASSERT_GE(diff, 16 * MB - (1u << 9));  // Simple encoding, unknown bits fit into the low 8 bits.
    507   auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
    508   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
    509   CheckThunk(thunk_offset);
    510 }
    511 
    512 TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
    513   auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
    514   constexpr uint32_t bl_offset_in_method3 = 3u * 2u;  // After NOPs.
    515   ArrayRef<const uint8_t> method3_code(method3_raw_code);
    516   ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
    517   const LinkerPatch method3_patches[] = {
    518       LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
    519   };
    520 
    521   constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
    522   bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
    523                                             ArrayRef<const LinkerPatch>(),
    524                                             method3_code,
    525                                             ArrayRef<const LinkerPatch>(method3_patches),
    526                                             just_over_max_negative_disp - bl_offset_in_method3);
    527   ASSERT_FALSE(thunk_in_gap);  // There should be a thunk but it should be after the method2.
    528 
    529   // Check linked code.
    530   uint32_t method3_offset = GetMethodOffset(3u);
    531   uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
    532                                                   InstructionSet::kThumb2);
    533   uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
    534   ASSERT_EQ(diff & 1u, 0u);
    535   ASSERT_LT(diff >> 1, 1u << 8);  // Simple encoding, (diff >> 1) fits into 8 bits.
    536   auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
    537   EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
    538   EXPECT_TRUE(CheckThunk(thunk_offset));
    539 }
    540 
    541 TEST_F(Thumb2RelativePatcherTest, StringBssEntry1) {
    542   TestStringBssEntry(0x00ff0000u, 0x00fcu);
    543   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    544 }
    545 
    546 TEST_F(Thumb2RelativePatcherTest, StringBssEntry2) {
    547   TestStringBssEntry(0x02ff0000u, 0x05fcu);
    548   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    549 }
    550 
    551 TEST_F(Thumb2RelativePatcherTest, StringBssEntry3) {
    552   TestStringBssEntry(0x08ff0000u, 0x08fcu);
    553   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    554 }
    555 
    556 TEST_F(Thumb2RelativePatcherTest, StringBssEntry4) {
    557   TestStringBssEntry(0xd0ff0000u, 0x60fcu);
    558   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    559 }
    560 
    561 TEST_F(Thumb2RelativePatcherTest, StringReference1) {
    562   TestStringReference(0x00ff00fcu);
    563   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    564 }
    565 
    566 TEST_F(Thumb2RelativePatcherTest, StringReference2) {
    567   TestStringReference(0x02ff05fcu);
    568   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    569 }
    570 
    571 TEST_F(Thumb2RelativePatcherTest, StringReference3) {
    572   TestStringReference(0x08ff08fcu);
    573   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    574 }
    575 
    576 TEST_F(Thumb2RelativePatcherTest, StringReference4) {
    577   TestStringReference(0xd0ff60fcu);
    578   ASSERT_LT(GetMethodOffset(1u), 0xfcu);
    579 }
    580 
    581 void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref_reg) {
    582   uint32_t valid_regs[] = {
    583       0,  1,  2,  3,      5,  6,  7,  // R4 is reserved for entrypoint address.
    584       8,  9, 10, 11,                  // IP, SP, LR and PC are reserved.
    585   };
    586   DCHECK_ALIGNED(offset, 4u);
    587   DCHECK_LT(offset, 4 * KB);
    588   constexpr size_t kMethodCodeSize = 8u;
    589   constexpr size_t kLiteralOffset = 0u;
    590   uint32_t method_idx = 0u;
    591   for (uint32_t base_reg : valid_regs) {
    592     for (uint32_t holder_reg : valid_regs) {
    593       uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12);
    594       const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr});
    595       ASSERT_EQ(kMethodCodeSize, raw_code.size());
    596       ArrayRef<const uint8_t> code(raw_code);
    597       uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
    598           base_reg, holder_reg, /* narrow */ false);
    599       const LinkerPatch patches[] = {
    600           LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
    601       };
    602       ++method_idx;
    603       AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
    604     }
    605   }
    606   Link();
    607 
    608   // All thunks are at the end.
    609   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
    610   method_idx = 0u;
    611   for (uint32_t base_reg : valid_regs) {
    612     for (uint32_t holder_reg : valid_regs) {
    613       ++method_idx;
    614       uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
    615       uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12);
    616       const std::vector<uint8_t> expected_code = RawCode({bne, ldr});
    617       ASSERT_EQ(kMethodCodeSize, expected_code.size()) << "bne=0x" << std::hex << bne;
    618       ASSERT_TRUE(
    619           CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
    620 
    621       std::vector<uint8_t> expected_thunk =
    622           CompileBakerOffsetThunk(base_reg, holder_reg, /* narrow */ false);
    623       ASSERT_GT(output_.size(), thunk_offset);
    624       ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
    625       ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
    626                                              expected_thunk.size());
    627       if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
    628         DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
    629         ASSERT_TRUE(false);
    630       }
    631 
    632       size_t gray_check_offset = thunk_offset;
    633       if (holder_reg == base_reg) {
    634         // Verify that the null-check uses the correct register, i.e. holder_reg.
    635         if (holder_reg < 8) {
    636           ASSERT_GE(output_.size() - gray_check_offset, 2u);
    637           ASSERT_EQ(0xb100 | holder_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
    638           gray_check_offset +=2u;
    639         } else {
    640           ASSERT_GE(output_.size() - gray_check_offset, 6u);
    641           ASSERT_EQ(0xf1b00f00u | (holder_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
    642           ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u);  // BEQ
    643           gray_check_offset += 6u;
    644         }
    645       }
    646       // Verify that the lock word for gray bit check is loaded from the holder address.
    647       ASSERT_GE(output_.size() - gray_check_offset,
    648                 4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
    649       const uint32_t load_lock_word =
    650           kLdrWInsn |
    651           (holder_reg << 16) |
    652           (/* IP */ 12 << 12) |
    653           mirror::Object::MonitorOffset().Uint32Value();
    654       ASSERT_EQ(load_lock_word, GetOutputInsn32(gray_check_offset));
    655       // Verify the gray bit check.
    656       DCHECK_GE(LockWord::kReadBarrierStateShift, 8u);  // ROR modified immediate.
    657       uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
    658       const uint32_t tst_gray_bit_without_offset =
    659           0xf0100f00 | (/* IP */ 12 << 16)
    660                      | (((ror_shift >> 4) & 1) << 26)   // i
    661                      | (((ror_shift >> 1) & 7) << 12)   // imm3
    662                      | ((ror_shift & 1) << 7);          // imm8, ROR('1':imm8<7:0>, ror_shift).
    663       EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(gray_check_offset + 4u));
    664       EXPECT_EQ(0xd100u, GetOutputInsn16(gray_check_offset + 8u) & 0xff00u);  // BNE
    665       // Verify the fake dependency (skip "ADD LR, LR, #ldr_offset").
    666       const uint32_t fake_dependency =
    667           0xeb000010 |              // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
    668           (/* IP */ 12) |           // Rm = IP
    669           (base_reg << 16) |        // Rn = base_reg
    670           (base_reg << 8);          // Rd = base_reg
    671       EXPECT_EQ(fake_dependency, GetOutputInsn32(gray_check_offset + 14u));
    672       // Do not check the rest of the implementation.
    673 
    674       // The next thunk follows on the next aligned offset.
    675       thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
    676     }
    677   }
    678 }
    679 
    680 void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg) {
    681   uint32_t valid_regs[] = {
    682       0,  1,  2,  3,      5,  6,  7,  // R4 is reserved for entrypoint address.
    683       8,  9, 10, 11,                  // IP, SP, LR and PC are reserved.
    684   };
    685   DCHECK_ALIGNED(offset, 4u);
    686   DCHECK_LT(offset, 32u);
    687   constexpr size_t kMethodCodeSize = 6u;
    688   constexpr size_t kLiteralOffset = 0u;
    689   uint32_t method_idx = 0u;
    690   for (uint32_t base_reg : valid_regs) {
    691     if (base_reg >= 8u) {
    692       continue;
    693     }
    694     for (uint32_t holder_reg : valid_regs) {
    695       uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg;
    696       const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr});
    697       ASSERT_EQ(kMethodCodeSize, raw_code.size());
    698       ArrayRef<const uint8_t> code(raw_code);
    699       uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
    700           base_reg, holder_reg, /* narrow */ true);
    701       const LinkerPatch patches[] = {
    702           LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
    703       };
    704       ++method_idx;
    705       AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
    706     }
    707   }
    708   Link();
    709 
    710   // All thunks are at the end.
    711   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
    712   method_idx = 0u;
    713   for (uint32_t base_reg : valid_regs) {
    714     if (base_reg >= 8u) {
    715       continue;
    716     }
    717     for (uint32_t holder_reg : valid_regs) {
    718       ++method_idx;
    719       uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
    720       uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg;
    721       const std::vector<uint8_t> expected_code = RawCode({bne, ldr});
    722       ASSERT_EQ(kMethodCodeSize, expected_code.size()) << "bne=0x" << std::hex << bne;
    723       ASSERT_TRUE(
    724           CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
    725 
    726       std::vector<uint8_t> expected_thunk =
    727           CompileBakerOffsetThunk(base_reg, holder_reg, /* narrow */ true);
    728       ASSERT_GT(output_.size(), thunk_offset);
    729       ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
    730       ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
    731                                              expected_thunk.size());
    732       if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
    733         DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
    734         ASSERT_TRUE(false);
    735       }
    736 
    737       size_t gray_check_offset = thunk_offset;
    738       if (holder_reg == base_reg) {
    739         // Verify that the null-check uses the correct register, i.e. holder_reg.
    740         if (holder_reg < 8) {
    741           ASSERT_GE(output_.size() - gray_check_offset, 2u);
    742           ASSERT_EQ(0xb100 | holder_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
    743           gray_check_offset +=2u;
    744         } else {
    745           ASSERT_GE(output_.size() - gray_check_offset, 6u);
    746           ASSERT_EQ(0xf1b00f00u | (holder_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
    747           ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u);  // BEQ
    748           gray_check_offset += 6u;
    749         }
    750       }
    751       // Verify that the lock word for gray bit check is loaded from the holder address.
    752       ASSERT_GE(output_.size() - gray_check_offset,
    753                 4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
    754       const uint32_t load_lock_word =
    755           kLdrWInsn |
    756           (holder_reg << 16) |
    757           (/* IP */ 12 << 12) |
    758           mirror::Object::MonitorOffset().Uint32Value();
    759       ASSERT_EQ(load_lock_word, GetOutputInsn32(gray_check_offset));
    760       // Verify the gray bit check.
    761       DCHECK_GE(LockWord::kReadBarrierStateShift, 8u);  // ROR modified immediate.
    762       uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
    763       const uint32_t tst_gray_bit_without_offset =
    764           0xf0100f00 | (/* IP */ 12 << 16)
    765                      | (((ror_shift >> 4) & 1) << 26)   // i
    766                      | (((ror_shift >> 1) & 7) << 12)   // imm3
    767                      | ((ror_shift & 1) << 7);          // imm8, ROR('1':imm8<7:0>, ror_shift).
    768       EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(gray_check_offset + 4u));
    769       EXPECT_EQ(0xd100u, GetOutputInsn16(gray_check_offset + 8u) & 0xff00u);  // BNE
    770       // Verify the fake dependency (skip "ADD LR, LR, #ldr_offset").
    771       const uint32_t fake_dependency =
    772           0xeb000010 |              // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
    773           (/* IP */ 12) |           // Rm = IP
    774           (base_reg << 16) |        // Rn = base_reg
    775           (base_reg << 8);          // Rd = base_reg
    776       EXPECT_EQ(fake_dependency, GetOutputInsn32(gray_check_offset + 14u));
    777       // Do not check the rest of the implementation.
    778 
    779       // The next thunk follows on the next aligned offset.
    780       thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
    781     }
    782   }
    783 }
    784 
    785 #define TEST_BAKER_FIELD_WIDE(offset, ref_reg)    \
    786   TEST_F(Thumb2RelativePatcherTest,               \
    787     BakerOffsetWide##offset##_##ref_reg) {        \
    788     TestBakerFieldWide(offset, ref_reg);          \
    789   }
    790 
    791 TEST_BAKER_FIELD_WIDE(/* offset */ 0, /* ref_reg */ 0)
    792 TEST_BAKER_FIELD_WIDE(/* offset */ 8, /* ref_reg */ 3)
    793 TEST_BAKER_FIELD_WIDE(/* offset */ 28, /* ref_reg */ 7)
    794 TEST_BAKER_FIELD_WIDE(/* offset */ 0xffc, /* ref_reg */ 11)
    795 
    796 #define TEST_BAKER_FIELD_NARROW(offset, ref_reg)  \
    797   TEST_F(Thumb2RelativePatcherTest,               \
    798     BakerOffsetNarrow##offset##_##ref_reg) {      \
    799     TestBakerFieldNarrow(offset, ref_reg);        \
    800   }
    801 
    802 TEST_BAKER_FIELD_NARROW(/* offset */ 0, /* ref_reg */ 0)
    803 TEST_BAKER_FIELD_NARROW(/* offset */ 8, /* ref_reg */ 3)
    804 TEST_BAKER_FIELD_NARROW(/* offset */ 28, /* ref_reg */ 7)
    805 
    806 TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) {
    807   // One thunk in the middle with maximum distance branches to it from both sides.
    808   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
    809   constexpr uint32_t kLiteralOffset1 = 6u;
    810   const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
    811   ArrayRef<const uint8_t> code1(raw_code1);
    812   uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
    813       /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
    814   const LinkerPatch patches1[] = {
    815       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
    816   };
    817   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
    818 
    819   constexpr uint32_t expected_thunk_offset =
    820       kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
    821   static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
    822   size_t filler1_size = expected_thunk_offset -
    823                         RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
    824   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
    825   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
    826   AddCompiledMethod(MethodRef(2u), filler1_code);
    827 
    828   // Enforce thunk reservation with a tiny method.
    829   AddCompiledMethod(MethodRef(3u), kNopCode);
    830 
    831   constexpr uint32_t kLiteralOffset2 = 4;
    832   static_assert(IsAligned<kArmAlignment>(kLiteralOffset2 + kPcAdjustment),
    833                 "PC for BNE must be aligned.");
    834 
    835   // Allow reaching the thunk from the very beginning of a method almost 1MiB away. Backward branch
    836   // reaches the full 1MiB but we need to take PC adjustment into account. Things to subtract:
    837   //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
    838   //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
    839   //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
    840   size_t thunk_size =
    841       CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
    842   size_t filler2_size =
    843       1 * MB - (kLiteralOffset2 + kPcAdjustment)
    844              - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
    845              - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
    846              - sizeof(OatQuickMethodHeader);
    847   std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
    848   ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
    849   AddCompiledMethod(MethodRef(4u), filler2_code);
    850 
    851   const std::vector<uint8_t> raw_code2 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn});
    852   ArrayRef<const uint8_t> code2(raw_code2);
    853   const LinkerPatch patches2[] = {
    854       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
    855   };
    856   AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
    857 
    858   Link();
    859 
    860   uint32_t first_method_offset = GetMethodOffset(1u);
    861   uint32_t last_method_offset = GetMethodOffset(5u);
    862   EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
    863 
    864   const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
    865   const uint32_t bne_max_backward = kBneWPlus0 | 0x04000000;
    866   const std::vector<uint8_t> expected_code1 =
    867       RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
    868   const std::vector<uint8_t> expected_code2 = RawCode({kNopWInsn, bne_max_backward, kLdrWInsn});
    869   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
    870   ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
    871 }
    872 
    873 TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) {
    874   // Based on the first part of BakerOffsetThunkInTheMiddle but the BNE is one instruction
    875   // earlier, so the thunk is emitted before the filler.
    876   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
    877   constexpr uint32_t kLiteralOffset1 = 4u;
    878   const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn});
    879   ArrayRef<const uint8_t> code1(raw_code1);
    880   uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
    881       /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
    882   const LinkerPatch patches1[] = {
    883       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
    884   };
    885   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
    886 
    887   constexpr uint32_t expected_thunk_offset =
    888       kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement + 2 */ (1u << 20);
    889   static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
    890   size_t filler1_size = expected_thunk_offset -
    891                         RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
    892   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
    893   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
    894   AddCompiledMethod(MethodRef(2u), filler1_code);
    895 
    896   Link();
    897 
    898   const uint32_t bne = BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmAlignment));
    899   const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, bne, kLdrWInsn, kNopInsn});
    900   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
    901 }
    902 
    903 TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast) {
    904   // Based on the BakerOffsetThunkInTheMiddle but the BNE in the last method is preceded
    905   // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end.
    906   // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
    907   constexpr uint32_t kLiteralOffset1 = 6u;
    908   const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
    909   ArrayRef<const uint8_t> code1(raw_code1);
    910   uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
    911       /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
    912   const LinkerPatch patches1[] = {
    913       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
    914   };
    915   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
    916 
    917   constexpr uint32_t expected_thunk_offset =
    918       kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
    919   static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
    920   size_t filler1_size = expected_thunk_offset -
    921                         RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
    922   std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
    923   ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
    924   AddCompiledMethod(MethodRef(2u), filler1_code);
    925 
    926   // Enforce thunk reservation with a tiny method.
    927   AddCompiledMethod(MethodRef(3u), kNopCode);
    928 
    929   constexpr uint32_t kReachableFromOffset2 = 4;
    930   constexpr uint32_t kLiteralOffset2 = kReachableFromOffset2 + 2;
    931   static_assert(IsAligned<kArmAlignment>(kReachableFromOffset2 + kPcAdjustment),
    932                 "PC for BNE must be aligned.");
    933 
    934   // If not for the extra NOP, this would allow reaching the thunk from the BNE
    935   // of a method 1MiB away. Backward branch reaches the full 1MiB  but we need to take
    936   // PC adjustment into account. Things to subtract:
    937   //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
    938   //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
    939   //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
    940   size_t thunk_size =
    941       CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
    942   size_t filler2_size =
    943       1 * MB - (kReachableFromOffset2 + kPcAdjustment)
    944              - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
    945              - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
    946              - sizeof(OatQuickMethodHeader);
    947   std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
    948   ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
    949   AddCompiledMethod(MethodRef(4u), filler2_code);
    950 
    951   // Extra 16-bit NOP compared to BakerOffsetThunkInTheMiddle.
    952   const std::vector<uint8_t> raw_code2 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
    953   ArrayRef<const uint8_t> code2(raw_code2);
    954   const LinkerPatch patches2[] = {
    955       LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
    956   };
    957   AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
    958 
    959   Link();
    960 
    961   uint32_t first_method_offset = GetMethodOffset(1u);
    962   uint32_t last_method_offset = GetMethodOffset(5u);
    963   EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
    964 
    965   const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
    966   const uint32_t bne_last =
    967       BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmAlignment));
    968   const std::vector<uint8_t> expected_code1 =
    969       RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
    970   const std::vector<uint8_t> expected_code2 =
    971       RawCode({kNopWInsn, kNopInsn, bne_last, kLdrWInsn});
    972   ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
    973   ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
    974 }
    975 
    976 TEST_F(Thumb2RelativePatcherTest, BakerArray) {
    977   uint32_t valid_regs[] = {
    978       0,  1,  2,  3,      5,  6,  7,  // R4 is reserved for entrypoint address.
    979       8,  9, 10, 11,                  // IP, SP, LR and PC are reserved.
    980   };
    981   auto ldr = [](uint32_t base_reg) {
    982     uint32_t index_reg = (base_reg == 0u) ? 1u : 0u;
    983     uint32_t ref_reg = (base_reg == 2) ? 3u : 2u;
    984     return kLdrRegLsl2 | index_reg | (base_reg << 16) | (ref_reg << 12);
    985   };
    986   constexpr size_t kMethodCodeSize = 8u;
    987   constexpr size_t kLiteralOffset = 0u;
    988   uint32_t method_idx = 0u;
    989   for (uint32_t base_reg : valid_regs) {
    990     ++method_idx;
    991     const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr(base_reg)});
    992     ASSERT_EQ(kMethodCodeSize, raw_code.size());
    993     ArrayRef<const uint8_t> code(raw_code);
    994     const LinkerPatch patches[] = {
    995         LinkerPatch::BakerReadBarrierBranchPatch(
    996             kLiteralOffset, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)),
    997     };
    998     AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
    999   }
   1000   Link();
   1001 
   1002   // All thunks are at the end.
   1003   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
   1004   method_idx = 0u;
   1005   for (uint32_t base_reg : valid_regs) {
   1006     ++method_idx;
   1007     uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
   1008     const std::vector<uint8_t> expected_code = RawCode({bne, ldr(base_reg)});
   1009     ASSERT_EQ(kMethodCodeSize, expected_code.size());
   1010     EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
   1011 
   1012     std::vector<uint8_t> expected_thunk = CompileBakerArrayThunk(base_reg);
   1013     ASSERT_GT(output_.size(), thunk_offset);
   1014     ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
   1015     ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
   1016                                            expected_thunk.size());
   1017     if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
   1018       DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
   1019       ASSERT_TRUE(false);
   1020     }
   1021 
   1022     // Verify that the lock word for gray bit check is loaded from the correct address
   1023     // before the base_reg which points to the array data.
   1024     ASSERT_GE(output_.size() - thunk_offset,
   1025               4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
   1026     int32_t data_offset =
   1027         mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
   1028     int32_t offset = mirror::Object::MonitorOffset().Int32Value() - data_offset;
   1029     ASSERT_LT(offset, 0);
   1030     ASSERT_GT(offset, -256);
   1031     const uint32_t load_lock_word =
   1032         kLdrNegativeOffset |
   1033         (-offset & 0xffu) |
   1034         (base_reg << 16) |
   1035         (/* IP */ 12 << 12);
   1036     EXPECT_EQ(load_lock_word, GetOutputInsn32(thunk_offset));
   1037     // Verify the gray bit check.
   1038     DCHECK_GE(LockWord::kReadBarrierStateShift, 8u);  // ROR modified immediate.
   1039     uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
   1040     const uint32_t tst_gray_bit_without_offset =
   1041         0xf0100f00 | (/* IP */ 12 << 16)
   1042                    | (((ror_shift >> 4) & 1) << 26)   // i
   1043                    | (((ror_shift >> 1) & 7) << 12)   // imm3
   1044                    | ((ror_shift & 1) << 7);          // imm8, ROR('1':imm8<7:0>, ror_shift).
   1045     EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(thunk_offset + 4u));
   1046     EXPECT_EQ(0xd100u, GetOutputInsn16(thunk_offset + 8u) & 0xff00u);  // BNE
   1047     // Verify the fake dependency.
   1048     const uint32_t fake_dependency =
   1049         0xeb000010 |              // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
   1050         (/* IP */ 12) |           // Rm = IP
   1051         (base_reg << 16) |        // Rn = base_reg
   1052         (base_reg << 8);          // Rd = base_reg
   1053     EXPECT_EQ(fake_dependency, GetOutputInsn32(thunk_offset + 14u));
   1054     // Do not check the rest of the implementation.
   1055 
   1056     // The next thunk follows on the next aligned offset.
   1057     thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
   1058   }
   1059 }
   1060 
   1061 TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) {
   1062   uint32_t valid_regs[] = {
   1063       0,  1,  2,  3,      5,  6,  7,  // R4 is reserved for entrypoint address.
   1064       8,  9, 10, 11,                  // IP, SP, LR and PC are reserved.
   1065   };
   1066   constexpr size_t kMethodCodeSize = 8u;
   1067   constexpr size_t kLiteralOffset = 4u;
   1068   uint32_t method_idx = 0u;
   1069   for (uint32_t root_reg : valid_regs) {
   1070     ++method_idx;
   1071     uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12);
   1072     const std::vector<uint8_t> raw_code = RawCode({ldr, kBneWPlus0});
   1073     ASSERT_EQ(kMethodCodeSize, raw_code.size());
   1074     ArrayRef<const uint8_t> code(raw_code);
   1075     const LinkerPatch patches[] = {
   1076         LinkerPatch::BakerReadBarrierBranchPatch(
   1077             kLiteralOffset,
   1078             Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)),
   1079     };
   1080     AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
   1081   }
   1082   Link();
   1083 
   1084   // All thunks are at the end.
   1085   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
   1086   method_idx = 0u;
   1087   for (uint32_t root_reg : valid_regs) {
   1088     ++method_idx;
   1089     uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
   1090     uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12);
   1091     const std::vector<uint8_t> expected_code = RawCode({ldr, bne});
   1092     ASSERT_EQ(kMethodCodeSize, expected_code.size());
   1093     EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
   1094 
   1095     std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg, /* narrow */ false);
   1096     ASSERT_GT(output_.size(), thunk_offset);
   1097     ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
   1098     ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
   1099                                            expected_thunk.size());
   1100     if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
   1101       DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
   1102       ASSERT_TRUE(false);
   1103     }
   1104 
   1105     // Verify that the fast-path null-check uses the correct register, i.e. root_reg.
   1106     if (root_reg < 8) {
   1107       ASSERT_GE(output_.size() - thunk_offset, 2u);
   1108       ASSERT_EQ(0xb100 | root_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
   1109     } else {
   1110       ASSERT_GE(output_.size() - thunk_offset, 6u);
   1111       ASSERT_EQ(0xf1b00f00u | (root_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
   1112       ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u);  // BEQ
   1113     }
   1114     // Do not check the rest of the implementation.
   1115 
   1116     // The next thunk follows on the next aligned offset.
   1117     thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
   1118   }
   1119 }
   1120 
   1121 TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) {
   1122   uint32_t valid_regs[] = {
   1123       0,  1,  2,  3,      5,  6,  7,  // R4 is reserved for entrypoint address.
   1124                                       // Not appplicable to high registers.
   1125   };
   1126   constexpr size_t kMethodCodeSize = 6u;
   1127   constexpr size_t kLiteralOffset = 2u;
   1128   uint32_t method_idx = 0u;
   1129   for (uint32_t root_reg : valid_regs) {
   1130     ++method_idx;
   1131     uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg;
   1132     const std::vector<uint8_t> raw_code = RawCode({ldr, kBneWPlus0});
   1133     ASSERT_EQ(kMethodCodeSize, raw_code.size());
   1134     ArrayRef<const uint8_t> code(raw_code);
   1135     const LinkerPatch patches[] = {
   1136         LinkerPatch::BakerReadBarrierBranchPatch(
   1137             kLiteralOffset,
   1138             Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)),
   1139     };
   1140     AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
   1141   }
   1142   Link();
   1143 
   1144   // All thunks are at the end.
   1145   uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
   1146   method_idx = 0u;
   1147   for (uint32_t root_reg : valid_regs) {
   1148     ++method_idx;
   1149     uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
   1150     uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg;
   1151     const std::vector<uint8_t> expected_code = RawCode({ldr, bne});
   1152     ASSERT_EQ(kMethodCodeSize, expected_code.size());
   1153     EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
   1154 
   1155     std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg, /* narrow */ true);
   1156     ASSERT_GT(output_.size(), thunk_offset);
   1157     ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
   1158     ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
   1159                                            expected_thunk.size());
   1160     if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
   1161       DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
   1162       ASSERT_TRUE(false);
   1163     }
   1164 
   1165     // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg.
   1166     ASSERT_GE(output_.size() - thunk_offset, 2u);
   1167     ASSERT_EQ(0xb100 | root_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
   1168     // Do not check the rest of the implementation.
   1169 
   1170     // The next thunk follows on the next aligned offset.
   1171     thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
   1172   }
   1173 }
   1174 
   1175 TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) {
   1176   // Test 1MiB of patches to the same thunk to stress-test different large offsets.
   1177   // (The low bits are not that important but the location of the high bits is easy to get wrong.)
   1178   std::vector<uint8_t> code;
   1179   code.reserve(1 * MB);
   1180   const size_t num_patches = 1 * MB / 8u;
   1181   std::vector<LinkerPatch> patches;
   1182   patches.reserve(num_patches);
   1183   const uint32_t ldr =
   1184       kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12);
   1185   uint32_t encoded_data =
   1186       Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false);
   1187   for (size_t i = 0; i != num_patches; ++i) {
   1188     PushBackInsn(&code, ldr);
   1189     PushBackInsn(&code, kBneWPlus0);
   1190     patches.push_back(LinkerPatch::BakerReadBarrierBranchPatch(8u * i + 4u, encoded_data));
   1191   }
   1192   ASSERT_EQ(1 * MB, code.size());
   1193   ASSERT_EQ(num_patches, patches.size());
   1194   AddCompiledMethod(MethodRef(1u),
   1195                     ArrayRef<const uint8_t>(code),
   1196                     ArrayRef<const LinkerPatch>(patches));
   1197   Link();
   1198 
   1199   // The thunk is right after the method code.
   1200   DCHECK_ALIGNED(1 * MB, kArmAlignment);
   1201   std::vector<uint8_t> expected_code;
   1202   for (size_t i = 0; i != num_patches; ++i) {
   1203     PushBackInsn(&expected_code, ldr);
   1204     PushBackInsn(&expected_code, BneWWithOffset(8u * i + 4u, 1 * MB));
   1205     patches.push_back(LinkerPatch::BakerReadBarrierBranchPatch(8u * i + 4u, encoded_data));
   1206   }
   1207   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
   1208 }
   1209 
   1210 TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) {
   1211   // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());`
   1212   // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily
   1213   // hold when we're reserving thunks of different sizes. This test exposes the situation
   1214   // by using Baker thunks and a method call thunk.
   1215 
   1216   // Add a method call patch that can reach to method 1 offset + 16MiB.
   1217   uint32_t method_idx = 0u;
   1218   constexpr size_t kMethodCallLiteralOffset = 2u;
   1219   constexpr uint32_t kMissingMethodIdx = 2u;
   1220   const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0});
   1221   const LinkerPatch method1_patches[] = {
   1222       LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u),
   1223   };
   1224   ArrayRef<const uint8_t> code1(raw_code1);
   1225   ++method_idx;
   1226   AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches));
   1227 
   1228   // Skip kMissingMethodIdx.
   1229   ++method_idx;
   1230   ASSERT_EQ(kMissingMethodIdx, method_idx);
   1231   // Add a method with the right size that the method code for the next one starts 1MiB
   1232   // after code for method 1.
   1233   size_t filler_size =
   1234       1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
   1235              - sizeof(OatQuickMethodHeader);
   1236   std::vector<uint8_t> filler_code = GenNops(filler_size / 2u);
   1237   ++method_idx;
   1238   AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
   1239   // Add 14 methods with 1MiB code+header, making the code for the next method start 1MiB
   1240   // before the currently scheduled MaxNextOffset() for the method call thunk.
   1241   for (uint32_t i = 0; i != 14; ++i) {
   1242     filler_size = 1 * MB - sizeof(OatQuickMethodHeader);
   1243     filler_code = GenNops(filler_size / 2u);
   1244     ++method_idx;
   1245     AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
   1246   }
   1247 
   1248   // Add 2 Baker GC root patches to the last method, one that would allow the thunk at
   1249   // 1MiB + kArmAlignment, i.e. kArmAlignment after the method call thunk, and the
   1250   // second that needs it kArmAlignment after that. Given the size of the GC root thunk
   1251   // is more than the space required by the method call thunk plus kArmAlignment,
   1252   // this pushes the first GC root thunk's pending MaxNextOffset() before the method call
   1253   // thunk's pending MaxNextOffset() which needs to be adjusted.
   1254   ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmAlignment) + kArmAlignment,
   1255             CompileBakerGcRootThunk(/* root_reg */ 0, /* narrow */ false).size());
   1256   static_assert(kArmAlignment == 8, "Code below assumes kArmAlignment == 8");
   1257   constexpr size_t kBakerLiteralOffset1 = kArmAlignment + 2u - kPcAdjustment;
   1258   constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmAlignment;
   1259   // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | (root_reg << 12)`.
   1260   const uint32_t ldr1 = kLdrWInsn | (/* root_reg */ 1 << 12);
   1261   const uint32_t ldr2 = kLdrWInsn | (/* root_reg */ 2 << 12);
   1262   const std::vector<uint8_t> last_method_raw_code = RawCode({
   1263       kNopInsn,                                 // Padding before first GC root read barrier.
   1264       ldr1, kBneWPlus0,                         // First GC root LDR with read barrier.
   1265       ldr2, kBneWPlus0,                         // Second GC root LDR with read barrier.
   1266   });
   1267   uint32_t encoded_data1 =
   1268       Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false);
   1269   uint32_t encoded_data2 =
   1270       Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false);
   1271   const LinkerPatch last_method_patches[] = {
   1272       LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1),
   1273       LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2),
   1274   };
   1275   ++method_idx;
   1276   AddCompiledMethod(MethodRef(method_idx),
   1277                     ArrayRef<const uint8_t>(last_method_raw_code),
   1278                     ArrayRef<const LinkerPatch>(last_method_patches));
   1279 
   1280   // The main purpose of the test is to check that Link() does not cause a crash.
   1281   Link();
   1282 
   1283   ASSERT_EQ(15 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u));
   1284 }
   1285 
   1286 }  // namespace linker
   1287 }  // namespace art
   1288