OSDN Git Service

Optimizing: Try pattern substitution when we cannot inline.
authorVladimir Marko <vmarko@google.com>
Fri, 22 Jan 2016 12:09:44 +0000 (12:09 +0000)
committerVladimir Marko <vmarko@google.com>
Thu, 28 Jan 2016 18:13:53 +0000 (18:13 +0000)
Change-Id: I7c01f4494bac8498accc0f087044ec509fee4c98

14 files changed:
compiler/optimizing/inliner.cc
compiler/optimizing/inliner.h
compiler/optimizing/optimizing_compiler_stats.h
compiler/optimizing/reference_type_propagation.cc
compiler/optimizing/reference_type_propagation.h
runtime/quick/inline_method_analyser.cc
runtime/quick/inline_method_analyser.h
runtime/verifier/method_verifier-inl.h
runtime/verifier/method_verifier.h
test/569-checker-pattern-replacement/expected.txt [new file with mode: 0644]
test/569-checker-pattern-replacement/info.txt [new file with mode: 0644]
test/569-checker-pattern-replacement/run [new file with mode: 0755]
test/569-checker-pattern-replacement/src-multidex/Second.java [new file with mode: 0644]
test/569-checker-pattern-replacement/src/Main.java [new file with mode: 0644]

index 35109fa..51fef7c 100644 (file)
@@ -21,6 +21,8 @@
 #include "class_linker.h"
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
 #include "driver/compiler_driver-inl.h"
 #include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
 #include "optimizing_compiler.h"
 #include "reference_type_propagation.h"
 #include "register_allocator.h"
+#include "quick/inline_method_analyser.h"
 #include "sharpening.h"
 #include "ssa_builder.h"
 #include "ssa_phi_elimination.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
-#include "dex/verified_method.h"
-#include "dex/verification_results.h"
 
 namespace art {
 
@@ -488,6 +489,11 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do
   // dex file here (though the transitivity of an inline chain would allow checking the calller).
   if (!compiler_driver_->MayInline(method->GetDexFile(),
                                    outer_compilation_unit_.GetDexFile())) {
+    if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+      VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
+      MaybeRecordStat(kReplacedInvokeWithSimplePattern);
+      return true;
+    }
     VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
                    << outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
                    << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
@@ -559,6 +565,140 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do
   return true;
 }
 
+static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
+                                                   size_t arg_vreg_index)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  size_t input_index = 0;
+  for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
+    DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+    if (Primitive::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
+      ++i;
+      DCHECK_NE(i, arg_vreg_index);
+    }
+  }
+  DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+  return invoke_instruction->InputAt(input_index);
+}
+
+// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
+                                      ArtMethod* resolved_method,
+                                      bool do_rtp) {
+  InlineMethod inline_method;
+  if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
+    return false;
+  }
+
+  HInstruction* return_replacement = nullptr;
+  switch (inline_method.opcode) {
+    case kInlineOpNop:
+      DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+      break;
+    case kInlineOpReturnArg:
+      return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+                                                         inline_method.d.return_data.arg);
+      break;
+    case kInlineOpNonWideConst:
+      if (resolved_method->GetShorty()[0] == 'L') {
+        DCHECK_EQ(inline_method.d.data, 0u);
+        return_replacement = graph_->GetNullConstant();
+      } else {
+        return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+      }
+      break;
+    case kInlineOpIGet: {
+      const InlineIGetIPutData& data = inline_method.d.ifield_data;
+      if (data.method_is_static || data.object_arg != 0u) {
+        // TODO: Needs null check.
+        return false;
+      }
+      HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+      HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+      DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
+      DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
+      invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
+      return_replacement = iget;
+      break;
+    }
+    case kInlineOpIPut: {
+      const InlineIGetIPutData& data = inline_method.d.ifield_data;
+      if (data.method_is_static || data.object_arg != 0u) {
+        // TODO: Needs null check.
+        return false;
+      }
+      HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+      HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
+      HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+      DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
+      DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
+      invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+      if (data.return_arg_plus1 != 0u) {
+        size_t return_arg = data.return_arg_plus1 - 1u;
+        return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
+  }
+
+  if (return_replacement != nullptr) {
+    invoke_instruction->ReplaceWith(return_replacement);
+  }
+  invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+
+  FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+  return true;
+}
+
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+                                                    uint32_t field_index,
+                                                    HInstruction* obj)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+  size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+  ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+  DCHECK(resolved_field != nullptr);
+  HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
+      obj,
+      resolved_field->GetTypeAsPrimitiveType(),
+      resolved_field->GetOffset(),
+      resolved_field->IsVolatile(),
+      field_index,
+      resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+      *resolved_method->GetDexFile(),
+      dex_cache,
+      kNoDexPc);
+  if (iget->GetType() == Primitive::kPrimNot) {
+    ReferenceTypePropagation rtp(graph_, handles_);
+    rtp.Visit(iget);
+  }
+  return iget;
+}
+
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+                                                    uint32_t field_index,
+                                                    HInstruction* obj,
+                                                    HInstruction* value)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+  size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+  ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+  DCHECK(resolved_field != nullptr);
+  HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
+      obj,
+      value,
+      resolved_field->GetTypeAsPrimitiveType(),
+      resolved_field->GetOffset(),
+      resolved_field->IsVolatile(),
+      field_index,
+      resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+      *resolved_method->GetDexFile(),
+      dex_cache,
+      kNoDexPc);
+  return iput;
+}
 bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
                                  HInvoke* invoke_instruction,
                                  bool same_dex_file,
@@ -815,7 +955,14 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
   if (return_replacement != nullptr) {
     DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
   }
+  FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+  return true;
+}
 
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+                                        HInvoke* invoke_instruction,
+                                        HInstruction* return_replacement,
+                                        bool do_rtp) {
   // Check the integrity of reference types and run another type propagation if needed.
   if (return_replacement != nullptr) {
     if (return_replacement->GetType() == Primitive::kPrimNot) {
@@ -849,8 +996,6 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
       }
     }
   }
-
-  return true;
 }
 
 }  // namespace art
index 3c01751..0127d55 100644 (file)
@@ -65,6 +65,20 @@ class HInliner : public HOptimization {
   bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
     SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+  bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+    SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Create a new HInstanceFieldGet.
+  HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method,
+                                            uint32_t field_index,
+                                            HInstruction* obj);
+  // Create a new HInstanceFieldSet.
+  HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method,
+                                            uint32_t field_index,
+                                            HInstruction* obj,
+                                            HInstruction* value);
+
   // Try to inline the target of a monomorphic call. If successful, the code
   // in the graph will look like:
   // if (receiver.getClass() != ic.GetMonomorphicType()) deopt
@@ -90,6 +104,12 @@ class HInliner : public HOptimization {
                                            uint32_t dex_pc) const
     SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void FixUpReturnReferenceType(ArtMethod* resolved_method,
+                                HInvoke* invoke_instruction,
+                                HInstruction* return_replacement,
+                                bool do_rtp)
+    SHARED_REQUIRES(Locks::mutator_lock_);
+
   HGraph* const outermost_graph_;
   const DexCompilationUnit& outer_compilation_unit_;
   const DexCompilationUnit& caller_compilation_unit_;
index 881beb4..52a7b10 100644 (file)
@@ -29,6 +29,7 @@ enum MethodCompilationStat {
   kAttemptCompilation = 0,
   kCompiled,
   kInlinedInvoke,
+  kReplacedInvokeWithSimplePattern,
   kInstructionSimplifications,
   kInstructionSimplificationsArch,
   kUnresolvedMethod,
@@ -97,6 +98,7 @@ class OptimizingCompilerStats {
       case kAttemptCompilation : name = "AttemptCompilation"; break;
       case kCompiled : name = "Compiled"; break;
       case kInlinedInvoke : name = "InlinedInvoke"; break;
+      case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break;
       case kInstructionSimplifications: name = "InstructionSimplifications"; break;
       case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break;
       case kUnresolvedMethod : name = "UnresolvedMethod"; break;
index 779f319..1224a48 100644 (file)
@@ -94,7 +94,6 @@ ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
     : HOptimization(graph, name),
       handle_cache_(handles),
       worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) {
-  worklist_.reserve(kDefaultWorklistSize);
 }
 
 void ReferenceTypePropagation::ValidateTypes() {
@@ -125,7 +124,14 @@ void ReferenceTypePropagation::ValidateTypes() {
   }
 }
 
+void ReferenceTypePropagation::Visit(HInstruction* instruction) {
+  RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+  instruction->Accept(&visitor);
+}
+
 void ReferenceTypePropagation::Run() {
+  worklist_.reserve(kDefaultWorklistSize);
+
   // To properly propagate type info we need to visit in the dominator-based order.
   // Reverse post order guarantees a node's dominators are visited first.
   // We take advantage of this order in `VisitBasicBlock`.
index 47ba027..a7f10a6 100644 (file)
@@ -35,6 +35,9 @@ class ReferenceTypePropagation : public HOptimization {
                            StackHandleScopeCollection* handles,
                            const char* name = kReferenceTypePropagationPassName);
 
+  // Visit a single instruction.
+  void Visit(HInstruction* instruction);
+
   void Run() OVERRIDE;
 
   static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
index 6554394..17306c9 100644 (file)
@@ -71,14 +71,37 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
 // we need to be able to detect possibly inlined method, we pass a null inline method to indicate
 // we don't want to take unresolved methods and fields into account during analysis.
 bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
-                                             InlineMethod* method) {
+                                             InlineMethod* result) {
   DCHECK(verifier != nullptr);
   if (!Runtime::Current()->UseJit()) {
-    DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+    DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
+  }
+
+  // Note: verifier->GetMethod() may be null.
+  return AnalyseMethodCode(verifier->CodeItem(),
+                           verifier->GetMethodReference(),
+                           (verifier->GetAccessFlags() & kAccStatic) != 0u,
+                           verifier->GetMethod(),
+                           result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
+  const DexFile::CodeItem* code_item = method->GetCodeItem();
+  if (code_item == nullptr) {
+    // Native or abstract.
+    return false;
   }
+  return AnalyseMethodCode(
+      code_item, method->ToMethodReference(), method->IsStatic(), method, result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+                                             const MethodReference& method_ref,
+                                             bool is_static,
+                                             ArtMethod* method,
+                                             InlineMethod* result) {
   // 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();
@@ -86,21 +109,21 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
   switch (opcode) {
     case Instruction::RETURN_VOID:
       if (method != nullptr) {
-        method->opcode = kInlineOpNop;
-        method->flags = kInlineSpecial;
-        method->d.data = 0u;
+        result->opcode = kInlineOpNop;
+        result->flags = kInlineSpecial;
+        result->d.data = 0u;
       }
       return true;
     case Instruction::RETURN:
     case Instruction::RETURN_OBJECT:
     case Instruction::RETURN_WIDE:
-      return AnalyseReturnMethod(code_item, method);
+      return AnalyseReturnMethod(code_item, result);
     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);
+      return AnalyseConstMethod(code_item, result);
     case Instruction::IGET:
     case Instruction::IGET_OBJECT:
     case Instruction::IGET_BOOLEAN:
@@ -112,7 +135,7 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
     // case Instruction::IGET_QUICK:
     // case Instruction::IGET_WIDE_QUICK:
     // case Instruction::IGET_OBJECT_QUICK:
-      return AnalyseIGetMethod(verifier, method);
+      return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
     case Instruction::IPUT:
     case Instruction::IPUT_OBJECT:
     case Instruction::IPUT_BOOLEAN:
@@ -124,7 +147,7 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
     // case Instruction::IPUT_QUICK:
     // case Instruction::IPUT_WIDE_QUICK:
     // case Instruction::IPUT_OBJECT_QUICK:
-      return AnalyseIPutMethod(verifier, method);
+      return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
     default:
       return false;
   }
@@ -194,9 +217,11 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item
   return true;
 }
 
-bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+                                             const MethodReference& method_ref,
+                                             bool is_static,
+                                             ArtMethod* method,
                                              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));
@@ -227,10 +252,10 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
     return false;  // Not returning the value retrieved by IGET?
   }
 
-  if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+  if (is_static || object_arg != 0u) {
     // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
-    if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+    if (!IsSyntheticAccessor(method_ref)) {
       return false;
     }
   }
@@ -243,13 +268,13 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
 
   if (result != nullptr) {
     InlineIGetIPutData* data = &result->d.ifield_data;
-    if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) {
+    if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
       return false;
     }
     result->opcode = kInlineOpIGet;
     result->flags = kInlineSpecial;
     data->op_variant = IGetVariant(opcode);
-    data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+    data->method_is_static = is_static ? 1u : 0u;
     data->object_arg = object_arg;  // Allow IGET on any register, not just "this".
     data->src_arg = 0u;
     data->return_arg_plus1 = 0u;
@@ -257,9 +282,11 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
   return true;
 }
 
-bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+                                             const MethodReference& method_ref,
+                                             bool is_static,
+                                             ArtMethod* method,
                                              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));
@@ -292,10 +319,10 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
   uint32_t object_arg = object_reg - arg_start;
   uint32_t src_arg = src_reg - arg_start;
 
-  if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+  if (is_static || object_arg != 0u) {
     // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
-    if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+    if (!IsSyntheticAccessor(method_ref)) {
       return false;
     }
   }
@@ -310,13 +337,13 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
 
   if (result != nullptr) {
     InlineIGetIPutData* data = &result->d.ifield_data;
-    if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) {
+    if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
       return false;
     }
     result->opcode = kInlineOpIPut;
     result->flags = kInlineSpecial;
     data->op_variant = IPutVariant(opcode);
-    data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+    data->method_is_static = is_static ? 1u : 0u;
     data->object_arg = object_arg;  // Allow IPUT on any register, not just "this".
     data->src_arg = src_arg;
     data->return_arg_plus1 = return_arg_plus1;
@@ -324,15 +351,17 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
   return true;
 }
 
-bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
-                                                      verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
+                                                      uint32_t field_idx,
+                                                      bool is_put,
                                                       InlineIGetIPutData* result) {
-  mirror::DexCache* dex_cache = verifier->GetDexCache();
-  uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
-  auto* cl = Runtime::Current()->GetClassLinker();
-  ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, cl->GetImagePointerSize());
-  ArtField* field = cl->GetResolvedField(field_idx, dex_cache);
-  if (method == nullptr || field == nullptr || field->IsStatic()) {
+  if (method == nullptr) {
+    return false;
+  }
+  mirror::DexCache* dex_cache = method->GetDexCache();
+  size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size);
+  if (field == nullptr || field->IsStatic()) {
     return false;
   }
   mirror::Class* method_class = method->GetDeclaringClass();
index 1bb816b..046d225 100644 (file)
@@ -188,7 +188,9 @@ class InlineMethodAnalyser {
    * @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)
+  static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
@@ -211,17 +213,32 @@ class InlineMethodAnalyser {
   static bool IsSyntheticAccessor(MethodReference ref);
 
  private:
+  static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+                                const MethodReference& method_ref,
+                                bool is_static,
+                                ArtMethod* method,
+                                InlineMethod* result)
+      SHARED_REQUIRES(Locks::mutator_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)
+  static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+                                const MethodReference& method_ref,
+                                bool is_static,
+                                ArtMethod* method,
+                                InlineMethod* result)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+  static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+                                const MethodReference& method_ref,
+                                bool is_static,
+                                ArtMethod* method,
+                                InlineMethod* result)
       SHARED_REQUIRES(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,
+  static bool ComputeSpecialAccessorInfo(ArtMethod* method,
+                                         uint32_t field_idx,
+                                         bool is_put,
                                          InlineIGetIPutData* result)
       SHARED_REQUIRES(Locks::mutator_lock_);
 };
index f52d011..def61db 100644 (file)
@@ -50,6 +50,10 @@ inline mirror::DexCache* MethodVerifier::GetDexCache() {
   return dex_cache_.Get();
 }
 
+inline ArtMethod* MethodVerifier::GetMethod() const {
+  return mirror_method_;
+}
+
 inline MethodReference MethodVerifier::GetMethodReference() const {
   return MethodReference(dex_file_, dex_method_idx_);
 }
index a26e0fb..613d5af 100644 (file)
@@ -270,6 +270,7 @@ class MethodVerifier {
   ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index);
   mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
   mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+  ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
   MethodReference GetMethodReference() const;
   uint32_t GetAccessFlags() const;
   bool HasCheckCasts() const;
diff --git a/test/569-checker-pattern-replacement/expected.txt b/test/569-checker-pattern-replacement/expected.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/569-checker-pattern-replacement/info.txt b/test/569-checker-pattern-replacement/info.txt
new file mode 100644 (file)
index 0000000..4dfa932
--- /dev/null
@@ -0,0 +1 @@
+Test pattern substitution used when we cannot inline.
diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run
new file mode 100755 (executable)
index 0000000..f7e9df2
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+exec ${RUN} "$@" \
+    -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex
diff --git a/test/569-checker-pattern-replacement/src-multidex/Second.java b/test/569-checker-pattern-replacement/src-multidex/Second.java
new file mode 100644 (file)
index 0000000..cba1dc8
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public final class Second {
+  public static void staticNop(int unused) { }
+
+  public void nop() { }
+
+  public static Object staticReturnArg2(int unused1, String arg2) {
+    return arg2;
+  }
+
+  public long returnArg1(long arg1) {
+    return arg1;
+  }
+
+  public static int staticReturn9() {
+    return 9;
+  }
+
+  public int return7(Object unused) {
+    return 7;
+  }
+
+  public static String staticReturnNull() {
+    return null;
+  }
+
+  public Object returnNull() {
+    return null;
+  }
+
+  public int getInstanceIntField() {
+    return instanceIntField;
+  }
+
+  public double getInstanceDoubleField(int unused1) {
+    return instanceDoubleField;
+  }
+
+  public Object getInstanceObjectField(long unused1) {
+    return instanceObjectField;
+  }
+
+  public String getInstanceStringField(Object unused1, String unused2, long unused3) {
+    return instanceStringField;
+  }
+
+  public static int staticGetInstanceIntField(Second s) {
+    return s.instanceIntField;
+  }
+
+  public double getInstanceDoubleFieldFromParam(Second s) {
+    return s.instanceDoubleField;
+  }
+
+  public int getStaticIntField() {
+    return staticIntField;
+  }
+
+  public void setInstanceLongField(int ignored, long value) {
+    instanceLongField = value;
+  }
+
+  public int setInstanceLongFieldReturnArg2(long value, int arg2) {
+    instanceLongField = value;
+    return arg2;
+  }
+
+  public static void staticSetInstanceLongField(Second s, long value) {
+    s.instanceLongField = value;
+  }
+
+  public void setInstanceLongFieldThroughParam(Second s, long value) {
+    s.instanceLongField = value;
+  }
+
+  public void setStaticFloatField(float value) {
+    staticFloatField = value;
+  }
+
+  public int instanceIntField = 42;
+  public double instanceDoubleField = -42.0;
+  public Object instanceObjectField = null;
+  public String instanceStringField = "dummy";
+  public long instanceLongField = 0;  // Overwritten by setters.
+
+  public static int staticIntField = 4242;
+  public static float staticFloatField = 0.0f;  // Overwritten by setters.
+}
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
new file mode 100644 (file)
index 0000000..9a85c81
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+    /// CHECK-START: void Main.staticNop() inliner (before)
+    /// CHECK:                          InvokeStaticOrDirect
+
+    /// CHECK-START: void Main.staticNop() inliner (after)
+    /// CHECK-NOT:                      InvokeStaticOrDirect
+
+    public static void staticNop() {
+      Second.staticNop(11);
+    }
+
+    /// CHECK-START: void Main.nop(Second) inliner (before)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: void Main.nop(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static void nop(Second s) {
+      s.nop();
+    }
+
+    /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
+    /// CHECK-DAG:  <<Value:l\d+>>      ParameterValue
+    /// CHECK-DAG:  <<Ignored:i\d+>>    IntConstant 77
+    /// CHECK-DAG:  <<ClinitCk:l\d+>>   ClinitCheck
+    /// CHECK-DAG:  <<Invoke:l\d+>>     InvokeStaticOrDirect [<<Ignored>>,<<Value>>,<<ClinitCk>>]
+    /// CHECK-DAG:                      Return [<<Invoke>>]
+
+    /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+    /// CHECK-DAG:  <<Value:l\d+>>      ParameterValue
+    /// CHECK-DAG:                      Return [<<Value>>]
+
+    /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+    /// CHECK-NOT:                      InvokeStaticOrDirect
+
+    public static Object staticReturnArg2(String value) {
+      return Second.staticReturnArg2(77, value);
+    }
+
+    /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
+    /// CHECK-DAG:  <<Second:l\d+>>     ParameterValue
+    /// CHECK-DAG:  <<Value:j\d+>>      ParameterValue
+    /// CHECK-DAG:  <<NullCk:l\d+>>     NullCheck [<<Second>>]
+    /// CHECK-DAG:  <<Invoke:j\d+>>     InvokeVirtual [<<NullCk>>,<<Value>>]
+    /// CHECK-DAG:                      Return [<<Invoke>>]
+
+    /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+    /// CHECK-DAG:  <<Value:j\d+>>      ParameterValue
+    /// CHECK-DAG:                      Return [<<Value>>]
+
+    /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static long returnArg1(Second s, long value) {
+      return s.returnArg1(value);
+    }
+
+    /// CHECK-START: int Main.staticReturn9() inliner (before)
+    /// CHECK:      {{i\d+}}            InvokeStaticOrDirect
+
+    /// CHECK-START: int Main.staticReturn9() inliner (before)
+    /// CHECK-NOT:                      IntConstant 9
+
+    /// CHECK-START: int Main.staticReturn9() inliner (after)
+    /// CHECK-DAG:  <<Const9:i\d+>>     IntConstant 9
+    /// CHECK-DAG:                      Return [<<Const9>>]
+
+    /// CHECK-START: int Main.staticReturn9() inliner (after)
+    /// CHECK-NOT:                      InvokeStaticOrDirect
+
+    public static int staticReturn9() {
+      return Second.staticReturn9();
+    }
+
+    /// CHECK-START: int Main.return7(Second) inliner (before)
+    /// CHECK:      {{i\d+}}            InvokeVirtual
+
+    /// CHECK-START: int Main.return7(Second) inliner (before)
+    /// CHECK-NOT:                      IntConstant 7
+
+    /// CHECK-START: int Main.return7(Second) inliner (after)
+    /// CHECK-DAG:  <<Const7:i\d+>>     IntConstant 7
+    /// CHECK-DAG:                      Return [<<Const7>>]
+
+    /// CHECK-START: int Main.return7(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static int return7(Second s) {
+      return s.return7(null);
+    }
+
+    /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+    /// CHECK:      {{l\d+}}            InvokeStaticOrDirect
+
+    /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+    /// CHECK-NOT:                      NullConstant
+
+    /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+    /// CHECK-DAG:  <<Null:l\d+>>       NullConstant
+    /// CHECK-DAG:                      Return [<<Null>>]
+
+    /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+    /// CHECK-NOT:                      InvokeStaticOrDirect
+
+    public static String staticReturnNull() {
+      return Second.staticReturnNull();
+    }
+
+    /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+    /// CHECK:      {{l\d+}}            InvokeVirtual
+
+    /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+    /// CHECK-NOT:                      NullConstant
+
+    /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+    /// CHECK-DAG:  <<Null:l\d+>>       NullConstant
+    /// CHECK-DAG:                      Return [<<Null>>]
+
+    /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static Object returnNull(Second s) {
+      return s.returnNull();
+    }
+
+    /// CHECK-START: int Main.getInt(Second) inliner (before)
+    /// CHECK:      {{i\d+}}            InvokeVirtual
+
+    /// CHECK-START: int Main.getInt(Second) inliner (after)
+    /// CHECK:      {{i\d+}}            InstanceFieldGet
+
+    /// CHECK-START: int Main.getInt(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static int getInt(Second s) {
+      return s.getInstanceIntField();
+    }
+
+    /// CHECK-START: double Main.getDouble(Second) inliner (before)
+    /// CHECK:      {{d\d+}}            InvokeVirtual
+
+    /// CHECK-START: double Main.getDouble(Second) inliner (after)
+    /// CHECK:      {{d\d+}}            InstanceFieldGet
+
+    /// CHECK-START: double Main.getDouble(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static double getDouble(Second s) {
+      return s.getInstanceDoubleField(22);
+    }
+
+    /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
+    /// CHECK:      {{l\d+}}            InvokeVirtual
+
+    /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+    /// CHECK:      {{l\d+}}            InstanceFieldGet
+
+    /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static Object getObject(Second s) {
+      return s.getInstanceObjectField(-1L);
+    }
+
+    /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
+    /// CHECK:      {{l\d+}}            InvokeVirtual
+
+    /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+    /// CHECK:      {{l\d+}}            InstanceFieldGet
+
+    /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static String getString(Second s) {
+      return s.getInstanceStringField(null, "whatever", 1234L);
+    }
+
+    /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
+    /// CHECK:      {{i\d+}}            InvokeStaticOrDirect
+
+    /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+    /// CHECK:      {{i\d+}}            InvokeStaticOrDirect
+
+    /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldGet
+
+    public static int staticGetInt(Second s) {
+      return Second.staticGetInstanceIntField(s);
+    }
+
+    /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
+    /// CHECK:      {{d\d+}}            InvokeVirtual
+
+    /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+    /// CHECK:      {{d\d+}}            InvokeVirtual
+
+    /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldGet
+
+    public static double getDoubleFromParam(Second s) {
+      return s.getInstanceDoubleFieldFromParam(s);
+    }
+
+    /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
+    /// CHECK:      {{i\d+}}            InvokeVirtual
+
+    /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+    /// CHECK:      {{i\d+}}            InvokeVirtual
+
+    /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldGet
+    /// CHECK-NOT:                      StaticFieldGet
+
+    public static int getStaticInt(Second s) {
+      return s.getStaticIntField();
+    }
+
+    /// CHECK-START: long Main.setLong(Second, long) inliner (before)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+    /// CHECK:                          InstanceFieldSet
+
+    /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static long setLong(Second s, long value) {
+      s.setInstanceLongField(-1, value);
+      return s.instanceLongField;
+    }
+
+    /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+    /// CHECK-DAG:  <<Second:l\d+>>     ParameterValue
+    /// CHECK-DAG:  <<Value:j\d+>>      ParameterValue
+    /// CHECK-DAG:  <<Arg2:i\d+>>       ParameterValue
+    /// CHECK-DAG:  <<NullCk:l\d+>>     NullCheck [<<Second>>]
+    /// CHECK-DAG:                      InstanceFieldSet [<<NullCk>>,<<Value>>]
+    /// CHECK-DAG:  <<NullCk2:l\d+>>    NullCheck [<<Second>>]
+    /// CHECK-DAG:  <<IGet:j\d+>>       InstanceFieldGet [<<NullCk2>>]
+    /// CHECK-DAG:  <<Conv:j\d+>>       TypeConversion [<<Arg2>>]
+    /// CHECK-DAG:  <<Add:j\d+>>        Add [<<IGet>>,<<Conv>>]
+    /// CHECK-DAG:                      Return [<<Add>>]
+
+    /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+    /// CHECK-NOT:                      InvokeVirtual
+
+    public static long setLongReturnArg2(Second s, long value, int arg2) {
+      int result = s.setInstanceLongFieldReturnArg2(value, arg2);
+      return s.instanceLongField + result;
+    }
+
+    /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
+    /// CHECK:                          InvokeStaticOrDirect
+
+    /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+    /// CHECK:                          InvokeStaticOrDirect
+
+    /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldSet
+
+    public static long staticSetLong(Second s, long value) {
+      Second.staticSetInstanceLongField(s, value);
+      return s.instanceLongField;
+    }
+
+    /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldSet
+
+    public static long setLongThroughParam(Second s, long value) {
+      s.setInstanceLongFieldThroughParam(s, value);
+      return s.instanceLongField;
+    }
+
+    /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+    /// CHECK:                          InvokeVirtual
+
+    /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+    /// CHECK-NOT:                      InstanceFieldSet
+    /// CHECK-NOT:                      StaticFieldSet
+
+    public static float setStaticFloat(Second s, float value) {
+      s.setStaticFloatField(value);
+      return s.staticFloatField;
+    }
+
+    /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
+    /// CHECK-DAG:  <<Obj:l\d+>>        NewInstance
+    /// CHECK-DAG:              InvokeStaticOrDirect [<<Obj>>] method_name:java.lang.Object.<init>
+
+    /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
+    /// CHECK-NOT:                      InvokeStaticOrDirect
+
+    public static Object newObject() {
+      return new Object();
+    }
+
+    public static void main(String[] args) throws Exception {
+      Second s = new Second();
+
+      // Replaced NOP pattern.
+      staticNop();
+      nop(s);
+      // Replaced "return arg" pattern.
+      assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
+      assertEquals(4321L, returnArg1(s, 4321L));
+      // Replaced "return const" pattern.
+      assertEquals(9, staticReturn9());
+      assertEquals(7, return7(s));
+      assertEquals(null, staticReturnNull());
+      assertEquals(null, returnNull(s));
+      // Replaced IGET pattern.
+      assertEquals(42, getInt(s));
+      assertEquals(-42.0, getDouble(s));
+      assertEquals(null, getObject(s));
+      assertEquals("dummy", getString(s));
+      // Not replaced IGET pattern.
+      assertEquals(42, staticGetInt(s));
+      assertEquals(-42.0, getDoubleFromParam(s));
+      // SGET.
+      assertEquals(4242, getStaticInt(s));
+      // Replaced IPUT pattern.
+      assertEquals(111L, setLong(s, 111L));
+      assertEquals(345L, setLongReturnArg2(s, 222L, 123));
+      // Not replaced IPUT pattern.
+      assertEquals(222L, staticSetLong(s, 222L));
+      assertEquals(333L, setLongThroughParam(s, 333L));
+      // SPUT.
+      assertEquals(-11.5f, setStaticFloat(s, -11.5f));
+
+      if (newObject() == null) {
+        throw new AssertionError("new Object() cannot be null.");
+      }
+    }
+
+    private static void assertEquals(int expected, int actual) {
+      if (expected != actual) {
+        throw new AssertionError("Wrong result: " + expected + " != " + actual);
+      }
+    }
+
+    private static void assertEquals(double expected, double actual) {
+      if (expected != actual) {
+        throw new AssertionError("Wrong result: " + expected + " != " + actual);
+      }
+    }
+
+    private static void assertEquals(Object expected, Object actual) {
+      if (expected != actual && (expected == null || !expected.equals(actual))) {
+        throw new AssertionError("Wrong result: " + expected + " != " + actual);
+      }
+    }
+}