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