13f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko/* 23f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * Copyright (C) 2015 The Android Open Source Project 33f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * 43f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * Licensed under the Apache License, Version 2.0 (the "License"); 53f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * you may not use this file except in compliance with the License. 63f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * You may obtain a copy of the License at 73f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * 83f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * http://www.apache.org/licenses/LICENSE-2.0 93f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * 103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * Unless required by applicable law or agreed to in writing, software 113f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * distributed under the License is distributed on an "AS IS" BASIS, 123f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * See the License for the specific language governing permissions and 143f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko * limitations under the License. 153f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko */ 163f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 178cf9cb386cd9286d67e879f1ee501ec00d72a4e1Andreas Gampe#include "linker/arm64/relative_patcher_arm64.h" 188cf9cb386cd9286d67e879f1ee501ec00d72a4e1Andreas Gampe 19f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko#include "base/casts.h" 203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko#include "linker/relative_patcher_test.h" 21f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko#include "lock_word.h" 2266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko#include "mirror/array-inl.h" 23f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko#include "mirror/object.h" 24524e7ea8cd17bad17bd9f3e0ccbb19ad0d4d9c02Nicolas Geoffray#include "oat_quick_method_header.h" 253f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 263f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markonamespace art { 273f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markonamespace linker { 283f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 293f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoclass Arm64RelativePatcherTest : public RelativePatcherTest { 303f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko public: 313f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko explicit Arm64RelativePatcherTest(const std::string& variant) 3233bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko : RelativePatcherTest(InstructionSet::kArm64, variant) { } 333f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 343f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko protected: 353f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static const uint8_t kCallRawCode[]; 363f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static const ArrayRef<const uint8_t> kCallCode; 373f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static const uint8_t kNopRawCode[]; 383f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static const ArrayRef<const uint8_t> kNopCode; 393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 40f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // NOP instruction. 41f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static constexpr uint32_t kNopInsn = 0xd503201f; 42f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 433f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // All branches can be created from kBlPlus0 or kBPlus0 by adding the low 26 bits. 443f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static constexpr uint32_t kBlPlus0 = 0x94000000u; 453f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static constexpr uint32_t kBPlus0 = 0x14000000u; 463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Special BL values. 483f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static constexpr uint32_t kBlPlusMax = 0x95ffffffu; 493f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static constexpr uint32_t kBlMinusMax = 0x96000000u; 503f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // LDR immediate, 32-bit, unsigned offset. 52cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko static constexpr uint32_t kLdrWInsn = 0xb9400000u; 53cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 5466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // LDR register, 32-bit, LSL #2. 5566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko static constexpr uint32_t kLdrWLsl2Insn = 0xb8607800u; 5666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 5766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // LDUR, 32-bit. 5866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko static constexpr uint32_t kLdurWInsn = 0xb8400000u; 5966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 60cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko // ADD/ADDS/SUB/SUBS immediate, 64-bit. 61cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko static constexpr uint32_t kAddXInsn = 0x91000000u; 62cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko static constexpr uint32_t kAddsXInsn = 0xb1000000u; 63cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko static constexpr uint32_t kSubXInsn = 0xd1000000u; 64cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko static constexpr uint32_t kSubsXInsn = 0xf1000000u; 65cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // LDUR x2, [sp, #4], i.e. unaligned load crossing 64-bit boundary (assuming aligned sp). 673f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko static constexpr uint32_t kLdurInsn = 0xf840405fu; 683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin // LDR w12, <label> and LDR x12, <label>. Bits 5-23 contain label displacement in 4-byte units. 7097e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin static constexpr uint32_t kLdrWPcRelInsn = 0x1800000cu; 7197e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin static constexpr uint32_t kLdrXPcRelInsn = 0x5800000cu; 7297e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 7397e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin // LDR w13, [SP, #<pimm>] and LDR x13, [SP, #<pimm>]. Bits 10-21 contain displacement from SP 7497e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin // in units of 4-bytes (for 32-bit load) or 8-bytes (for 64-bit load). 7597e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin static constexpr uint32_t kLdrWSpRelInsn = 0xb94003edu; 7697e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin static constexpr uint32_t kLdrXSpRelInsn = 0xf94003edu; 7797e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 78f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // CBNZ x17, +0. Bits 5-23 are a placeholder for target offset from PC in units of 4-bytes. 7966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko static constexpr uint32_t kCbnzIP1Plus0Insn = 0xb5000011u; 80f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 81f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) { 82f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko CHECK_LE(pos, code->size()); 83f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint8_t insn_code[] = { 84f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_cast<uint8_t>(insn), 85f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_cast<uint8_t>(insn >> 8), 86f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_cast<uint8_t>(insn >> 16), 87f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_cast<uint8_t>(insn >> 24), 88f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 89f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code)."); 90f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code)); 91f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 92f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 93f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) { 94f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko InsertInsn(code, code->size(), insn); 95f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 96f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 97f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) { 98f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_code; 99f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko raw_code.reserve(insns.size() * 4u); 100f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t insn : insns) { 101f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&raw_code, insn); 102f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 103f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko return raw_code; 104f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 105f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1063f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code, 107b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko const ArrayRef<const LinkerPatch>& method1_patches, 1083f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko const ArrayRef<const uint8_t>& last_method_code, 109b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko const ArrayRef<const LinkerPatch>& last_method_patches, 1103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t distance_without_thunks) { 1113f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u); 1120c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t method1_offset = 1130c737df58fd4796df6c79837f409c40595168db5Vladimir Marko kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader); 114b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko AddCompiledMethod(MethodRef(1u), method1_code, method1_patches); 1150c737df58fd4796df6c79837f409c40595168db5Vladimir Marko const uint32_t gap_start = method1_offset + method1_code.size(); 1163f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1173f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // We want to put the method3 at a very precise offset. 1183f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko const uint32_t last_method_offset = method1_offset + distance_without_thunks; 1190c737df58fd4796df6c79837f409c40595168db5Vladimir Marko CHECK_ALIGNED(last_method_offset, kArm64Alignment); 1203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader); 1213f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1220c737df58fd4796df6c79837f409c40595168db5Vladimir Marko // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB). 1233f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // (This allows deduplicating the small chunks to avoid using 256MiB of memory for +-128MiB 1240c737df58fd4796df6c79837f409c40595168db5Vladimir Marko // offsets by this test. Making the first chunk bigger makes it easy to give all intermediate 1250c737df58fd4796df6c79837f409c40595168db5Vladimir Marko // methods the same alignment of the end, so the thunk insertion adds a predictable size as 1260c737df58fd4796df6c79837f409c40595168db5Vladimir Marko // long as it's after the first chunk.) 1273f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method_idx = 2u; 1283f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t kSmallChunkSize = 2 * MB; 1293f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko std::vector<uint8_t> gap_code; 1300c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t gap_size = gap_end - gap_start; 1310c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t num_small_chunks = std::max(gap_size / kSmallChunkSize, 1u) - 1u; 1320c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t chunk_start = gap_start; 1330c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t chunk_size = gap_size - num_small_chunks * kSmallChunkSize; 1340c737df58fd4796df6c79837f409c40595168db5Vladimir Marko for (uint32_t i = 0; i <= num_small_chunks; ++i) { // num_small_chunks+1 iterations. 1350c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t chunk_code_size = 1360c737df58fd4796df6c79837f409c40595168db5Vladimir Marko chunk_size - CodeAlignmentSize(chunk_start) - sizeof(OatQuickMethodHeader); 1373f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko gap_code.resize(chunk_code_size, 0u); 138f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code)); 1393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko method_idx += 1u; 1400c737df58fd4796df6c79837f409c40595168db5Vladimir Marko chunk_start += chunk_size; 1410c737df58fd4796df6c79837f409c40595168db5Vladimir Marko chunk_size = kSmallChunkSize; // For all but the first chunk. 1420c737df58fd4796df6c79837f409c40595168db5Vladimir Marko DCHECK_EQ(CodeAlignmentSize(gap_end), CodeAlignmentSize(chunk_start)); 1433f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1443f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1453f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Add the last method and link 1463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko AddCompiledMethod(MethodRef(method_idx), last_method_code, last_method_patches); 1473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 1483f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1493f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Check assumptions. 1503f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK_EQ(GetMethodOffset(1), method1_offset); 1513f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto last_result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); 1523f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK(last_result.first); 1533f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // There may be a thunk before method2. 1543f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko if (last_result.second != last_method_offset) { 1553f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Thunk present. Check that there's only one. 15633bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko uint32_t thunk_end = 15733bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko CompiledCode::AlignCode(gap_end, InstructionSet::kArm64) + MethodCallThunkSize(); 1580c737df58fd4796df6c79837f409c40595168db5Vladimir Marko uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end); 1590c737df58fd4796df6c79837f409c40595168db5Vladimir Marko CHECK_EQ(last_result.second, header_offset + sizeof(OatQuickMethodHeader)); 1603f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1613f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return method_idx; 1623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1633f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1643f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t GetMethodOffset(uint32_t method_idx) { 1653f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); 1663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK(result.first); 167cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(result.second, 4u); 1683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return result.second; 1693f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1703f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 171f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> CompileMethodCallThunk() { 1720a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); 173f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key); 174f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 175f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 176f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t MethodCallThunkSize() { 177f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko return CompileMethodCallThunk().size(); 1783f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1793f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 1803f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko bool CheckThunk(uint32_t thunk_offset) { 181f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code = CompileMethodCallThunk(); 1823f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko if (output_.size() < thunk_offset + expected_code.size()) { 1833f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LOG(ERROR) << "output_.size() == " << output_.size() << " < " 1843f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size()); 1853f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return false; 1863f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1873f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size()); 188f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko if (linked_code == ArrayRef<const uint8_t>(expected_code)) { 1893f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return true; 1903f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1913f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Log failure info. 192f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code); 1933f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return false; 1943f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 1953f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 196f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> GenNops(size_t num_nops) { 197f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> result; 19866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko result.reserve(num_nops * 4u); 199f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (size_t i = 0; i != num_nops; ++i) { 200f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, kNopInsn); 201f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 202f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko return result; 203f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 204f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 2053f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) { 2063f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko std::vector<uint8_t> result; 2073f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko result.reserve(num_nops * 4u + 4u); 2083f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko for (size_t i = 0; i != num_nops; ++i) { 209f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, kNopInsn); 2103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 211f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, bl); 2123f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return result; 2133f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 2143f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 215cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko std::vector<uint8_t> GenNopsAndAdrpAndUse(size_t num_nops, 216cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t method_offset, 217cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t target_offset, 218cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t use_insn) { 2193f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko std::vector<uint8_t> result; 2203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko result.reserve(num_nops * 4u + 8u); 2213f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko for (size_t i = 0; i != num_nops; ++i) { 222f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, kNopInsn); 2233f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 224cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(method_offset, 4u); 225cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(target_offset, 4u); 2263f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t adrp_offset = method_offset + num_nops * 4u; 2273f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t disp = target_offset - (adrp_offset & ~0xfffu); 228cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko if (use_insn == kLdrWInsn) { 229cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko DCHECK_ALIGNED(disp, 1u << 2); 230cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko use_insn |= 1 | // LDR x1, [x0, #(imm12 << 2)] 231cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ((disp & 0xfffu) << (10 - 2)); // imm12 = ((disp & 0xfffu) >> 2) is at bit 10. 232cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } else if (use_insn == kAddXInsn) { 233cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko use_insn |= 1 | // ADD x1, x0, #imm 234cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko (disp & 0xfffu) << 10; // imm12 = (disp & 0xfffu) is at bit 10. 235cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } else { 236cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko LOG(FATAL) << "Unexpected instruction: 0x" << std::hex << use_insn; 237cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 23866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t adrp = 0x90000000u | // ADRP x0, +SignExtend(immhi:immlo:Zeros(12), 64) 2393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ((disp & 0x3000u) << (29 - 12)) | // immlo = ((disp & 0x3000u) >> 12) is at bit 29, 2403f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ((disp & 0xffffc000) >> (14 - 5)) | // immhi = (disp >> 14) is at bit 5, 2413f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // We take the sign bit from the disp, limiting disp to +- 2GiB. 2423f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ((disp & 0x80000000) >> (31 - 23)); // sign bit in immhi is at bit 23. 243f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, adrp); 244f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko PushBackInsn(&result, use_insn); 2453f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko return result; 2463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 2473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 248cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops, 249cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t method_offset, 250cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t target_offset) { 251cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kLdrWInsn); 252cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 253cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 2545f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko void TestNopsAdrpLdr(size_t num_nops, uint32_t bss_begin, uint32_t string_entry_offset) { 2555f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko constexpr uint32_t kStringIndex = 1u; 2565f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko string_index_to_offset_map_.Put(kStringIndex, string_entry_offset); 2575f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko bss_begin_ = bss_begin; 2583f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched. 259f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 2605f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko LinkerPatch::StringBssEntryPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex), 2615f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko LinkerPatch::StringBssEntryPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex), 2623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 263cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko AddCompiledMethod(MethodRef(1u), 264cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const uint8_t>(code), 265b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko ArrayRef<const LinkerPatch>(patches)); 2663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 2673f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 2683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 2695f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t target_offset = bss_begin_ + string_entry_offset; 2703f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset); 2713f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 2723f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 2733f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 274cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko std::vector<uint8_t> GenNopsAndAdrpAdd(size_t num_nops, 275cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t method_offset, 276cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t target_offset) { 277cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kAddXInsn); 278cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 279cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 280cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestNopsAdrpAdd(size_t num_nops, uint32_t string_offset) { 281cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko constexpr uint32_t kStringIndex = 1u; 282cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko string_index_to_offset_map_.Put(kStringIndex, string_offset); 283cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched. 284f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 285cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex), 286cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex), 287cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko }; 288cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko AddCompiledMethod(MethodRef(1u), 289cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const uint8_t>(code), 290cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const LinkerPatch>(patches)); 291cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko Link(); 292cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 293cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 294cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko auto expected_code = GenNopsAndAdrpAdd(num_nops, method1_offset, string_offset); 295cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 296cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 297cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 298cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void PrepareNopsAdrpInsn2Ldr(size_t num_nops, 299cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t insn2, 3005f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t bss_begin, 3015f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t string_entry_offset) { 3025f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko constexpr uint32_t kStringIndex = 1u; 3035f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko string_index_to_offset_map_.Put(kStringIndex, string_entry_offset); 3045f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko bss_begin_ = bss_begin; 3053f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched. 3063f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko InsertInsn(&code, num_nops * 4u + 4u, insn2); 307f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 3085f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko LinkerPatch::StringBssEntryPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex), 3095f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko LinkerPatch::StringBssEntryPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex), 3103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 311cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko AddCompiledMethod(MethodRef(1u), 312cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const uint8_t>(code), 313b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko ArrayRef<const LinkerPatch>(patches)); 3143f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 3153f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 3163f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 317cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void PrepareNopsAdrpInsn2Add(size_t num_nops, uint32_t insn2, uint32_t string_offset) { 318cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko constexpr uint32_t kStringIndex = 1u; 319cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko string_index_to_offset_map_.Put(kStringIndex, string_offset); 320cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched. 321cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko InsertInsn(&code, num_nops * 4u + 4u, insn2); 322f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 323cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex), 324cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex), 325cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko }; 326cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko AddCompiledMethod(MethodRef(1u), 327cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const uint8_t>(code), 328cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ArrayRef<const LinkerPatch>(patches)); 329cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko Link(); 330cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 3313f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 332cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestNopsAdrpInsn2AndUse(size_t num_nops, 333cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t insn2, 334cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t target_offset, 335cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t use_insn) { 3363f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 337cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn); 3383f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko InsertInsn(&expected_code, num_nops * 4u + 4u, insn2); 3393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 3403f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 3413f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 342cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops, 343cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t insn2, 344cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t target_offset, 345cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t use_insn) { 3463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 3473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK(!compiled_method_refs_.empty()); 348fc8b422c286501346b5b797420fb616aaa5e952aMathieu Chartier CHECK_EQ(compiled_method_refs_[0].index, 1u); 3493f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size()); 35035831e8bfa1c0944d4c978d99c4c5b9577945170Vladimir Marko uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size(); 35133bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko uint32_t thunk_offset = 35233bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko CompiledCode::AlignCode(method1_offset + method1_size, InstructionSet::kArm64); 3533f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u); 354cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(b_diff, 4u); 3553f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_LT(b_diff, 128 * MB); 3563f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t b_out = kBPlus0 + ((b_diff >> 2) & 0x03ffffffu); 3573f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t b_in = kBPlus0 + ((-b_diff >> 2) & 0x03ffffffu); 3583f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 359cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn); 3603f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko InsertInsn(&expected_code, num_nops * 4u + 4u, insn2); 3613f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Replace adrp with bl. 3623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko expected_code.erase(expected_code.begin() + num_nops * 4u, 3633f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko expected_code.begin() + num_nops * 4u + 4u); 3643f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko InsertInsn(&expected_code, num_nops * 4u, b_out); 3653f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 3663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 3673f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_thunk_code = GenNopsAndAdrpLdr(0u, thunk_offset, target_offset); 3683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(expected_thunk_code.size(), 8u); 3693f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko expected_thunk_code.erase(expected_thunk_code.begin() + 4u, expected_thunk_code.begin() + 8u); 3703f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko InsertInsn(&expected_thunk_code, 4u, b_in); 3713f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(expected_thunk_code.size(), 8u); 3723f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 373f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t thunk_size = MethodCallThunkSize(); 3743f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(thunk_offset + thunk_size, output_.size()); 3753f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(thunk_size, expected_thunk_code.size()); 3763f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> thunk_code(&output_[thunk_offset], thunk_size); 3773f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko if (ArrayRef<const uint8_t>(expected_thunk_code) != thunk_code) { 3783f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko DumpDiff(ArrayRef<const uint8_t>(expected_thunk_code), thunk_code); 3793f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko FAIL(); 3803f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 3813f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 3823f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 383cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpInsn2Ldr(uint32_t insn2, 384cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 385cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 3865f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t bss_begin, 3875f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t string_entry_offset) { 3883f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = 3890c737df58fd4796df6c79837f409c40595168db5Vladimir Marko kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader); 3903f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_LT(method1_offset, adrp_offset); 391cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(adrp_offset, 4u); 3923f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t num_nops = (adrp_offset - method1_offset) / 4u; 3935f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko PrepareNopsAdrpInsn2Ldr(num_nops, insn2, bss_begin, string_entry_offset); 3945f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t target_offset = bss_begin_ + string_entry_offset; 3953f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko if (has_thunk) { 396cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, target_offset, kLdrWInsn); 3973f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } else { 398cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpInsn2AndUse(num_nops, insn2, target_offset, kLdrWInsn); 3993f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 4003f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong. 4013f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko } 40297e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 403cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdurLdr(uint32_t adrp_offset, 404cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 4055f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t bss_begin, 4065f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t string_entry_offset) { 4075f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TestAdrpInsn2Ldr(kLdurInsn, adrp_offset, has_thunk, bss_begin, string_entry_offset); 40897e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 40997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 410cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn, 411cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko int32_t pcrel_disp, 412cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 413cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 4145f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t bss_begin, 4155f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t string_entry_offset) { 41697e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin ASSERT_LT(pcrel_disp, 0x100000); 41797e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin ASSERT_GE(pcrel_disp, -0x100000); 41897e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin ASSERT_EQ(pcrel_disp & 0x3, 0); 41997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5); 4205f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset); 42197e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 42297e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 423cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn, 424cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t sprel_disp_in_load_units, 425cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 426cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 4275f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t bss_begin, 4285f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko uint32_t string_entry_offset) { 42997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin ASSERT_LT(sprel_disp_in_load_units, 0x1000u); 43097e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10); 4315f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset); 43297e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 433cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 434cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpInsn2Add(uint32_t insn2, 435cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 436cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 437cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t string_offset) { 438cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t method1_offset = 4390c737df58fd4796df6c79837f409c40595168db5Vladimir Marko kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader); 440cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_LT(method1_offset, adrp_offset); 441cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(adrp_offset, 4u); 442cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t num_nops = (adrp_offset - method1_offset) / 4u; 443cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko PrepareNopsAdrpInsn2Add(num_nops, insn2, string_offset); 444cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko if (has_thunk) { 445cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, string_offset, kAddXInsn); 446cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } else { 447cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpInsn2AndUse(num_nops, insn2, string_offset, kAddXInsn); 448cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 449cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong. 450cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 451cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 452cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdurAdd(uint32_t adrp_offset, bool has_thunk, uint32_t string_offset) { 453cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(kLdurInsn, adrp_offset, has_thunk, string_offset); 454cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 455cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 456cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn, 457cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko int32_t pcrel_disp, 458cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 459cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 460cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t string_offset) { 461cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_LT(pcrel_disp, 0x100000); 462cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_GE(pcrel_disp, -0x100000); 463cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_EQ(pcrel_disp & 0x3, 0); 464cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5); 465cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); 466cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 467cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 468cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko void TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn, 469cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t sprel_disp_in_load_units, 470cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adrp_offset, 471cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko bool has_thunk, 472cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t string_offset) { 473cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko ASSERT_LT(sprel_disp_in_load_units, 0x1000u); 474cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10); 475cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); 476cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 477f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 478f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) { 479f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( 480f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); 4810a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); 4820a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key); 483f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 484f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 48566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) { 48666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( 48766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); 4880a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); 4890a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key); 49066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko } 49166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 492f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) { 493f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( 494f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)); 4950a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); 4960a51fc30486bff07e6752c73edbefe168d1d9e7bVladimir Marko return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key); 497f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 498f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 499f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t GetOutputInsn(uint32_t offset) { 500f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko CHECK_LE(offset, output_.size()); 501f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko CHECK_GE(output_.size() - offset, 4u); 502f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko return (static_cast<uint32_t>(output_[offset]) << 0) | 503f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (static_cast<uint32_t>(output_[offset + 1]) << 8) | 504f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (static_cast<uint32_t>(output_[offset + 2]) << 16) | 505f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (static_cast<uint32_t>(output_[offset + 3]) << 24); 506f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 507f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 50866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko void TestBakerField(uint32_t offset, uint32_t ref_reg); 5093f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko}; 5103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5113f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoconst uint8_t Arm64RelativePatcherTest::kCallRawCode[] = { 5123f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 0x00, 0x00, 0x00, 0x94 5133f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko}; 5143f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5153f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoconst ArrayRef<const uint8_t> Arm64RelativePatcherTest::kCallCode(kCallRawCode); 5163f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5173f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoconst uint8_t Arm64RelativePatcherTest::kNopRawCode[] = { 5183f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 0x1f, 0x20, 0x03, 0xd5 5193f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko}; 5203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5213f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoconst ArrayRef<const uint8_t> Arm64RelativePatcherTest::kNopCode(kNopRawCode); 5223f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5233f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoclass Arm64RelativePatcherTestDefault : public Arm64RelativePatcherTest { 5243f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko public: 5253f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Arm64RelativePatcherTestDefault() : Arm64RelativePatcherTest("default") { } 5263f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko}; 5273f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5283f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Markoclass Arm64RelativePatcherTestDenver64 : public Arm64RelativePatcherTest { 5293f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko public: 5303f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Arm64RelativePatcherTestDenver64() : Arm64RelativePatcherTest("denver64") { } 5313f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko}; 5323f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5333f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallSelf) { 534f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 5353f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(0u, nullptr, 1u), 5363f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 537b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 5383f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 5393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 540f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code = RawCode({kBlPlus0}); 5413f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 5423f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 5433f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5443f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallOther) { 545f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch method1_patches[] = { 5463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(0u, nullptr, 2u), 5473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 548b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches)); 549f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch method2_patches[] = { 5503f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(0u, nullptr, 1u), 5513f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 552b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches)); 5533f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 5543f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5553f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 5563f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method2_offset = GetMethodOffset(2u); 5573f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t diff_after = method2_offset - method1_offset; 558cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(diff_after, 4u); 5593f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_LT(diff_after >> 2, 1u << 8); // Simple encoding, (diff_after >> 2) fits into 8 bits. 560f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> method1_expected_code = RawCode({kBlPlus0 + (diff_after >> 2)}); 5613f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code))); 5623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t diff_before = method1_offset - method2_offset; 563cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(diff_before, 4u); 5643f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_GE(diff_before, -1u << 27); 5653f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto method2_expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff_before >> 2) & 0x03ffffffu)); 5663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code))); 5673f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 5683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5693f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) { 570f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 5713f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(0u, nullptr, 2u), 5723f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 573b207e1473dda1730604a28db2b4fa52f2998aeaeVladimir Marko AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); 5743f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko Link(); 5753f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 5763f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 5773f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t diff = kTrampolineOffset - method1_offset; 5783f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(diff & 1u, 0u); 5793f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_GE(diff, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned). 5803f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff >> 2) & 0x03ffffffu)); 5813f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 5823f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 5833f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 584d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) { 585d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko constexpr uint32_t missing_method_index = 1024u; 586d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0); 587d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs. 588d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ArrayRef<const uint8_t> last_method_code(last_method_raw_code); 589d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size()); 590f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch last_method_patches[] = { 591d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index), 592d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko }; 593d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko 594d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4; 595d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko uint32_t last_method_idx = Create2MethodsWithGap( 596d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko kNopCode, ArrayRef<const LinkerPatch>(), last_method_code, 597d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ArrayRef<const LinkerPatch>(last_method_patches), 598d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko just_over_max_negative_disp - bl_offset_in_last_method); 599d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 600d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko uint32_t last_method_offset = GetMethodOffset(last_method_idx); 601d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ASSERT_EQ(method1_offset, 602d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp); 603d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first); 604d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko 605d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko // Check linked code. 606d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko uint32_t thunk_offset = 60733bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64); 608d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method); 609cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(diff, 4u); 610d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ASSERT_LT(diff, 128 * MB); 611d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2)); 612d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx), 613d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko ArrayRef<const uint8_t>(expected_code))); 614d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko EXPECT_TRUE(CheckThunk(thunk_offset)); 615d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko} 616d1eaf0dc9abc42dbcbbd9c4b98bf930ae5f394f3Vladimir Marko 6173f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) { 6183f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0); 6193f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t bl_offset_in_method1 = 1u * 4u; // After NOPs. 6203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> method1_code(method1_raw_code); 6213f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size()); 6223f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap(). 623f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch method1_patches[] = { 6243f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx), 6253f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 6263f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6273f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t max_positive_disp = 128 * MB - 4u; 628345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko uint32_t last_method_idx = Create2MethodsWithGap(method1_code, 629345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(method1_patches), 630345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko kNopCode, 631345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(), 6323f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko bl_offset_in_method1 + max_positive_disp); 6333f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(expected_last_method_idx, last_method_idx); 6343f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6353f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 6363f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_offset = GetMethodOffset(last_method_idx); 6373f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(method1_offset + bl_offset_in_method1 + max_positive_disp, last_method_offset); 6383f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6393f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Check linked code. 6403f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndBl(1u, kBlPlusMax); 6413f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 6423f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 6433f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6443f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarBefore) { 6453f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto last_method_raw_code = GenNopsAndBl(0u, kBlPlus0); 6463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t bl_offset_in_last_method = 0u * 4u; // After NOPs. 6473f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> last_method_code(last_method_raw_code); 6483f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size()); 649f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch last_method_patches[] = { 6503f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u), 6513f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 6523f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6533f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t max_negative_disp = 128 * MB; 654345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko uint32_t last_method_idx = Create2MethodsWithGap(kNopCode, 655345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(), 656345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko last_method_code, 657345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(last_method_patches), 6583f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko max_negative_disp - bl_offset_in_last_method); 6593f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 6603f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_offset = GetMethodOffset(last_method_idx); 6613f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(method1_offset, last_method_offset + bl_offset_in_last_method - max_negative_disp); 6623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6633f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Check linked code. 6643f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndBl(0u, kBlMinusMax); 6653f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx), 6663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t>(expected_code))); 6673f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 6683f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6693f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarAfter) { 6703f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto method1_raw_code = GenNopsAndBl(0u, kBlPlus0); 6713f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t bl_offset_in_method1 = 0u * 4u; // After NOPs. 6723f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> method1_code(method1_raw_code); 6733f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size()); 6743f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap(). 675f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch method1_patches[] = { 6763f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx), 6773f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 6783f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6793f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t just_over_max_positive_disp = 128 * MB; 6803f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_idx = Create2MethodsWithGap( 681345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko method1_code, 682345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(method1_patches), 683345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko kNopCode, 684345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(), 6853f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko bl_offset_in_method1 + just_over_max_positive_disp); 6863f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(expected_last_method_idx, last_method_idx); 6873f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 6883f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 6893f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_offset = GetMethodOffset(last_method_idx); 6900c737df58fd4796df6c79837f409c40595168db5Vladimir Marko ASSERT_TRUE(IsAligned<kArm64Alignment>(last_method_offset)); 6913f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_header_offset = last_method_offset - sizeof(OatQuickMethodHeader); 692f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t thunk_size = MethodCallThunkSize(); 69333bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko uint32_t thunk_offset = RoundDown(last_method_header_offset - thunk_size, kArm64Alignment); 694f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size), 6950c737df58fd4796df6c79837f409c40595168db5Vladimir Marko last_method_header_offset); 6963f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1); 697cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(diff, 4u); 6983f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_LT(diff, 128 * MB); 6993f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2)); 7003f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); 7013f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko CheckThunk(thunk_offset); 7023f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7033f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7043f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) { 7053f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0); 7063f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs. 7073f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t> last_method_code(last_method_raw_code); 7083f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size()); 709f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch last_method_patches[] = { 7103f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u), 7113f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko }; 7123f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7133f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4; 7143f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_idx = Create2MethodsWithGap( 715345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko kNopCode, ArrayRef<const LinkerPatch>(), last_method_code, 716345f93e8533e5b5af1e567d1c9d2e21f96c69258Vladimir Marko ArrayRef<const LinkerPatch>(last_method_patches), 7173f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko just_over_max_negative_disp - bl_offset_in_last_method); 7183f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t method1_offset = GetMethodOffset(1u); 7193f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t last_method_offset = GetMethodOffset(last_method_idx); 7203f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_EQ(method1_offset, 7213f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp); 7223f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7233f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko // Check linked code. 7243f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t thunk_offset = 72533bff25bcd7a02d35c54f63740eadb1a4833fc92Vladimir Marko CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64); 7263f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method); 727cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko CHECK_ALIGNED(diff, 4u); 7283f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ASSERT_LT(diff, 128 * MB); 7293f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2)); 7303f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx), 7313f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko ArrayRef<const uint8_t>(expected_code))); 7323f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko EXPECT_TRUE(CheckThunk(thunk_offset)); 7333f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7343f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7355f07820bd9df33d68faf4a501b681b9a7913d458Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringBssEntry1) { 7363f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko TestNopsAdrpLdr(0u, 0x12345678u, 0x1234u); 7373f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7383f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7395f07820bd9df33d68faf4a501b681b9a7913d458Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringBssEntry2) { 7403f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko TestNopsAdrpLdr(0u, -0x12345678u, 0x4444u); 7413f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7423f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7435f07820bd9df33d68faf4a501b681b9a7913d458Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringBssEntry3) { 7443f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko TestNopsAdrpLdr(0u, 0x12345000u, 0x3ffcu); 7453f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7463f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 7475f07820bd9df33d68faf4a501b681b9a7913d458Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringBssEntry4) { 7483f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u); 7493f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7503f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 751cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringReference1) { 752cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpAdd(0u, 0x12345678u); 7533f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7543f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 755cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringReference2) { 756cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpAdd(0u, -0x12345678u); 7573f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7583f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 759cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringReference3) { 760cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpAdd(0u, 0x12345000u); 7613f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7623f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 763cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, StringReference4) { 764cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestNopsAdrpAdd(0u, 0x12345ffcu); 7653f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} 7663f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 767cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define TEST_FOR_OFFSETS(test, disp1, disp2) \ 768cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \ 769cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2) 7703f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 771cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \ 7725f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## Ldur ## disp) { \ 7731a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ 774cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \ 775cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 7763f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 777cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238) 7783f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 779cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \ 7805f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntry ## adrp_offset ## Ldur ## disp) { \ 781cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \ 782cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 7833f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko 784cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238) 78597e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 78697e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin// LDR <Wt>, <label> is always aligned. We should never have to use a fixup. 787cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \ 7885f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WPcRel ## disp) { \ 78997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \ 79097e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 79197e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 792cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238) 79397e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 79497e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8. 795cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \ 7965f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XPcRel ## disp) { \ 7971a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \ 7981a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \ 79997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \ 80097e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 80197e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 802cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238) 80397e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 80497e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed. 805cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \ 8065f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WSpRel ## disp) { \ 8071a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \ 80897e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 80997e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 810cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4) 81197e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 812cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \ 8135f07820bd9df33d68faf4a501b681b9a7913d458Vladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XSpRel ## disp) { \ 8141a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \ 81597e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin } 81697e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 817cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8) 818cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 819cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \ 820cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \ 8211a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ 822cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \ 823cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 824cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 825cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840) 826cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 827cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \ 828cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \ 829cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdurAdd(adrp_offset, false, disp); \ 830cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 831cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 832cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840) 833cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 834cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \ 835cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \ 836cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko /* SUB unrelated to "ADRP x0, addr". */ \ 837cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ \ 838cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(sub, adrp_offset, false, disp); \ 839cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 840cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 841cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840) 842cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 843cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \ 844cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \ 845cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko /* SUBS that uses the result of "ADRP x0, addr". */ \ 846cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ \ 847cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(subs, adrp_offset, false, disp); \ 848cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 849cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 850cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840) 851cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 852cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \ 853cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \ 854cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \ 855cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ \ 856cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(add, adrp_offset, false, disp); \ 857cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 858cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 859cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840) 860cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 861cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \ 862cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \ 863cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \ 864cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ \ 8651a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ 866cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \ 867cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 868cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 869cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840) 870cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 871cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko// LDR <Wt>, <label> is always aligned. We should never have to use a fixup. 872cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \ 873cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \ 874cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \ 875cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 876cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 877cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238) 878cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 879cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8. 880cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \ 881cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \ 8821a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \ 8831a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \ 884cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \ 885cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 886cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 887cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238) 888cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 889cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed. 890cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \ 891cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \ 8921a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u); \ 893cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 894cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 895cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4) 896cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 897cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko#define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \ 898cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \ 8991a0de6acd03b43a03678b58f47b2f21b5215cc22Chih-Hung Hsieh TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u); \ 900cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko } 901cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir Marko 902cac5a7e871f1f346b317894359ad06fa7bd67fbaVladimir MarkoTEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8) 90397e2f26524f4d08796a0a224b17e082734cceb5fMatteo Franchin 90466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Markovoid Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) { 905f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t valid_regs[] = { 906f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 907f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved. 908f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 909f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // LR and SP/ZR are reserved. 910f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 911f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DCHECK_ALIGNED(offset, 4u); 912f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DCHECK_LT(offset, 16 * KB); 913f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kMethodCodeSize = 8u; 914f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kLiteralOffset = 0u; 915f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t method_idx = 0u; 916f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t base_reg : valid_regs) { 917f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t holder_reg : valid_regs) { 91866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg; 919f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr}); 920f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(kMethodCodeSize, raw_code.size()); 921f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code(raw_code); 922f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data = 923f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); 924f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 925f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), 926f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 927f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 928f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches)); 929f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 930f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 931f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 932f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 933f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // All thunks are at the end. 934f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); 935f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko method_idx = 0u; 936f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t base_reg : valid_regs) { 937f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t holder_reg : valid_regs) { 938f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 939f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset); 940f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2)); 94166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg; 942f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr}); 943f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(kMethodCodeSize, expected_code.size()); 944f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE( 945f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code))); 946f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 947f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> expected_thunk = CompileBakerOffsetThunk(base_reg, holder_reg); 948f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GT(output_.size(), thunk_offset); 949f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size()); 950f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset, 951f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko expected_thunk.size()); 952f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) { 953f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk); 954f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(false); 955f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 956f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 957f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t gray_check_offset = thunk_offset; 958f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko if (holder_reg == base_reg) { 959f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Verify that the null-check CBZ uses the correct register, i.e. holder_reg. 960f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GE(output_.size() - gray_check_offset, 4u); 96166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_EQ(0x34000000u | holder_reg, GetOutputInsn(thunk_offset) & 0xff00001fu); 962f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko gray_check_offset +=4u; 963f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 964f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Verify that the lock word for gray bit check is loaded from the holder address. 965f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static constexpr size_t kGrayCheckInsns = 5; 966f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GE(output_.size() - gray_check_offset, 4u * kGrayCheckInsns); 967f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t load_lock_word = 968f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko kLdrWInsn | 969f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (mirror::Object::MonitorOffset().Uint32Value() << (10 - 2)) | 970f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (holder_reg << 5) | 971f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko /* ip0 */ 16; 972f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko EXPECT_EQ(load_lock_word, GetOutputInsn(gray_check_offset)); 973f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Verify the gray bit check. 97466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const uint32_t check_gray_bit_without_offset = 97566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16; 97666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(gray_check_offset + 4u) & 0xfff8001fu); 977f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Verify the fake dependency. 978f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t fake_dependency = 97966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0x8b408000u | // ADD Xd, Xn, Xm, LSR 32 980f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (/* ip0 */ 16 << 16) | // Xm = ip0 981f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko (base_reg << 5) | // Xn = base_reg 982f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko base_reg; // Xd = base_reg 983f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko EXPECT_EQ(fake_dependency, GetOutputInsn(gray_check_offset + 12u)); 984f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Do not check the rest of the implementation. 985f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 986f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // The next thunk follows on the next aligned offset. 987f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); 988f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 989f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 990f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 991f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 99266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko#define TEST_BAKER_FIELD(offset, ref_reg) \ 993f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko TEST_F(Arm64RelativePatcherTestDefault, \ 99466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko BakerOffset##offset##_##ref_reg) { \ 99566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko TestBakerField(offset, ref_reg); \ 996f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 997f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 99866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir MarkoTEST_BAKER_FIELD(/* offset */ 0, /* ref_reg */ 0) 99966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir MarkoTEST_BAKER_FIELD(/* offset */ 8, /* ref_reg */ 15) 100066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir MarkoTEST_BAKER_FIELD(/* offset */ 0x3ffc, /* ref_reg */ 29) 1001f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1002f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { 1003f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // One thunk in the middle with maximum distance branches to it from both sides. 100466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`. 1005f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kLiteralOffset1 = 4; 1006f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); 1007f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code1(raw_code1); 1008f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data = 1009f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); 1010f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches1[] = { 1011f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), 1012f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1013f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1)); 1014f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1015f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 1016f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // allows the branch to reach that thunk. 1017f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler1_size = 1018f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); 1019f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); 1020f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> filler1_code(raw_filler1_code); 1021f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(2u), filler1_code); 1022f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1023f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Enforce thunk reservation with a tiny method. 1024f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(3u), kNopCode); 1025f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1026f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Allow reaching the thunk from the very beginning of a method 1MiB away. Backward branch 1027f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // reaches the full 1MiB. Things to subtract: 1028f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - thunk size and method 3 pre-header, rounded up (padding in between if needed) 1029f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - method 3 code and method 4 pre-header, rounded up (padding in between if needed) 1030f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - method 4 header (let there be no padding between method 4 code and method 5 pre-header). 1031f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size(); 1032f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler2_size = 1033f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment) 1034f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) 1035f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko - sizeof(OatQuickMethodHeader); 1036f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u); 1037f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> filler2_code(raw_filler2_code); 1038f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(4u), filler2_code); 1039f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1040f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kLiteralOffset2 = 0; 1041f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code2 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn}); 1042f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code2(raw_code2); 1043f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches2[] = { 1044f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data), 1045f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1046f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2)); 1047f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1048f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 1049f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1050f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t first_method_offset = GetMethodOffset(1u); 1051f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t last_method_offset = GetMethodOffset(5u); 1052f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko EXPECT_EQ(2 * MB, last_method_offset - first_method_offset); 1053f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1054f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0; 1055f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_max_backward = kCbnzIP1Plus0Insn | 0x00800000; 1056f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn}); 1057f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code2 = RawCode({cbnz_max_backward, kLdrWInsn}); 1058f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1))); 1059f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2))); 1060f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 1061f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1062f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { 1063f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Based on the first part of BakerOffsetThunkInTheMiddle but the CBNZ is one instruction 1064f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // earlier, so the thunk is emitted before the filler. 106566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`. 1066f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kLiteralOffset1 = 0; 1067f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn}); 1068f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code1(raw_code1); 1069f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data = 1070f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); 1071f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches1[] = { 1072f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), 1073f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1074f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1)); 1075f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1076f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 1077f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // allows the branch to reach that thunk. 1078f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler1_size = 1079f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); 1080f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); 1081f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> filler1_code(raw_filler1_code); 1082f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(2u), filler1_code); 1083f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1084f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 1085f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1086f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1; 1087f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2)); 1088f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn}); 1089f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1))); 1090f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 1091f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1092f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFromLast) { 1093f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Based on the BakerOffsetThunkInTheMiddle but the CBNZ in the last method is preceded 1094f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end. 109566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`. 1096f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kLiteralOffset1 = 4; 1097f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); 1098f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code1(raw_code1); 1099f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data = 1100f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); 1101f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches1[] = { 1102f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), 1103f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1104f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1)); 1105f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1106f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 1107f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // allows the branch to reach that thunk. 1108f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler1_size = 1109f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); 1110f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); 1111f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> filler1_code(raw_filler1_code); 1112f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(2u), filler1_code); 1113f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1114f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Enforce thunk reservation with a tiny method. 1115f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(3u), kNopCode); 1116f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1117f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // If not for the extra NOP, this would allow reaching the thunk from the very beginning 1118f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // of a method 1MiB away. Backward branch reaches the full 1MiB. Things to subtract: 1119f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - thunk size and method 3 pre-header, rounded up (padding in between if needed) 1120f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - method 3 code and method 4 pre-header, rounded up (padding in between if needed) 1121f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // - method 4 header (let there be no padding between method 4 code and method 5 pre-header). 1122f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size(); 1123f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler2_size = 1124f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment) 1125f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) 1126f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko - sizeof(OatQuickMethodHeader); 1127f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u); 1128f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> filler2_code(raw_filler2_code); 1129f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(4u), filler2_code); 1130f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1131f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Extra NOP compared to BakerOffsetThunkInTheMiddle. 1132f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kLiteralOffset2 = 4; 1133f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code2 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); 1134f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code2(raw_code2); 1135f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches2[] = { 1136f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data), 1137f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1138f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2)); 1139f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1140f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 1141f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1142f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0; 1143f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2; 1144f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2)); 1145f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn}); 1146f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn}); 1147f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1))); 1148f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2))); 1149f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 1150f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 115166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerArray) { 115266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t valid_regs[] = { 115366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 115466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved. 115566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 115666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // LR and SP/ZR are reserved. 115766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko }; 115866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko auto ldr = [](uint32_t base_reg) { 115966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t index_reg = (base_reg == 0u) ? 1u : 0u; 116066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t ref_reg = (base_reg == 2) ? 3u : 2u; 116166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko return kLdrWLsl2Insn | (index_reg << 16) | (base_reg << 5) | ref_reg; 116266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko }; 116366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko constexpr size_t kMethodCodeSize = 8u; 116466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko constexpr size_t kLiteralOffset = 0u; 116566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t method_idx = 0u; 116666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko for (uint32_t base_reg : valid_regs) { 116766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ++method_idx; 116866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr(base_reg)}); 116966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_EQ(kMethodCodeSize, raw_code.size()); 117066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ArrayRef<const uint8_t> code(raw_code); 117166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const LinkerPatch patches[] = { 117266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko LinkerPatch::BakerReadBarrierBranchPatch( 117366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), 117466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko }; 117566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches)); 117666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko } 117766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko Link(); 117866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 117966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // All thunks are at the end. 118066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); 118166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko method_idx = 0u; 118266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko for (uint32_t base_reg : valid_regs) { 118366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ++method_idx; 118466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset); 118566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2)); 118666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr(base_reg)}); 118766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_EQ(kMethodCodeSize, expected_code.size()); 118866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code))); 118966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 119066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko std::vector<uint8_t> expected_thunk = CompileBakerArrayThunk(base_reg); 119166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_GT(output_.size(), thunk_offset); 119266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size()); 119366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset, 119466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko expected_thunk.size()); 119566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) { 119666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk); 119766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_TRUE(false); 119866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko } 119966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 120066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Verify that the lock word for gray bit check is loaded from the correct address 120166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // before the base_reg which points to the array data. 120266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko static constexpr size_t kGrayCheckInsns = 5; 120366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_GE(output_.size() - thunk_offset, 4u * kGrayCheckInsns); 120466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko int32_t data_offset = 120566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); 120666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko int32_t offset = mirror::Object::MonitorOffset().Int32Value() - data_offset; 120766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_LT(offset, 0); 120866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const uint32_t load_lock_word = 120966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko kLdurWInsn | 121066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ((offset & 0x1ffu) << 12) | 121166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko (base_reg << 5) | 121266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko /* ip0 */ 16; 121366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko EXPECT_EQ(load_lock_word, GetOutputInsn(thunk_offset)); 121466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Verify the gray bit check. 121566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const uint32_t check_gray_bit_without_offset = 121666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16; 121766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(thunk_offset + 4u) & 0xfff8001fu); 121866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Verify the fake dependency. 121966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko const uint32_t fake_dependency = 122066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 0x8b408000u | // ADD Xd, Xn, Xm, LSR 32 122166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko (/* ip0 */ 16 << 16) | // Xm = ip0 122266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko (base_reg << 5) | // Xn = base_reg 122366d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko base_reg; // Xd = base_reg 122466d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko EXPECT_EQ(fake_dependency, GetOutputInsn(thunk_offset + 12u)); 122566d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // Do not check the rest of the implementation. 122666d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 122766d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko // The next thunk follows on the next aligned offset. 122866d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); 122966d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko } 123066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko} 123166d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko 123266d691de219e840b3f84385d8bd1b7001562b0e5Vladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { 1233f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t valid_regs[] = { 1234f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1235f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved. 1236f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 1237f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // LR and SP/ZR are reserved. 1238f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1239f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kMethodCodeSize = 8u; 1240f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kLiteralOffset = 4u; 1241f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t method_idx = 0u; 1242f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t root_reg : valid_regs) { 1243f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1244f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg; 1245f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code = RawCode({ldr, kCbnzIP1Plus0Insn}); 1246f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(kMethodCodeSize, raw_code.size()); 1247f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code(raw_code); 1248f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch patches[] = { 1249f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch( 1250f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)), 1251f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1252f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches)); 1253f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 1254f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 1255f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1256f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // All thunks are at the end. 1257f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); 1258f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko method_idx = 0u; 1259f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t root_reg : valid_regs) { 1260f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1261f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset); 1262f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2)); 1263f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg; 1264f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> expected_code = RawCode({ldr, cbnz}); 1265f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(kMethodCodeSize, expected_code.size()); 1266f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code))); 1267f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1268f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg); 1269f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GT(output_.size(), thunk_offset); 1270f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size()); 1271f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset, 1272f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko expected_thunk.size()); 1273f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) { 1274f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk); 1275f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_TRUE(false); 1276f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 1277f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1278f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg. 1279f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_GE(output_.size() - thunk_offset, 4u); 128066d691de219e840b3f84385d8bd1b7001562b0e5Vladimir Marko ASSERT_EQ(0x34000000u | root_reg, GetOutputInsn(thunk_offset) & 0xff00001fu); 1281f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Do not check the rest of the implementation. 1282f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1283f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // The next thunk follows on the next aligned offset. 1284f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); 1285f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 1286f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 1287f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1288f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir MarkoTEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { 1289f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());` 1290f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily 1291f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // hold when we're reserving thunks of different sizes. This test exposes the situation 1292f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // by using Baker thunks and a method call thunk. 1293f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1294f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Add a method call patch that can reach to method 1 offset + 128MiB. 1295f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t method_idx = 0u; 1296f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kMethodCallLiteralOffset = 4u; 1297f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr uint32_t kMissingMethodIdx = 2u; 1298f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0}); 1299f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch method1_patches[] = { 1300f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u), 1301f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1302f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t> code1(raw_code1); 1303f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1304f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches)); 1305f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1306f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Skip kMissingMethodIdx. 1307f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1308f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(kMissingMethodIdx, method_idx); 1309f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Add a method with the right size that the method code for the next one starts 1MiB 1310f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // after code for method 1. 1311f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko size_t filler_size = 1312f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) 1313f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko - sizeof(OatQuickMethodHeader); 1314f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko std::vector<uint8_t> filler_code = GenNops(filler_size / 4u); 1315f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1316f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code)); 1317f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Add 126 methods with 1MiB code+header, making the code for the next method start 1MiB 1318f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // before the currently scheduled MaxNextOffset() for the method call thunk. 1319f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko for (uint32_t i = 0; i != 126; ++i) { 1320f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko filler_size = 1 * MB - sizeof(OatQuickMethodHeader); 1321f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko filler_code = GenNops(filler_size / 4u); 1322f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1323f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code)); 1324f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko } 1325f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1326f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Add 2 Baker GC root patches to the last method, one that would allow the thunk at 1327f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the 1328f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // second that needs it kArm64Alignment after that. Given the size of the GC root thunk 1329f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // is more than the space required by the method call thunk plus kArm64Alignment, 1330f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // this pushes the first GC root thunk's pending MaxNextOffset() before the method call 1331f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // thunk's pending MaxNextOffset() which needs to be adjusted. 1332f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment, 1333f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko CompileBakerGcRootThunk(/* root_reg */ 0).size()); 1334f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16"); 1335f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment; 1336f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment; 1337f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`. 1338f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1; 1339f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2; 1340f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const std::vector<uint8_t> last_method_raw_code = RawCode({ 1341f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko kNopInsn, kNopInsn, kNopInsn, kNopInsn, // Padding before first GC root read barrier. 1342f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ldr1, kCbnzIP1Plus0Insn, // First GC root LDR with read barrier. 1343f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko kNopInsn, kNopInsn, // Padding before second GC root read barrier. 1344f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier. 1345f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }); 1346f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); 1347f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); 1348f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko const LinkerPatch last_method_patches[] = { 1349f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), 1350f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), 1351f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko }; 1352f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ++method_idx; 1353f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko AddCompiledMethod(MethodRef(method_idx), 1354f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const uint8_t>(last_method_raw_code), 1355f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ArrayRef<const LinkerPatch>(last_method_patches)); 1356f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1357f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko // The main purpose of the test is to check that Link() does not cause a crash. 1358f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko Link(); 1359f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 1360f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko ASSERT_EQ(127 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u)); 1361f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko} 1362f4f2daafb38c9c07ea74044a0fb89a2a19288b7aVladimir Marko 13633f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} // namespace linker 13643f311cfa86af18ccbd6f1607f037401244ad4d56Vladimir Marko} // namespace art 1365