OSDN Git Service

PC-relative loads from dex cache arrays for x86-64.
authorVladimir Marko <vmarko@google.com>
Fri, 27 Mar 2015 18:18:36 +0000 (18:18 +0000)
committerVladimir Marko <vmarko@google.com>
Thu, 2 Apr 2015 08:12:07 +0000 (09:12 +0100)
Change-Id: I6cfe22c7e69512b3c0f95b073aaa572db74ec189

compiler/dex/quick/x86/call_x86.cc
compiler/dex/quick/x86/codegen_x86.h
compiler/dex/quick/x86/int_x86.cc
compiler/dex/quick/x86/target_x86.cc
compiler/oat_writer.cc
compiler/oat_writer.h

index e81228a..bf7c157 100644 (file)
@@ -25,6 +25,7 @@
 #include "gc/accounting/card_table.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -322,13 +323,13 @@ void X86Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) {
  * Bit of a hack here - in the absence of a real scheduling pass,
  * emit the next instruction in static & direct invoke sequences.
  */
-static int X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
-                             int state, const MethodReference& target_method,
-                             uint32_t,
-                             uintptr_t direct_code, uintptr_t direct_method,
-                             InvokeType type) {
+int X86Mir2Lir::X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                                  int state, const MethodReference& target_method,
+                                  uint32_t,
+                                  uintptr_t direct_code, uintptr_t direct_method,
+                                  InvokeType type) {
   UNUSED(info, direct_code);
-  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+  X86Mir2Lir* cg = static_cast<X86Mir2Lir*>(cu->cg.get());
   if (direct_method != 0) {
     switch (state) {
     case 0:  // Get the current Method* [sets kArg0]
@@ -346,6 +347,17 @@ static int X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
     default:
       return -1;
     }
+  } else if (cg->CanUseOpPcRelDexCacheArrayLoad()) {
+    switch (state) {
+      case 0: {
+        CHECK_EQ(cu->dex_file, target_method.dex_file);
+        size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
+        cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, cg->TargetReg(kArg0, kRef));
+        break;
+      }
+      default:
+        return -1;
+    }
   } else {
     RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
     switch (state) {
index 040a8c4..758684e 100644 (file)
@@ -104,6 +104,9 @@ class X86Mir2Lir : public Mir2Lir {
   /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
   void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
 
+  bool CanUseOpPcRelDexCacheArrayLoad() const OVERRIDE;
+  void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) OVERRIDE;
+
   void GenImplicitNullCheck(RegStorage reg, int opt_flags) OVERRIDE;
 
   // Required for target - register utilities.
@@ -952,6 +955,9 @@ class X86Mir2Lir : public Mir2Lir {
   // Instructions needing patching with PC relative code addresses.
   ArenaVector<LIR*> call_method_insns_;
 
+  // Instructions needing patching with PC relative code addresses.
+  ArenaVector<LIR*> dex_cache_access_insns_;
+
   // Prologue decrement of stack pointer.
   LIR* stack_decrement_;
 
@@ -992,6 +998,12 @@ class X86Mir2Lir : public Mir2Lir {
   void SwapBits(RegStorage result_reg, int shift, int32_t value);
   void SwapBits64(RegStorage result_reg, int shift, int64_t value);
 
+  static int X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                               int state, const MethodReference& target_method,
+                               uint32_t,
+                               uintptr_t direct_code, uintptr_t direct_method,
+                               InvokeType type);
+
   static const X86EncodingMap EncodingMap[kX86Last];
 
   friend std::ostream& operator<<(std::ostream& os, const X86OpCode& rhs);
index 4eb626c..5def5c8 100755 (executable)
@@ -1324,14 +1324,16 @@ bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
   return true;
 }
 
+// When we don't know the proper offset for the value, pick one that will force
+// 4 byte offset.  We will fix this up in the assembler or linker later to have
+// the right value.
+static constexpr int kDummy32BitOffset = 256;
+
 void X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
   if (cu_->target64) {
     // We can do this directly using RIP addressing.
-    // We don't know the proper offset for the value, so pick one that will force
-    // 4 byte offset.  We will fix this up in the assembler later to have the right
-    // value.
     ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-    LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), kRIPReg, 256);
+    LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), kRIPReg, kDummy32BitOffset);
     res->target = target;
     res->flags.fixup = kFixupLoad;
     return;
@@ -1349,15 +1351,32 @@ void X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
   store_method_addr_used_ = true;
 
   // Load the proper value from the literal area.
-  // We don't know the proper offset for the value, so pick one that will force
-  // 4 byte offset.  We will fix this up in the assembler later to have the right
-  // value.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), reg.GetReg(), 256);
+  LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), reg.GetReg(), kDummy32BitOffset);
   res->target = target;
   res->flags.fixup = kFixupLoad;
 }
 
+bool X86Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+  // TODO: Implement for 32-bit.
+  return cu_->target64 && dex_cache_arrays_layout_.Valid();
+}
+
+void X86Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset,
+                                          RegStorage r_dest) {
+  if (cu_->target64) {
+    LIR* mov = NewLIR3(kX86Mov32RM, r_dest.GetReg(), kRIPReg, kDummy32BitOffset);
+    mov->flags.fixup = kFixupLabel;
+    mov->operands[3] = WrapPointer(dex_file);
+    mov->operands[4] = offset;
+    dex_cache_access_insns_.push_back(mov);
+  } else {
+    // TODO: Implement for 32-bit.
+    LOG(FATAL) << "Unimplemented.";
+    UNREACHABLE();
+  }
+}
+
 LIR* X86Mir2Lir::OpVldm(RegStorage r_base, int count) {
   UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVldm for x86";
index f128eb7..cad82a1 100755 (executable)
@@ -829,6 +829,7 @@ X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator*
       method_address_insns_(arena->Adapter()),
       class_type_address_insns_(arena->Adapter()),
       call_method_insns_(arena->Adapter()),
+      dex_cache_access_insns_(arena->Adapter()),
       stack_decrement_(nullptr), stack_increment_(nullptr),
       const_vectors_(nullptr) {
   method_address_insns_.reserve(100);
@@ -1058,6 +1059,9 @@ void X86Mir2Lir::InstallLiteralPools() {
     }
   }
 
+  patches_.reserve(method_address_insns_.size() + class_type_address_insns_.size() +
+                   call_method_insns_.size() + dex_cache_access_insns_.size());
+
   // Handle the fixups for methods.
   for (LIR* p : method_address_insns_) {
       DCHECK_EQ(p->opcode, kX86Mov32RI);
@@ -1084,7 +1088,6 @@ void X86Mir2Lir::InstallLiteralPools() {
   }
 
   // And now the PC-relative calls to methods.
-  patches_.reserve(call_method_insns_.size());
   for (LIR* p : call_method_insns_) {
       DCHECK_EQ(p->opcode, kX86CallI);
       uint32_t target_method_idx = p->operands[1];
@@ -1096,6 +1099,17 @@ void X86Mir2Lir::InstallLiteralPools() {
                                                         target_dex_file, target_method_idx));
   }
 
+  // PC-relative references to dex cache arrays.
+  for (LIR* p : dex_cache_access_insns_) {
+    DCHECK(p->opcode == kX86Mov32RM);
+    const DexFile* dex_file = UnwrapPointer<DexFile>(p->operands[3]);
+    uint32_t offset = p->operands[4];
+    // The offset to patch is the last 4 bytes of the instruction.
+    int patch_offset = p->offset + p->flags.size - 4;
+    DCHECK(!p->flags.is_nop);
+    patches_.push_back(LinkerPatch::DexCacheArrayPatch(patch_offset, dex_file, p->offset, offset));
+  }
+
   // And do the normal processing.
   Mir2Lir::InstallLiteralPools();
 }
index 04f0db6..2c3f982 100644 (file)
@@ -105,9 +105,9 @@ class OatWriter::NoRelativePatcher FINAL : public RelativePatcher {
   DISALLOW_COPY_AND_ASSIGN(NoRelativePatcher);
 };
 
-class OatWriter::X86RelativePatcher FINAL : public RelativePatcher {
+class OatWriter::X86BaseRelativePatcher : public RelativePatcher {
  public:
-  X86RelativePatcher() { }
+  X86BaseRelativePatcher() { }
 
   uint32_t ReserveSpace(uint32_t offset,
                         const CompiledMethod* compiled_method ATTRIBUTE_UNUSED) OVERRIDE {
@@ -129,19 +129,42 @@ class OatWriter::X86RelativePatcher FINAL : public RelativePatcher {
     reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
   }
 
+ protected:
+  // PC displacement from patch location; the base address of x86/x86-64 relative
+  // calls and x86-64 RIP-relative addressing is the PC of the next instruction and
+  // the patch location is 4 bytes earlier.
+  static constexpr int32_t kPcDisplacement = 4;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(X86BaseRelativePatcher);
+};
+
+class OatWriter::X86RelativePatcher FINAL : public X86BaseRelativePatcher {
+ public:
+  X86RelativePatcher() { }
+
   virtual void 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.";
   }
+};
 
- private:
-  // PC displacement from patch location; x86 PC for relative calls points to the next
-  // instruction and the patch location is 4 bytes earlier.
-  static constexpr int32_t kPcDisplacement = 4;
+class OatWriter::X86_64RelativePatcher FINAL : public X86BaseRelativePatcher {
+ public:
+  X86_64RelativePatcher() { }
+
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
+                                      uint32_t patch_offset, uint32_t target_offset) {
+    DCHECK_LE(patch.LiteralOffset() + 4u, code->size());
+    // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+    uint32_t displacement = target_offset - patch_offset;
+    displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
 
-  DISALLOW_COPY_AND_ASSIGN(X86RelativePatcher);
+    typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
+    reinterpret_cast<unaligned_int32_t*>(&(*code)[patch.LiteralOffset()])[0] = displacement;
+  }
 };
 
 class OatWriter::ArmBaseRelativePatcher : public RelativePatcher {
@@ -706,9 +729,11 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
 
   switch (compiler_driver_->GetInstructionSet()) {
     case kX86:
-    case kX86_64:
       relative_patcher_.reset(new X86RelativePatcher);
       break;
+    case kX86_64:
+      relative_patcher_.reset(new X86_64RelativePatcher);
+      break;
     case kArm:
       // Fall through: we generate Thumb2 code for "arm".
     case kThumb2:
index 676d628..8951634 100644 (file)
@@ -329,7 +329,9 @@ class OatWriter {
 
   class RelativePatcher;
   class NoRelativePatcher;
+  class X86BaseRelativePatcher;
   class X86RelativePatcher;
+  class X86_64RelativePatcher;
   class ArmBaseRelativePatcher;
   class Thumb2RelativePatcher;
   class Arm64RelativePatcher;