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