OSDN Git Service

Move inline method detection to runtime.
authorVladimir Marko <vmarko@google.com>
Wed, 12 Mar 2014 15:42:41 +0000 (15:42 +0000)
committerVladimir Marko <vmarko@google.com>
Wed, 12 Mar 2014 17:59:23 +0000 (17:59 +0000)
The debugger needs this for selective deoptimization.

Change-Id: I8100000449b56e619288fb05d41ea6f02b53b334

compiler/Android.mk
compiler/dex/quick/dex_file_method_inliner.cc
compiler/dex/quick/dex_file_method_inliner.h
compiler/dex/quick/mir_to_lir.cc
compiler/driver/compiler_driver.cc
compiler/driver/compiler_driver.h
runtime/Android.mk
runtime/quick/inline_method_analyser.cc [new file with mode: 0644]
runtime/quick/inline_method_analyser.h [new file with mode: 0644]

index 48e2bcd..2127b40 100644 (file)
@@ -132,8 +132,7 @@ LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
 endif
 
 LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
-       dex/compiler_enums.h \
-       dex/quick/dex_file_method_inliner.h
+       dex/compiler_enums.h
 
 # $(1): target or host
 # $(2): ndebug or debug
index 7423393..e50ba24 100644 (file)
  * limitations under the License.
  */
 
+#include "dex_file_method_inliner.h"
+
 #include <algorithm>
+
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/mutex-inl.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
-#include "dex_file_method_inliner.h"
-
 namespace art {
 
-namespace {  // anonymous namespace
-
-constexpr uint8_t kIGetIPutOpSizes[] = {
-    kWord,          // IGET, IPUT
-    kLong,          // IGET_WIDE, IPUT_WIDE
-    kWord,          // IGET_OBJECT, IPUT_OBJECT
-    kSignedByte,    // IGET_BOOLEAN, IPUT_BOOLEAN
-    kSignedByte,    // IGET_BYTE, IPUT_BYTE
-    kUnsignedHalf,  // IGET_CHAR, IPUT_CHAR
-    kSignedHalf,    // IGET_SHORT, IPUT_SHORT
-};
-
-}  // anonymous namespace
-
 const uint32_t DexFileMethodInliner::kIndexUnresolved;
 const char* const DexFileMethodInliner::kClassCacheNames[] = {
     "Z",                       // kClassCacheBoolean
@@ -271,56 +258,10 @@ DexFileMethodInliner::~DexFileMethodInliner() {
 
 bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) {
   InlineMethod method;
-  bool success = AnalyseMethodCode(verifier, &method);
+  bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method);
   return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method);
 }
 
-bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier,
-                                             InlineMethod* method) {
-  // We currently support only plain return or 2-instruction methods.
-
-  const DexFile::CodeItem* code_item = verifier->CodeItem();
-  DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
-  const Instruction* instruction = Instruction::At(code_item->insns_);
-  Instruction::Code opcode = instruction->Opcode();
-
-  switch (opcode) {
-    case Instruction::RETURN_VOID:
-      method->opcode = kInlineOpNop;
-      method->flags = kInlineSpecial;
-      method->d.data = 0u;
-      return true;
-    case Instruction::RETURN:
-    case Instruction::RETURN_OBJECT:
-    case Instruction::RETURN_WIDE:
-      return AnalyseReturnMethod(code_item, method);
-    case Instruction::CONST:
-    case Instruction::CONST_4:
-    case Instruction::CONST_16:
-    case Instruction::CONST_HIGH16:
-      // TODO: Support wide constants (RETURN_WIDE).
-      return AnalyseConstMethod(code_item, method);
-    case Instruction::IGET:
-    case Instruction::IGET_OBJECT:
-    case Instruction::IGET_BOOLEAN:
-    case Instruction::IGET_BYTE:
-    case Instruction::IGET_CHAR:
-    case Instruction::IGET_SHORT:
-    case Instruction::IGET_WIDE:
-      return AnalyseIGetMethod(verifier, method);
-    case Instruction::IPUT:
-    case Instruction::IPUT_OBJECT:
-    case Instruction::IPUT_BOOLEAN:
-    case Instruction::IPUT_BYTE:
-    case Instruction::IPUT_CHAR:
-    case Instruction::IPUT_SHORT:
-    case Instruction::IPUT_WIDE:
-      return AnalyseIPutMethod(verifier, method);
-    default:
-      return false;
-  }
-}
-
 bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) {
   ReaderMutexLock mu(Thread::Current(), lock_);
   auto it = inline_methods_.find(method_index);
@@ -543,160 +484,4 @@ bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMetho
   }
 }
 
-bool DexFileMethodInliner::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
-                                               InlineMethod* result) {
-  const Instruction* return_instruction = Instruction::At(code_item->insns_);
-  Instruction::Code return_opcode = return_instruction->Opcode();
-  uint16_t size = (return_opcode == Instruction::RETURN_WIDE) ? kLong : kWord;
-  uint16_t is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
-  uint32_t reg = return_instruction->VRegA_11x();
-  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
-  DCHECK_GE(reg, arg_start);
-  DCHECK_LT(size == kLong ? reg + 1 : reg, code_item->registers_size_);
-
-  result->opcode = kInlineOpReturnArg;
-  result->flags = kInlineSpecial;
-  InlineReturnArgData* data = &result->d.return_data;
-  data->arg = reg - arg_start;
-  data->op_size = size;
-  data->is_object = is_object;
-  data->reserved = 0u;
-  data->reserved2 = 0u;
-  return true;
-}
-
-bool DexFileMethodInliner::AnalyseConstMethod(const DexFile::CodeItem* code_item,
-                                              InlineMethod* result) {
-  const Instruction* instruction = Instruction::At(code_item->insns_);
-  const Instruction* return_instruction = instruction->Next();
-  Instruction::Code return_opcode = return_instruction->Opcode();
-  if (return_opcode != Instruction::RETURN &&
-      return_opcode != Instruction::RETURN_OBJECT) {
-    return false;
-  }
-
-  uint32_t return_reg = return_instruction->VRegA_11x();
-  DCHECK_LT(return_reg, code_item->registers_size_);
-
-  uint32_t vA, vB, dummy;
-  uint64_t dummy_wide;
-  instruction->Decode(vA, vB, dummy_wide, dummy, nullptr);
-  if (instruction->Opcode() == Instruction::CONST_HIGH16) {
-    vB <<= 16;
-  }
-  DCHECK_LT(vA, code_item->registers_size_);
-  if (vA != return_reg) {
-    return false;  // Not returning the value set by const?
-  }
-  if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) {
-    return false;  // Returning non-null reference constant?
-  }
-  result->opcode = kInlineOpNonWideConst;
-  result->flags = kInlineSpecial;
-  result->d.data = static_cast<uint64_t>(vB);
-  return true;
-}
-
-bool DexFileMethodInliner::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
-                                             InlineMethod* result) {
-  const DexFile::CodeItem* code_item = verifier->CodeItem();
-  const Instruction* instruction = Instruction::At(code_item->insns_);
-  Instruction::Code opcode = instruction->Opcode();
-  DCHECK_LT(static_cast<size_t>(opcode - Instruction::IGET), arraysize(kIGetIPutOpSizes));
-  uint16_t size = kIGetIPutOpSizes[opcode - Instruction::IGET];
-
-  const Instruction* return_instruction = instruction->Next();
-  Instruction::Code return_opcode = return_instruction->Opcode();
-  if (!(return_opcode == Instruction::RETURN && size != kLong) &&
-      !(return_opcode == Instruction::RETURN_WIDE && size == kLong) &&
-      !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT)) {
-    return false;
-  }
-
-  uint32_t return_reg = return_instruction->VRegA_11x();
-  DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
-            code_item->registers_size_);
-
-  uint32_t dst_reg = instruction->VRegA_22c();
-  uint32_t object_reg = instruction->VRegB_22c();
-  uint32_t field_idx = instruction->VRegC_22c();
-  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
-  DCHECK_GE(object_reg, arg_start);
-  DCHECK_LT(object_reg, code_item->registers_size_);
-  DCHECK_LT(size == kLong ? dst_reg + 1 : dst_reg, code_item->registers_size_);
-  if (dst_reg != return_reg) {
-    return false;  // Not returning the value retrieved by IGET?
-  }
-
-  if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
-    // TODO: Support inlining IGET on other register than "this".
-    return false;
-  }
-
-  if (!CompilerDriver::ComputeSpecialAccessorInfo(field_idx, false, verifier,
-                                                  &result->d.ifield_data)) {
-    return false;
-  }
-
-  result->opcode = kInlineOpIGet;
-  result->flags = kInlineSpecial;
-  InlineIGetIPutData* data = &result->d.ifield_data;
-  data->op_size = size;
-  data->is_object = (opcode == Instruction::IGET_OBJECT) ? 1u : 0u;
-  data->object_arg = object_reg - arg_start;  // Allow IGET on any register, not just "this".
-  data->src_arg = 0;
-  data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
-  data->reserved = 0;
-  return true;
-}
-
-bool DexFileMethodInliner::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
-                                             InlineMethod* result) {
-  const DexFile::CodeItem* code_item = verifier->CodeItem();
-  const Instruction* instruction = Instruction::At(code_item->insns_);
-  Instruction::Code opcode = instruction->Opcode();
-  DCHECK_LT(static_cast<size_t>(opcode - Instruction::IPUT), arraysize(kIGetIPutOpSizes));
-  uint16_t size = kIGetIPutOpSizes[opcode - Instruction::IPUT];
-
-  const Instruction* return_instruction = instruction->Next();
-  if (return_instruction->Opcode() != Instruction::RETURN_VOID) {
-    // TODO: Support returning an argument.
-    // This is needed by builder classes and generated accessor setters.
-    //    builder.setX(value): iput value, this, fieldX; return-object this;
-    //    object.access$nnn(value): iput value, this, fieldX; return value;
-    // Use InlineIGetIPutData::reserved to hold the information.
-    return false;
-  }
-
-  uint32_t src_reg = instruction->VRegA_22c();
-  uint32_t object_reg = instruction->VRegB_22c();
-  uint32_t field_idx = instruction->VRegC_22c();
-  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
-  DCHECK_GE(object_reg, arg_start);
-  DCHECK_LT(object_reg, code_item->registers_size_);
-  DCHECK_GE(src_reg, arg_start);
-  DCHECK_LT(size == kLong ? src_reg + 1 : src_reg, code_item->registers_size_);
-
-  if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
-    // TODO: Support inlining IPUT on other register than "this".
-    return false;
-  }
-
-  if (!CompilerDriver::ComputeSpecialAccessorInfo(field_idx, true, verifier,
-                                                  &result->d.ifield_data)) {
-    return false;
-  }
-
-  result->opcode = kInlineOpIPut;
-  result->flags = kInlineSpecial;
-  InlineIGetIPutData* data = &result->d.ifield_data;
-  data->op_size = size;
-  data->is_object = (opcode == Instruction::IPUT_OBJECT) ? 1u : 0u;
-  data->object_arg = object_reg - arg_start;  // Allow IPUT on any register, not just "this".
-  data->src_arg = src_reg - arg_start;
-  data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
-  data->reserved = 0;
-  return true;
-}
-
 }  // namespace art
index 4aff01c..a6d4cab 100644 (file)
@@ -23,6 +23,7 @@
 #include "safe_map.h"
 #include "dex/compiler_enums.h"
 #include "dex_file.h"
+#include "quick/inline_method_analyser.h"
 
 namespace art {
 
@@ -33,102 +34,6 @@ class MethodVerifier;
 struct CallInfo;
 class Mir2Lir;
 
-enum InlineMethodOpcode : uint16_t {
-  kIntrinsicDoubleCvt,
-  kIntrinsicFloatCvt,
-  kIntrinsicReverseBytes,
-  kIntrinsicAbsInt,
-  kIntrinsicAbsLong,
-  kIntrinsicAbsFloat,
-  kIntrinsicAbsDouble,
-  kIntrinsicMinMaxInt,
-  kIntrinsicSqrt,
-  kIntrinsicCharAt,
-  kIntrinsicCompareTo,
-  kIntrinsicIsEmptyOrLength,
-  kIntrinsicIndexOf,
-  kIntrinsicCurrentThread,
-  kIntrinsicPeek,
-  kIntrinsicPoke,
-  kIntrinsicCas,
-  kIntrinsicUnsafeGet,
-  kIntrinsicUnsafePut,
-
-  kInlineOpNop,
-  kInlineOpReturnArg,
-  kInlineOpNonWideConst,
-  kInlineOpIGet,
-  kInlineOpIPut,
-};
-std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
-
-enum InlineMethodFlags : uint16_t {
-  kNoInlineMethodFlags = 0x0000,
-  kInlineIntrinsic     = 0x0001,
-  kInlineSpecial       = 0x0002,
-};
-
-// IntrinsicFlags are stored in InlineMethod::d::raw_data
-enum IntrinsicFlags {
-  kIntrinsicFlagNone = 0,
-
-  // kIntrinsicMinMaxInt
-  kIntrinsicFlagMax = kIntrinsicFlagNone,
-  kIntrinsicFlagMin = 1,
-
-  // kIntrinsicIsEmptyOrLength
-  kIntrinsicFlagLength  = kIntrinsicFlagNone,
-  kIntrinsicFlagIsEmpty = kIntrinsicFlagMin,
-
-  // kIntrinsicIndexOf
-  kIntrinsicFlagBase0 = kIntrinsicFlagMin,
-
-  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas
-  kIntrinsicFlagIsLong     = kIntrinsicFlagMin,
-  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut
-  kIntrinsicFlagIsVolatile = 2,
-  // kIntrinsicUnsafePut, kIntrinsicUnsafeCas
-  kIntrinsicFlagIsObject   = 4,
-  // kIntrinsicUnsafePut
-  kIntrinsicFlagIsOrdered  = 8,
-};
-
-// Check that OpSize fits into 3 bits (at least the values the inliner uses).
-COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 &&
-               kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow);
-
-struct InlineIGetIPutData {
-  uint16_t op_size : 3;  // OpSize
-  uint16_t is_object : 1;
-  uint16_t object_arg : 4;
-  uint16_t src_arg : 4;  // iput only
-  uint16_t method_is_static : 1;
-  uint16_t reserved : 3;
-  uint16_t field_idx;
-  uint32_t is_volatile : 1;
-  uint32_t field_offset : 31;
-};
-COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint64_t), InvalidSizeOfInlineIGetIPutData);
-
-struct InlineReturnArgData {
-  uint16_t arg;
-  uint16_t op_size : 3;  // OpSize
-  uint16_t is_object : 1;
-  uint16_t reserved : 12;
-  uint32_t reserved2;
-};
-COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint64_t), InvalidSizeOfInlineReturnArgData);
-
-struct InlineMethod {
-  InlineMethodOpcode opcode;
-  InlineMethodFlags flags;
-  union {
-    uint64_t data;
-    InlineIGetIPutData ifield_data;
-    InlineReturnArgData return_data;
-  } d;
-};
-
 /**
  * Handles inlining of methods from a particular DexFile.
  *
@@ -157,17 +62,6 @@ class DexFileMethodInliner {
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
 
     /**
-     * Analyse method code to determine if the method is a candidate for inlining.
-     * If it is, record the inlining data.
-     *
-     * @param verifier the method verifier holding data about the method to analyse.
-     * @param method placeholder for the inline method data.
-     * @return true if the method is a candidate for inlining, false otherwise.
-     */
-    bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
-
-    /**
      * Check whether a particular method index corresponds to an intrinsic function.
      */
     bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_);
@@ -392,13 +286,6 @@ class DexFileMethodInliner {
 
     bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) LOCKS_EXCLUDED(lock_);
 
-    static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
-    static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
-    static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-    static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
     ReaderWriterMutex lock_;
     /*
      * Maps method indexes (for the particular DexFile) to Intrinsic defintions.
index 40ed5ef..31f5c28 100644 (file)
@@ -123,8 +123,8 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) {
     return false;
   }
 
-  DCHECK_NE(data.op_size, kDouble);  // The inliner doesn't distinguish kDouble, uses kLong.
-  bool wide = (data.op_size == kLong);
+  bool wide = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE));
+  // The inliner doesn't distinguish kDouble or kFloat, use shorty.
   bool double_or_float = cu_->shorty[0] == 'F' || cu_->shorty[0] == 'D';
 
   // Point of no return - no aborts after this
@@ -151,8 +151,7 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) {
     return false;
   }
 
-  DCHECK_NE(data.op_size, kDouble);  // The inliner doesn't distinguish kDouble, uses kLong.
-  bool wide = (data.op_size == kLong);
+  bool wide = (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE));
 
   // Point of no return - no aborts after this
   GenPrintLabel(mir);
@@ -173,7 +172,7 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) {
   if (data.is_volatile) {
     GenMemBarrier(kLoadLoad);
   }
-  if (data.is_object) {
+  if (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT)) {
     MarkGCCard(reg_src, reg_obj);
   }
   return true;
@@ -181,8 +180,8 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) {
 
 bool Mir2Lir::GenSpecialIdentity(MIR* mir, const InlineMethod& special) {
   const InlineReturnArgData& data = special.d.return_data;
-  DCHECK_NE(data.op_size, kDouble);  // The inliner doesn't distinguish kDouble, uses kLong.
-  bool wide = (data.op_size == kLong);
+  bool wide = (data.is_wide != 0u);
+  // The inliner doesn't distinguish kDouble or kFloat, use shorty.
   bool double_or_float = cu_->shorty[0] == 'F' || cu_->shorty[0] == 'D';
 
   // Point of no return - no aborts after this
index 7c4a6f7..d545c06 100644 (file)
@@ -961,29 +961,6 @@ void CompilerDriver::ProcessedInvoke(InvokeType invoke_type, int flags) {
   stats_->ProcessedInvoke(invoke_type, flags);
 }
 
-bool CompilerDriver::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
-                                                verifier::MethodVerifier* verifier,
-                                                InlineIGetIPutData* result) {
-  mirror::DexCache* dex_cache = verifier->GetDexCache();
-  uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
-  mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
-  mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
-  if (method == nullptr || field == nullptr || field->IsStatic()) {
-    return false;
-  }
-  mirror::Class* method_class = method->GetDeclaringClass();
-  mirror::Class* field_class = field->GetDeclaringClass();
-  if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
-      (is_put && field->IsFinal() && method_class != field_class)) {
-    return false;
-  }
-  DCHECK_GE(field->GetOffset().Int32Value(), 0);
-  result->field_idx = field_idx;
-  result->field_offset = field->GetOffset().Int32Value();
-  result->is_volatile = field->IsVolatile();
-  return true;
-}
-
 bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
                                               bool is_put, MemberOffset* field_offset,
                                               bool* is_volatile) {
index 26210c9..171be7d 100644 (file)
@@ -287,13 +287,6 @@ class CompilerDriver {
   void ProcessedStaticField(bool resolved, bool local);
   void ProcessedInvoke(InvokeType invoke_type, int flags);
 
-  // Can we fast path instance field access in a verified accessor?
-  // If yes, computes field's offset and volatility and whether the method is static or not.
-  static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
-                                         verifier::MethodVerifier* verifier,
-                                         InlineIGetIPutData* result)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Can we fast path instance field access? Computes field's offset and volatility.
   bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
                                 MemberOffset* field_offset, bool* is_volatile)
index 98bec85..10b3f1e 100644 (file)
@@ -125,6 +125,7 @@ LIBART_COMMON_SRC_FILES := \
        os_linux.cc \
        parsed_options.cc \
        primitive.cc \
+       quick/inline_method_analyser.cc \
        reference_table.cc \
        reflection.cc \
        runtime.cc \
@@ -291,6 +292,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
        lock_word.h \
        mirror/class.h \
        oat.h \
+       quick/inline_method_analyser.h \
        thread.h \
        thread_state.h \
        verifier/method_verifier.h
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
new file mode 100644 (file)
index 0000000..4388d31
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2014 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 "inline_method_analyser.h"
+#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
+#include "mirror/art_field.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
+
+/*
+ * NOTE: This code is part of the quick compiler. It lives in the runtime
+ * only to allow the debugger to check whether a method has been inlined.
+ */
+
+namespace art {
+
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET),
+               check_iget_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE),
+               check_iget_wide_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
+               check_iget_object_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
+               check_iget_boolean_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE),
+               check_iget_byte_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR),
+               check_iget_char_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT),
+               check_iget_short_type);
+
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT),
+               check_iput_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE),
+               check_iput_wide_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
+               check_iput_object_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
+               check_iput_boolean_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE),
+               check_iput_byte_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR),
+               check_iput_char_type);
+COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT),
+               check_iput_short_type);
+
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT), check_iget_iput_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), check_iget_iput_wide_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), check_iget_iput_object_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), check_iget_iput_boolean_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), check_iget_iput_byte_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), check_iget_iput_char_variant);
+COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
+    InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), check_iget_iput_short_variant);
+
+bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
+                                             InlineMethod* method) {
+  // We currently support only plain return or 2-instruction methods.
+
+  const DexFile::CodeItem* code_item = verifier->CodeItem();
+  DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  Instruction::Code opcode = instruction->Opcode();
+
+  switch (opcode) {
+    case Instruction::RETURN_VOID:
+      method->opcode = kInlineOpNop;
+      method->flags = kInlineSpecial;
+      method->d.data = 0u;
+      return true;
+    case Instruction::RETURN:
+    case Instruction::RETURN_OBJECT:
+    case Instruction::RETURN_WIDE:
+      return AnalyseReturnMethod(code_item, method);
+    case Instruction::CONST:
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+    case Instruction::CONST_HIGH16:
+      // TODO: Support wide constants (RETURN_WIDE).
+      return AnalyseConstMethod(code_item, method);
+    case Instruction::IGET:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT:
+    case Instruction::IGET_WIDE:
+      return AnalyseIGetMethod(verifier, method);
+    case Instruction::IPUT:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT:
+    case Instruction::IPUT_WIDE:
+      return AnalyseIPutMethod(verifier, method);
+    default:
+      return false;
+  }
+}
+
+bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
+                                               InlineMethod* result) {
+  const Instruction* return_instruction = Instruction::At(code_item->insns_);
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  uint32_t reg = return_instruction->VRegA_11x();
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(reg, arg_start);
+  DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
+      code_item->registers_size_);
+
+  result->opcode = kInlineOpReturnArg;
+  result->flags = kInlineSpecial;
+  InlineReturnArgData* data = &result->d.return_data;
+  data->arg = reg - arg_start;
+  data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u;
+  data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
+  data->reserved = 0u;
+  data->reserved2 = 0u;
+  return true;
+}
+
+bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
+                                              InlineMethod* result) {
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  const Instruction* return_instruction = instruction->Next();
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  if (return_opcode != Instruction::RETURN &&
+      return_opcode != Instruction::RETURN_OBJECT) {
+    return false;
+  }
+
+  uint32_t return_reg = return_instruction->VRegA_11x();
+  DCHECK_LT(return_reg, code_item->registers_size_);
+
+  uint32_t vA, vB, dummy;
+  uint64_t dummy_wide;
+  instruction->Decode(vA, vB, dummy_wide, dummy, nullptr);
+  if (instruction->Opcode() == Instruction::CONST_HIGH16) {
+    vB <<= 16;
+  }
+  DCHECK_LT(vA, code_item->registers_size_);
+  if (vA != return_reg) {
+    return false;  // Not returning the value set by const?
+  }
+  if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) {
+    return false;  // Returning non-null reference constant?
+  }
+  result->opcode = kInlineOpNonWideConst;
+  result->flags = kInlineSpecial;
+  result->d.data = static_cast<uint64_t>(vB);
+  return true;
+}
+
+bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
+                                             InlineMethod* result) {
+  const DexFile::CodeItem* code_item = verifier->CodeItem();
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  Instruction::Code opcode = instruction->Opcode();
+  DCHECK(IsInstructionIGet(opcode));
+
+  const Instruction* return_instruction = instruction->Next();
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) &&
+      !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) &&
+      !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE &&
+          opcode != Instruction::IGET_OBJECT)) {
+    return false;
+  }
+
+  uint32_t return_reg = return_instruction->VRegA_11x();
+  DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
+            code_item->registers_size_);
+
+  uint32_t dst_reg = instruction->VRegA_22c();
+  uint32_t object_reg = instruction->VRegB_22c();
+  uint32_t field_idx = instruction->VRegC_22c();
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(object_reg, arg_start);
+  DCHECK_LT(object_reg, code_item->registers_size_);
+  DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
+  if (dst_reg != return_reg) {
+    return false;  // Not returning the value retrieved by IGET?
+  }
+
+  if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
+    // TODO: Support inlining IGET on other register than "this".
+    return false;
+  }
+
+  if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, &result->d.ifield_data)) {
+    return false;
+  }
+
+  result->opcode = kInlineOpIGet;
+  result->flags = kInlineSpecial;
+  InlineIGetIPutData* data = &result->d.ifield_data;
+  data->op_variant = IGetVariant(opcode);
+  data->object_arg = object_reg - arg_start;  // Allow IGET on any register, not just "this".
+  data->src_arg = 0;
+  data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
+  data->reserved = 0;
+  return true;
+}
+
+bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
+                                             InlineMethod* result) {
+  const DexFile::CodeItem* code_item = verifier->CodeItem();
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  Instruction::Code opcode = instruction->Opcode();
+  DCHECK(IsInstructionIPut(opcode));
+
+  const Instruction* return_instruction = instruction->Next();
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  if (return_opcode != Instruction::RETURN_VOID) {
+    // TODO: Support returning an argument.
+    // This is needed by builder classes and generated accessor setters.
+    //    builder.setX(value): iput value, this, fieldX; return-object this;
+    //    object.access$nnn(value): iput value, this, fieldX; return value;
+    // Use InlineIGetIPutData::reserved to hold the information.
+    return false;
+  }
+
+  uint32_t src_reg = instruction->VRegA_22c();
+  uint32_t object_reg = instruction->VRegB_22c();
+  uint32_t field_idx = instruction->VRegC_22c();
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(object_reg, arg_start);
+  DCHECK_LT(object_reg, code_item->registers_size_);
+  DCHECK_GE(src_reg, arg_start);
+  DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
+
+  if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
+    // TODO: Support inlining IPUT on other register than "this".
+    return false;
+  }
+
+  if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, &result->d.ifield_data)) {
+    return false;
+  }
+
+  result->opcode = kInlineOpIPut;
+  result->flags = kInlineSpecial;
+  InlineIGetIPutData* data = &result->d.ifield_data;
+  data->op_variant = IPutVariant(opcode);
+  data->object_arg = object_reg - arg_start;  // Allow IPUT on any register, not just "this".
+  data->src_arg = src_reg - arg_start;
+  data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
+  data->reserved = 0;
+  return true;
+}
+
+bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
+                                                      verifier::MethodVerifier* verifier,
+                                                      InlineIGetIPutData* result) {
+  mirror::DexCache* dex_cache = verifier->GetDexCache();
+  uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
+  mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
+  mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
+  if (method == nullptr || field == nullptr || field->IsStatic()) {
+    return false;
+  }
+  mirror::Class* method_class = method->GetDeclaringClass();
+  mirror::Class* field_class = field->GetDeclaringClass();
+  if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
+      (is_put && field->IsFinal() && method_class != field_class)) {
+    return false;
+  }
+  DCHECK_GE(field->GetOffset().Int32Value(), 0);
+  result->field_idx = field_idx;
+  result->field_offset = field->GetOffset().Int32Value();
+  result->is_volatile = field->IsVolatile();
+  return true;
+}
+
+}  // namespace art
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
new file mode 100644 (file)
index 0000000..8e1a408
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2014 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_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
+#define ART_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
+
+/*
+ * NOTE: This code is part of the quick compiler. It lives in the runtime
+ * only to allow the debugger to check whether a method has been inlined.
+ */
+
+namespace art {
+
+namespace verifier {
+class MethodVerifier;
+}  // namespace verifier
+
+enum InlineMethodOpcode : uint16_t {
+  kIntrinsicDoubleCvt,
+  kIntrinsicFloatCvt,
+  kIntrinsicReverseBytes,
+  kIntrinsicAbsInt,
+  kIntrinsicAbsLong,
+  kIntrinsicAbsFloat,
+  kIntrinsicAbsDouble,
+  kIntrinsicMinMaxInt,
+  kIntrinsicSqrt,
+  kIntrinsicCharAt,
+  kIntrinsicCompareTo,
+  kIntrinsicIsEmptyOrLength,
+  kIntrinsicIndexOf,
+  kIntrinsicCurrentThread,
+  kIntrinsicPeek,
+  kIntrinsicPoke,
+  kIntrinsicCas,
+  kIntrinsicUnsafeGet,
+  kIntrinsicUnsafePut,
+
+  kInlineOpNop,
+  kInlineOpReturnArg,
+  kInlineOpNonWideConst,
+  kInlineOpIGet,
+  kInlineOpIPut,
+};
+std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
+
+enum InlineMethodFlags : uint16_t {
+  kNoInlineMethodFlags = 0x0000,
+  kInlineIntrinsic     = 0x0001,
+  kInlineSpecial       = 0x0002,
+};
+
+// IntrinsicFlags are stored in InlineMethod::d::raw_data
+enum IntrinsicFlags {
+  kIntrinsicFlagNone = 0,
+
+  // kIntrinsicMinMaxInt
+  kIntrinsicFlagMax = kIntrinsicFlagNone,
+  kIntrinsicFlagMin = 1,
+
+  // kIntrinsicIsEmptyOrLength
+  kIntrinsicFlagLength  = kIntrinsicFlagNone,
+  kIntrinsicFlagIsEmpty = kIntrinsicFlagMin,
+
+  // kIntrinsicIndexOf
+  kIntrinsicFlagBase0 = kIntrinsicFlagMin,
+
+  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas
+  kIntrinsicFlagIsLong     = kIntrinsicFlagMin,
+  // kIntrinsicUnsafeGet, kIntrinsicUnsafePut
+  kIntrinsicFlagIsVolatile = 2,
+  // kIntrinsicUnsafePut, kIntrinsicUnsafeCas
+  kIntrinsicFlagIsObject   = 4,
+  // kIntrinsicUnsafePut
+  kIntrinsicFlagIsOrdered  = 8,
+};
+
+struct InlineIGetIPutData {
+  // The op_variant below is opcode-Instruction::IGET for IGETs and
+  // opcode-Instruction::IPUT for IPUTs. This is because the runtime
+  // doesn't know the OpSize enumeration.
+  uint16_t op_variant : 3;
+  uint16_t object_arg : 4;
+  uint16_t src_arg : 4;  // iput only
+  uint16_t method_is_static : 1;
+  uint16_t reserved : 4;
+  uint16_t field_idx;
+  uint32_t is_volatile : 1;
+  uint32_t field_offset : 31;
+};
+COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint64_t), InvalidSizeOfInlineIGetIPutData);
+
+struct InlineReturnArgData {
+  uint16_t arg;
+  uint16_t is_wide : 1;
+  uint16_t is_object : 1;
+  uint16_t reserved : 14;
+  uint32_t reserved2;
+};
+COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint64_t), InvalidSizeOfInlineReturnArgData);
+
+struct InlineMethod {
+  InlineMethodOpcode opcode;
+  InlineMethodFlags flags;
+  union {
+    uint64_t data;
+    InlineIGetIPutData ifield_data;
+    InlineReturnArgData return_data;
+  } d;
+};
+
+class InlineMethodAnalyser {
+ public:
+  /**
+   * Analyse method code to determine if the method is a candidate for inlining.
+   * If it is, record the inlining data.
+   *
+   * @param verifier the method verifier holding data about the method to analyse.
+   * @param method placeholder for the inline method data.
+   * @return true if the method is a candidate for inlining, false otherwise.
+   */
+  static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
+    return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
+  }
+
+  static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
+    return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
+  }
+
+  static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
+    return opcode - Instruction::IGET;
+  }
+
+  static constexpr uint16_t IPutVariant(Instruction::Code opcode) {
+    return opcode - Instruction::IPUT;
+  }
+
+ private:
+  static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
+  static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
+  static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Can we fast path instance field access in a verified accessor?
+  // If yes, computes field's offset and volatility and whether the method is static or not.
+  static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
+                                         verifier::MethodVerifier* verifier,
+                                         InlineIGetIPutData* result)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_QUICK_INLINE_METHOD_ANALYSER_H_