Try to substitute constructor chains for IPUTs.

Match a constructor chain where each constructor either
forwards some or all of its arguments to the next (i.e.
superclass constructor or a constructor in the same class)
and may pass extra zeros (of any type, including null),
followed by any number of IPUTs on "this", storing either
arguments or zeros, until we reach the contructor of
java.lang.Object.

When collecting IPUTs from the constructor chain, remove
any IPUTs that store the same field as an IPUT that comes
later. This is safe in this case even if those IPUTs store
volatile fields because the uninitialized object reference
wasn't allowed to escape yet. Also remove any IPUTs that
store zero values as the allocated object is already zero
initialized.

Change-Id: If93022310bf04fe38ee741665ac4a65d4c2bb25f
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 9b91b53..34a5e34 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -612,8 +612,9 @@
         // TODO: Needs null check.
         return false;
       }
+      Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
       HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
-      HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+      HInstanceFieldGet* iget = CreateInstanceFieldGet(dex_cache, 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);
@@ -626,9 +627,10 @@
         // TODO: Needs null check.
         return false;
       }
+      Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
       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);
+      HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, 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);
@@ -638,6 +640,59 @@
       }
       break;
     }
+    case kInlineOpConstructor: {
+      const InlineConstructorData& data = inline_method.d.constructor_data;
+      // Get the indexes to arrays for easier processing.
+      uint16_t iput_field_indexes[] = {
+          data.iput0_field_index, data.iput1_field_index, data.iput2_field_index
+      };
+      uint16_t iput_args[] = { data.iput0_arg, data.iput1_arg, data.iput2_arg };
+      static_assert(arraysize(iput_args) == arraysize(iput_field_indexes), "Size mismatch");
+      // Count valid field indexes.
+      size_t number_of_iputs = 0u;
+      while (number_of_iputs != arraysize(iput_field_indexes) &&
+          iput_field_indexes[number_of_iputs] != DexFile::kDexNoIndex16) {
+        // Check that there are no duplicate valid field indexes.
+        DCHECK_EQ(0, std::count(iput_field_indexes + number_of_iputs + 1,
+                                iput_field_indexes + arraysize(iput_field_indexes),
+                                iput_field_indexes[number_of_iputs]));
+        ++number_of_iputs;
+      }
+      // Check that there are no valid field indexes in the rest of the array.
+      DCHECK_EQ(0, std::count_if(iput_field_indexes + number_of_iputs,
+                                 iput_field_indexes + arraysize(iput_field_indexes),
+                                 [](uint16_t index) { return index != DexFile::kDexNoIndex16; }));
+
+      // Create HInstanceFieldSet for each IPUT that stores non-zero data.
+      Handle<mirror::DexCache> dex_cache;
+      HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, /* this */ 0u);
+      bool needs_constructor_barrier = false;
+      for (size_t i = 0; i != number_of_iputs; ++i) {
+        HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, iput_args[i]);
+        if (!value->IsConstant() ||
+            (!value->AsConstant()->IsZero() && !value->IsNullConstant())) {
+          if (dex_cache.GetReference() == nullptr) {
+            dex_cache = handles_->NewHandle(resolved_method->GetDexCache());
+          }
+          uint16_t field_index = iput_field_indexes[i];
+          HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, field_index, obj, value);
+          invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+
+          // Check whether the field is final. If it is, we need to add a barrier.
+          size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+          ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+          DCHECK(resolved_field != nullptr);
+          if (resolved_field->IsFinal()) {
+            needs_constructor_barrier = true;
+          }
+        }
+      }
+      if (needs_constructor_barrier) {
+        HMemoryBarrier* barrier = new (graph_->GetArena()) HMemoryBarrier(kStoreStore, kNoDexPc);
+        invoke_instruction->GetBlock()->InsertInstructionBefore(barrier, invoke_instruction);
+      }
+      break;
+    }
     default:
       LOG(FATAL) << "UNREACHABLE";
       UNREACHABLE();
@@ -652,11 +707,10 @@
   return true;
 }
 
-HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex_cache,
                                                     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);
@@ -667,7 +721,7 @@
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
-      *resolved_method->GetDexFile(),
+      *dex_cache->GetDexFile(),
       dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
@@ -679,12 +733,11 @@
   return iget;
 }
 
-HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex_cache,
                                                     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);
@@ -696,7 +749,7 @@
       resolved_field->IsVolatile(),
       field_index,
       resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
-      *resolved_method->GetDexFile(),
+      *dex_cache->GetDexFile(),
       dex_cache,
       // Read barrier generates a runtime call in slow path and we need a valid
       // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.