Refactor RelativePatcher out of OatWriter.

Move the relative patcher classes to compiler/linker/ and
compiler/linker/<arch>/ . Refactor them to avoid OatWriter
dependency so that they can be unit tested. Add tests for
x86 and x86-64.

Change-Id: I1b42baa9fc431378e4cce1399bec590c5b5a409f
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
new file mode 100644
index 0000000..ecbbd09
--- /dev/null
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "linker/arm/relative_patcher_arm_base.h"
+
+#include "compiled_method.h"
+#include "oat.h"
+#include "output_stream.h"
+
+namespace art {
+namespace linker {
+
+uint32_t ArmBaseRelativePatcher::ReserveSpace(uint32_t offset,
+                                              const CompiledMethod* compiled_method) {
+  return ReserveSpaceInternal(offset, compiled_method, 0u);
+}
+
+uint32_t ArmBaseRelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
+  if (current_thunk_to_write_ == thunk_locations_.size()) {
+    return offset;
+  }
+  uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
+  if (UNLIKELY(aligned_offset == thunk_locations_[current_thunk_to_write_])) {
+    ++current_thunk_to_write_;
+    uint32_t aligned_code_delta = aligned_offset - offset;
+    if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
+      return 0u;
+    }
+    if (UNLIKELY(!WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk_code_)))) {
+      return 0u;
+    }
+    uint32_t thunk_end_offset = aligned_offset + thunk_code_.size();
+    // Align after writing chunk, see the ReserveSpace() above.
+    offset = CompiledMethod::AlignCode(thunk_end_offset, instruction_set_);
+    aligned_code_delta = offset - thunk_end_offset;
+    if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
+      return 0u;
+    }
+  }
+  return offset;
+}
+
+ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
+                                               InstructionSet instruction_set,
+                                               std::vector<uint8_t> thunk_code,
+                                               uint32_t max_positive_displacement,
+                                               uint32_t max_negative_displacement)
+    : provider_(provider), instruction_set_(instruction_set), thunk_code_(thunk_code),
+      max_positive_displacement_(max_positive_displacement),
+      max_negative_displacement_(max_negative_displacement),
+      thunk_locations_(), current_thunk_to_write_(0u), unprocessed_patches_() {
+}
+
+uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
+                                                      const CompiledMethod* compiled_method,
+                                                      uint32_t max_extra_space) {
+  // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
+  // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
+  // of code. To avoid any alignment discrepancies for the final chunk, we always align the
+  // offset after reserving of writing any chunk.
+  if (UNLIKELY(compiled_method == nullptr)) {
+    uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
+    bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+    if (needs_thunk) {
+      thunk_locations_.push_back(aligned_offset);
+      offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
+    }
+    return offset;
+  }
+  DCHECK(compiled_method->GetQuickCode() != nullptr);
+  uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+  uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+  uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
+  // Adjust for extra space required by the subclass.
+  next_aligned_offset = compiled_method->AlignCode(next_aligned_offset + max_extra_space);
+  if (!unprocessed_patches_.empty() &&
+      next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
+    bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
+    if (needs_thunk) {
+      // A single thunk will cover all pending patches.
+      unprocessed_patches_.clear();
+      uint32_t thunk_location = compiled_method->AlignCode(offset);
+      thunk_locations_.push_back(thunk_location);
+      offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
+    }
+  }
+  for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+    if (patch.Type() == kLinkerPatchCallRelative) {
+      unprocessed_patches_.emplace_back(patch.TargetMethod(),
+                                        quick_code_offset + patch.LiteralOffset());
+    }
+  }
+  return offset;
+}
+
+uint32_t ArmBaseRelativePatcher::CalculateDisplacement(uint32_t patch_offset,
+                                                       uint32_t target_offset) {
+  // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+  uint32_t displacement = target_offset - patch_offset;
+  // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
+  if (displacement > max_positive_displacement_ && displacement < -max_negative_displacement_) {
+    // Unwritten thunks have higher offsets, check if it's within range.
+    DCHECK(current_thunk_to_write_ == thunk_locations_.size() ||
+           thunk_locations_[current_thunk_to_write_] > patch_offset);
+    if (current_thunk_to_write_ != thunk_locations_.size() &&
+        thunk_locations_[current_thunk_to_write_] - patch_offset < max_positive_displacement_) {
+      displacement = thunk_locations_[current_thunk_to_write_] - patch_offset;
+    } else {
+      // We must have a previous thunk then.
+      DCHECK_NE(current_thunk_to_write_, 0u);
+      DCHECK_LT(thunk_locations_[current_thunk_to_write_ - 1], patch_offset);
+      displacement = thunk_locations_[current_thunk_to_write_ - 1] - patch_offset;
+      DCHECK(displacement >= -max_negative_displacement_);
+    }
+  }
+  return displacement;
+}
+
+bool ArmBaseRelativePatcher::ReserveSpaceProcessPatches(uint32_t next_aligned_offset) {
+  // Process as many patches as possible, stop only on unresolved targets or calls too far back.
+  while (!unprocessed_patches_.empty()) {
+    uint32_t patch_offset = unprocessed_patches_.front().second;
+    auto result = provider_->FindMethodOffset(unprocessed_patches_.front().first);
+    if (!result.first) {
+      // If still unresolved, check if we have a thunk within range.
+      DCHECK(thunk_locations_.empty() || thunk_locations_.back() <= patch_offset);
+      if (thunk_locations_.empty() ||
+          patch_offset - thunk_locations_.back() > max_negative_displacement_) {
+        return next_aligned_offset - patch_offset > max_positive_displacement_;
+      }
+    } else if (result.second >= patch_offset) {
+      DCHECK_LE(result.second - patch_offset, max_positive_displacement_);
+    } else {
+      // When calling back, check if we have a thunk that's closer than the actual target.
+      uint32_t target_offset =
+          (thunk_locations_.empty() || result.second > thunk_locations_.back())
+          ? result.second
+          : thunk_locations_.back();
+      DCHECK_GT(patch_offset, target_offset);
+      if (patch_offset - target_offset > max_negative_displacement_) {
+        return true;
+      }
+    }
+    unprocessed_patches_.pop_front();
+  }
+  return false;
+}
+
+}  // namespace linker
+}  // namespace art
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
new file mode 100644
index 0000000..a88d25b
--- /dev/null
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
+#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
+
+#include <deque>
+
+#include "linker/relative_patcher.h"
+#include "method_reference.h"
+
+namespace art {
+namespace linker {
+
+class ArmBaseRelativePatcher : public RelativePatcher {
+ public:
+  uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE;
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
+
+ protected:
+  ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
+                         InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
+                         uint32_t max_positive_displacement, uint32_t max_negative_displacement);
+
+  uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method,
+                                uint32_t max_extra_space);
+  uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset);
+
+ private:
+  bool ReserveSpaceProcessPatches(uint32_t next_aligned_offset);
+
+  RelativePatcherTargetProvider* const provider_;
+  const InstructionSet instruction_set_;
+  const std::vector<uint8_t> thunk_code_;
+  const uint32_t max_positive_displacement_;
+  const uint32_t max_negative_displacement_;
+  std::vector<uint32_t> thunk_locations_;
+  size_t current_thunk_to_write_;
+
+  // ReserveSpace() tracks unprocessed patches.
+  typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
+  std::deque<UnprocessedPatch> unprocessed_patches_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
+};
+
+}  // namespace linker
+}  // namespace art
+
+#endif  // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
new file mode 100644
index 0000000..4267743
--- /dev/null
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "linker/arm/relative_patcher_thumb2.h"
+
+#include "compiled_method.h"
+#include "mirror/art_method.h"
+#include "utils/arm/assembler_thumb2.h"
+
+namespace art {
+namespace linker {
+
+Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider)
+    : ArmBaseRelativePatcher(provider, kThumb2, CompileThunkCode(),
+                             kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+}
+
+void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                                      uint32_t patch_offset, uint32_t target_offset) {
+  DCHECK_LE(literal_offset + 4u, code->size());
+  DCHECK_EQ(literal_offset & 1u, 0u);
+  DCHECK_EQ(patch_offset & 1u, 0u);
+  DCHECK_EQ(target_offset & 1u, 1u);  // Thumb2 mode bit.
+  uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
+  displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
+  DCHECK_EQ(displacement & 1u, 0u);
+  DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u);  // 25-bit signed.
+  uint32_t signbit = (displacement >> 31) & 0x1;
+  uint32_t i1 = (displacement >> 23) & 0x1;
+  uint32_t i2 = (displacement >> 22) & 0x1;
+  uint32_t imm10 = (displacement >> 12) & 0x03ff;
+  uint32_t imm11 = (displacement >> 1) & 0x07ff;
+  uint32_t j1 = i1 ^ (signbit ^ 1);
+  uint32_t j2 = i2 ^ (signbit ^ 1);
+  uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
+  value |= 0xf000d000;  // BL
+
+  uint8_t* addr = &(*code)[literal_offset];
+  // Check that we're just overwriting an existing BL.
+  DCHECK_EQ(addr[1] & 0xf8, 0xf0);
+  DCHECK_EQ(addr[3] & 0xd0, 0xd0);
+  // Write the new BL.
+  addr[0] = (value >> 16) & 0xff;
+  addr[1] = (value >> 24) & 0xff;
+  addr[2] = (value >> 0) & 0xff;
+  addr[3] = (value >> 8) & 0xff;
+}
+
+void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                   const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                   uint32_t patch_offset ATTRIBUTE_UNUSED,
+                                                   uint32_t target_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "Unexpected relative dex cache array patch.";
+}
+
+std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() {
+  // The thunk just uses the entry point in the ArtMethod. This works even for calls
+  // to the generic JNI and interpreter trampolines.
+  arm::Thumb2Assembler assembler;
+  assembler.LoadFromOffset(
+      arm::kLoadWord, arm::PC, arm::R0,
+      mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
+  assembler.bkpt(0);
+  std::vector<uint8_t> thunk_code(assembler.CodeSize());
+  MemoryRegion code(thunk_code.data(), thunk_code.size());
+  assembler.FinalizeInstructions(code);
+  return thunk_code;
+}
+
+}  // namespace linker
+}  // namespace art
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
new file mode 100644
index 0000000..5611303
--- /dev/null
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_
+#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_
+
+#include "linker/arm/relative_patcher_arm_base.h"
+
+namespace art {
+namespace linker {
+
+class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
+ public:
+  explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider);
+
+  void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                 uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+  void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
+                              uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+
+ private:
+  static std::vector<uint8_t> CompileThunkCode();
+
+  // PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
+  static constexpr int32_t kPcDisplacement = 4;
+
+  // Maximum positive and negative displacement measured from the patch location.
+  // (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
+  // the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
+  static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
+  static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
+
+  DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher);
+};
+
+}  // namespace linker
+}  // namespace art
+
+#endif  // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_