ART: Enable multi-level instruction inlining

Change-Id: I4b4c927d7b1598dc197793c25185fb079dec7fe1
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 7ed3c84..1dd3508 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -174,6 +174,38 @@
   ComputeLiveInAndLiveOutSets();
 }
 
+static void RecursivelyProcessInputs(HInstruction* current,
+                                     HInstruction* actual_user,
+                                     BitVector* live_in) {
+  for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
+    HInstruction* input = current->InputAt(i);
+    bool has_in_location = current->GetLocations()->InAt(i).IsValid();
+    bool has_out_location = input->GetLocations()->Out().IsValid();
+
+    if (has_in_location) {
+      DCHECK(has_out_location)
+          << "Instruction " << current->DebugName() << current->GetId()
+          << " expects an input value at index " << i << " but "
+          << input->DebugName() << input->GetId() << " does not produce one.";
+      DCHECK(input->HasSsaIndex());
+      // `input` generates a result used by `current`. Add use and update
+      // the live-in set.
+      input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i, actual_user);
+      live_in->SetBit(input->GetSsaIndex());
+    } else if (has_out_location) {
+      // `input` generates a result but it is not used by `current`.
+    } else {
+      // `input` is inlined into `current`. Walk over its inputs and record
+      // uses at `current`.
+      DCHECK(input->IsEmittedAtUseSite());
+      // Check that the inlined input is not a phi. Recursing on loop phis could
+      // lead to an infinite loop.
+      DCHECK(!input->IsPhi());
+      RecursivelyProcessInputs(input, actual_user, live_in);
+    }
+  }
+}
+
 void SsaLivenessAnalysis::ComputeLiveRanges() {
   // Do a post order visit, adding inputs of instructions live in the block where
   // that instruction is defined, and killing instructions that are being visited.
@@ -261,35 +293,7 @@
           DCHECK(!current->HasEnvironmentUses());
         }
       } else {
-        for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
-          HInstruction* input = current->InputAt(i);
-          bool has_in_location = current->GetLocations()->InAt(i).IsValid();
-          bool has_out_location = input->GetLocations()->Out().IsValid();
-
-          if (has_in_location) {
-            DCHECK(has_out_location);
-            DCHECK(input->HasSsaIndex());
-            // `Input` generates a result used by `current`. Add use and update
-            // the live-in set.
-            input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
-            live_in->SetBit(input->GetSsaIndex());
-          } else if (has_out_location) {
-            // `Input` generates a result but it is not used by `current`.
-          } else {
-            // `Input` is inlined into `current`. Walk over its inputs and record
-            // uses at `current`.
-            DCHECK(input->IsEmittedAtUseSite());
-            for (size_t i2 = 0, e2 = input->InputCount(); i2 < e2; ++i2) {
-              HInstruction* inlined_input = input->InputAt(i2);
-              DCHECK(inlined_input->HasSsaIndex()) << "Recursive inlining not allowed.";
-              if (input->GetLocations()->InAt(i2).IsValid()) {
-                live_in->SetBit(inlined_input->GetSsaIndex());
-                inlined_input->GetLiveInterval()->AddUse(
-                    /* owner */ input, /* environment */ nullptr, i2, /* actual_user */ current);
-              }
-            }
-          }
-        }
+        RecursivelyProcessInputs(current, current, live_in);
       }
     }