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/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
new file mode 100644
index 0000000..515355b
--- /dev/null
+++ b/compiler/linker/relative_patcher_test.h
@@ -0,0 +1,237 @@
+/*
+ * 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_RELATIVE_PATCHER_TEST_H_
+#define ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_
+
+#include "arch/instruction_set.h"
+#include "arch/instruction_set_features.h"
+#include "base/macros.h"
+#include "compiled_method.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
+#include "globals.h"
+#include "gtest/gtest.h"
+#include "linker/relative_patcher.h"
+#include "method_reference.h"
+#include "oat.h"
+#include "utils/array_ref.h"
+#include "vector_output_stream.h"
+
+namespace art {
+namespace linker {
+
+// Base class providing infrastructure for architecture-specific tests.
+class RelativePatcherTest : public testing::Test {
+ protected:
+  RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
+      : compiler_options_(),
+        verification_results_(&compiler_options_),
+        inliner_map_(),
+        driver_(&compiler_options_, &verification_results_, &inliner_map_,
+                Compiler::kQuick, instruction_set, nullptr,
+                false, nullptr, nullptr, 1u,
+                false, false, "", nullptr, -1, ""),
+        error_msg_(),
+        instruction_set_(instruction_set),
+        features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
+        method_offset_map_(),
+        patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)),
+        dex_cache_arrays_begin_(0u),
+        compiled_method_refs_(),
+        compiled_methods_(),
+        patched_code_(),
+        output_(),
+        out_("test output stream", &output_) {
+    CHECK(error_msg_.empty()) << instruction_set << "/" << variant;
+    patched_code_.reserve(16 * KB);
+  }
+
+  MethodReference MethodRef(uint32_t method_idx) {
+    return MethodReference(nullptr, method_idx);
+  }
+
+  void AddCompiledMethod(MethodReference method_ref,
+                         const ArrayRef<const uint8_t>& code,
+                         const ArrayRef<LinkerPatch>& patches) {
+    compiled_method_refs_.push_back(method_ref);
+    compiled_methods_.emplace_back(new CompiledMethod(
+        &driver_, instruction_set_, code,
+        0u, 0u, 0u, nullptr, ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+        ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+        patches));
+  }
+
+  void Link() {
+    // Reserve space.
+    static_assert(kTrampolineOffset == 0u, "Unexpected trampoline offset.");
+    uint32_t offset = kTrampolineSize;
+    size_t idx = 0u;
+    for (auto& compiled_method : compiled_methods_) {
+      offset = patcher_->ReserveSpace(offset, compiled_method.get());
+
+      uint32_t aligned_offset = compiled_method->AlignCode(offset);
+      uint32_t aligned_code_delta = aligned_offset - offset;
+      offset += aligned_code_delta;
+
+      offset += sizeof(OatQuickMethodHeader);
+      uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
+      const auto& code = *compiled_method->GetQuickCode();
+      offset += code.size();
+
+      method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset);
+      ++idx;
+    }
+    offset = patcher_->ReserveSpace(offset, nullptr);
+    uint32_t output_size = offset;
+    output_.reserve(output_size);
+
+    // Write data.
+    DCHECK(output_.empty());
+    uint8_t dummy_trampoline[kTrampolineSize];
+    memset(dummy_trampoline, 0, sizeof(dummy_trampoline));
+    out_.WriteFully(dummy_trampoline, kTrampolineSize);
+    offset = kTrampolineSize;
+    static const uint8_t kPadding[] = {
+        0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+    };
+    uint8_t dummy_header[sizeof(OatQuickMethodHeader)];
+    memset(dummy_header, 0, sizeof(dummy_header));
+    for (auto& compiled_method : compiled_methods_) {
+      offset = patcher_->WriteThunks(&out_, offset);
+
+      uint32_t aligned_offset = compiled_method->AlignCode(offset);
+      uint32_t aligned_code_delta = aligned_offset - offset;
+      CHECK_LE(aligned_code_delta, sizeof(kPadding));
+      out_.WriteFully(kPadding, aligned_code_delta);
+      offset += aligned_code_delta;
+
+      out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
+      offset += sizeof(OatQuickMethodHeader);
+      ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode());
+      if (!compiled_method->GetPatches().empty()) {
+        patched_code_.assign(code.begin(), code.end());
+        code = ArrayRef<const uint8_t>(patched_code_);
+        for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+          if (patch.Type() == kLinkerPatchCallRelative) {
+            auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod());
+            uint32_t target_offset =
+                result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta();
+            patcher_->PatchCall(&patched_code_, patch.LiteralOffset(),
+                                offset + patch.LiteralOffset(), target_offset);
+          } else if (patch.Type() == kLinkerPatchDexCacheArray) {
+            uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset();
+            patcher_->PatchDexCacheReference(&patched_code_, patch,
+                                             offset + patch.LiteralOffset(), target_offset);
+          } else {
+            LOG(FATAL) << "Bad patch type.";
+          }
+        }
+      }
+      out_.WriteFully(&code[0], code.size());
+      offset += code.size();
+    }
+    offset = patcher_->WriteThunks(&out_, offset);
+    CHECK_EQ(offset, output_size);
+    CHECK_EQ(output_.size(), output_size);
+  }
+
+  bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) {
+    // Sanity check: original code size must match linked_code.size().
+    size_t idx = 0u;
+    for (auto ref : compiled_method_refs_) {
+      if (ref.dex_file == method_ref.dex_file &&
+          ref.dex_method_index == method_ref.dex_method_index) {
+        break;
+      }
+      ++idx;
+    }
+    CHECK_NE(idx, compiled_method_refs_.size());
+    CHECK_EQ(compiled_methods_[idx]->GetQuickCode()->size(), expected_code.size());
+
+    auto result = method_offset_map_.FindMethodOffset(method_ref);
+    CHECK(result.first);  // Must have been linked.
+    size_t offset = result.second;
+    CHECK_LT(offset, output_.size());
+    CHECK_LE(offset + expected_code.size(), output_.size());
+    ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size());
+    if (linked_code == expected_code) {
+      return true;
+    }
+    // Log failure info.
+    std::ostringstream expected_hex;
+    std::ostringstream linked_hex;
+    std::ostringstream diff_indicator;
+    static const char digits[] = "0123456789abcdef";
+    bool found_diff = false;
+    for (size_t i = 0; i != expected_code.size(); ++i) {
+      expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf];
+      linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf];
+      diff_indicator << " ";
+      if (!found_diff) {
+        found_diff = (expected_code[i] != linked_code[i]);
+        diff_indicator << (found_diff ? "^^" : "  ");
+      }
+    }
+    CHECK(found_diff);
+    LOG(ERROR) << "diff expected_code linked_code";
+    LOG(ERROR) << "<" << expected_hex.str();
+    LOG(ERROR) << ">" << linked_hex.str();
+    LOG(ERROR) << " " << diff_indicator.str();
+    return false;
+  }
+
+  // Map method reference to assinged offset.
+  // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
+  class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider {
+   public:
+    std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE {
+      auto it = map.find(ref);
+      if (it == map.end()) {
+        return std::pair<bool, uint32_t>(false, 0u);
+      } else {
+        return std::pair<bool, uint32_t>(true, it->second);
+      }
+    }
+    SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map;
+  };
+
+  static const uint32_t kTrampolineSize = 4u;
+  static const uint32_t kTrampolineOffset = 0u;
+
+  CompilerOptions compiler_options_;
+  VerificationResults verification_results_;
+  DexFileToMethodInlinerMap inliner_map_;
+  CompilerDriver driver_;  // Needed for constructing CompiledMethod.
+  std::string error_msg_;
+  InstructionSet instruction_set_;
+  std::unique_ptr<const InstructionSetFeatures> features_;
+  MethodOffsetMap method_offset_map_;
+  std::unique_ptr<RelativePatcher> patcher_;
+  uint32_t dex_cache_arrays_begin_;
+  std::vector<MethodReference> compiled_method_refs_;
+  std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
+  std::vector<uint8_t> patched_code_;
+  std::vector<uint8_t> output_;
+  VectorOutputStream out_;
+};
+
+}  // namespace linker
+}  // namespace art
+
+#endif  // ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_