Step 2 of 2: conditional passes.

Rationale:
The change introduces actual conditional passes
(dependence on inliner). This ensures more
cases are optimized downstream without
needlessly introducing compile-time.

NOTE:
Some checker tests needed to be rewritten
due to subtle changes in the phase ordering.
No optimizations were harmed in the process,
though.

Bug: b/78171933, b/74026074

Test: test-art-host,target

Change-Id: I335260df780e14ba1f22499ad74d79060c7be44d
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 57db7a6..d37c43d 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -121,12 +121,15 @@
     case OptimizationPass::kX86MemoryOperandGeneration:
       return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName;
 #endif
+    case OptimizationPass::kNone:
+      LOG(FATAL) << "kNone does not represent an actual pass";
+      UNREACHABLE();
   }
 }
 
-#define X(x) if (name == OptimizationPassName((x))) return (x)
+#define X(x) if (pass_name == OptimizationPassName((x))) return (x)
 
-OptimizationPass OptimizationPassByName(const std::string& name) {
+OptimizationPass OptimizationPassByName(const std::string& pass_name) {
   X(OptimizationPass::kBoundsCheckElimination);
   X(OptimizationPass::kCHAGuardOptimization);
   X(OptimizationPass::kCodeSinking);
@@ -160,7 +163,7 @@
   X(OptimizationPass::kPcRelativeFixupsX86);
   X(OptimizationPass::kX86MemoryOperandGeneration);
 #endif
-  LOG(FATAL) << "Cannot find optimization " << name;
+  LOG(FATAL) << "Cannot find optimization " << pass_name;
   UNREACHABLE();
 }
 
@@ -187,9 +190,9 @@
 
   // Loop over the requested optimizations.
   for (size_t i = 0; i < length; i++) {
-    OptimizationPass pass = definitions[i].first;
-    const char* alt_name = definitions[i].second;
-    const char* name = alt_name != nullptr
+    OptimizationPass pass = definitions[i].pass;
+    const char* alt_name = definitions[i].pass_name;
+    const char* pass_name = alt_name != nullptr
         ? alt_name
         : OptimizationPassName(pass);
     HOptimization* opt = nullptr;
@@ -199,47 +202,48 @@
       // Analysis passes (kept in most recent for subsequent passes).
       //
       case OptimizationPass::kSideEffectsAnalysis:
-        opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name);
+        opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, pass_name);
         break;
       case OptimizationPass::kInductionVarAnalysis:
-        opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name);
+        opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, pass_name);
         break;
       case OptimizationPass::kLoadStoreAnalysis:
-        opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name);
+        opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, pass_name);
         break;
       //
       // Passes that need prior analysis.
       //
       case OptimizationPass::kGlobalValueNumbering:
         CHECK(most_recent_side_effects != nullptr);
-        opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name);
+        opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name);
         break;
       case OptimizationPass::kInvariantCodeMotion:
         CHECK(most_recent_side_effects != nullptr);
-        opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name);
+        opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, pass_name);
         break;
       case OptimizationPass::kLoopOptimization:
         CHECK(most_recent_induction != nullptr);
-        opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name);
+        opt = new (allocator) HLoopOptimization(
+            graph, driver, most_recent_induction, stats, pass_name);
         break;
       case OptimizationPass::kBoundsCheckElimination:
         CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr);
         opt = new (allocator) BoundsCheckElimination(
-            graph, *most_recent_side_effects, most_recent_induction, name);
+            graph, *most_recent_side_effects, most_recent_induction, pass_name);
         break;
       case OptimizationPass::kLoadStoreElimination:
         CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr);
         opt = new (allocator) LoadStoreElimination(
-            graph, *most_recent_side_effects, *most_recent_lsa, stats, name);
+            graph, *most_recent_side_effects, *most_recent_lsa, stats, pass_name);
         break;
       //
       // Regular passes.
       //
       case OptimizationPass::kConstantFolding:
-        opt = new (allocator) HConstantFolding(graph, name);
+        opt = new (allocator) HConstantFolding(graph, pass_name);
         break;
       case OptimizationPass::kDeadCodeElimination:
-        opt = new (allocator) HDeadCodeElimination(graph, stats, name);
+        opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name);
         break;
       case OptimizationPass::kInliner: {
         CodeItemDataAccessor accessor(*dex_compilation_unit.GetDexFile(),
@@ -256,33 +260,33 @@
                                        /* total_number_of_instructions */ 0,
                                        /* parent */ nullptr,
                                        /* depth */ 0,
-                                       name);
+                                       pass_name);
         break;
       }
       case OptimizationPass::kSharpening:
-        opt = new (allocator) HSharpening(graph, codegen, driver, name);
+        opt = new (allocator) HSharpening(graph, codegen, driver, pass_name);
         break;
       case OptimizationPass::kSelectGenerator:
-        opt = new (allocator) HSelectGenerator(graph, handles, stats, name);
+        opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name);
         break;
       case OptimizationPass::kInstructionSimplifier:
-        opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name);
+        opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name);
         break;
       case OptimizationPass::kIntrinsicsRecognizer:
-        opt = new (allocator) IntrinsicsRecognizer(graph, stats, name);
+        opt = new (allocator) IntrinsicsRecognizer(graph, stats, pass_name);
         break;
       case OptimizationPass::kCHAGuardOptimization:
-        opt = new (allocator) CHAGuardOptimization(graph, name);
+        opt = new (allocator) CHAGuardOptimization(graph, pass_name);
         break;
       case OptimizationPass::kCodeSinking:
-        opt = new (allocator) CodeSinking(graph, stats, name);
+        opt = new (allocator) CodeSinking(graph, stats, pass_name);
         break;
       case OptimizationPass::kConstructorFenceRedundancyElimination:
-        opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name);
+        opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, pass_name);
         break;
       case OptimizationPass::kScheduling:
         opt = new (allocator) HInstructionScheduling(
-            graph, driver->GetInstructionSet(), codegen, name);
+            graph, driver->GetInstructionSet(), codegen, pass_name);
         break;
       //
       // Arch-specific passes.
@@ -319,11 +323,14 @@
         opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats);
         break;
 #endif
+      case OptimizationPass::kNone:
+        LOG(FATAL) << "kNone does not represent an actual pass";
+        UNREACHABLE();
     }  // switch
 
     // Add each next optimization to result vector.
     CHECK(opt != nullptr);
-    DCHECK_STREQ(name, opt->GetPassName());  // sanity
+    DCHECK_STREQ(pass_name, opt->GetPassName());  // sanity
     optimizations.push_back(opt);
   }
 
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index b00d686..88b283c 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -102,21 +102,32 @@
 #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
   kX86MemoryOperandGeneration,
 #endif
+  kNone,
+  kLast = kNone
 };
 
 // Lookup name of optimization pass.
 const char* OptimizationPassName(OptimizationPass pass);
 
 // Lookup optimization pass by name.
-OptimizationPass OptimizationPassByName(const std::string& name);
+OptimizationPass OptimizationPassByName(const std::string& pass_name);
 
 // Optimization definition consisting of an optimization pass
-// and an optional alternative name (nullptr denotes default).
-typedef std::pair<OptimizationPass, const char*> OptimizationDef;
+// an optional alternative name (nullptr denotes default), and
+// an optional pass dependence (kNone denotes no dependence).
+struct OptimizationDef {
+  OptimizationDef(OptimizationPass p, const char* pn, OptimizationPass d)
+      : pass(p), pass_name(pn), depends_on(d) {}
+  OptimizationPass pass;
+  const char* pass_name;
+  OptimizationPass depends_on;
+};
 
 // Helper method for optimization definition array entries.
-inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) {
-  return std::make_pair(pass, name);
+inline OptimizationDef OptDef(OptimizationPass pass,
+                              const char* pass_name = nullptr,
+                              OptimizationPass depends_on = OptimizationPass::kNone) {
+  return OptimizationDef(pass, pass_name, depends_on);
 }
 
 // Helper method to construct series of optimization passes.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index f68bcbe..a6163a7 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -313,11 +313,22 @@
         dex_compilation_unit,
         handles);
     DCHECK_EQ(length, optimizations.size());
-    // Run the optimization passes one by one.
+    // Run the optimization passes one by one. Any "depends_on" pass refers back to
+    // the most recent occurrence of that pass, skipped or executed.
+    std::bitset<static_cast<size_t>(OptimizationPass::kLast) + 1u> pass_changes;
+    pass_changes[static_cast<size_t>(OptimizationPass::kNone)] = true;
     bool change = false;
     for (size_t i = 0; i < length; ++i) {
-      PassScope scope(optimizations[i]->GetPassName(), pass_observer);
-      change |= optimizations[i]->Run();
+      if (pass_changes[static_cast<size_t>(definitions[i].depends_on)]) {
+        // Execute the pass and record whether it changed anything.
+        PassScope scope(optimizations[i]->GetPassName(), pass_observer);
+        bool pass_change = optimizations[i]->Run();
+        pass_changes[static_cast<size_t>(definitions[i].pass)] = pass_change;
+        change |= pass_change;
+      } else {
+        // Skip the pass and record that nothing changed.
+        pass_changes[static_cast<size_t>(definitions[i].pass)] = false;
+      }
     }
     return change;
   }
@@ -579,6 +590,7 @@
   if (pass_names != nullptr) {
     // If passes were defined on command-line, build the optimization
     // passes and run these instead of the built-in optimizations.
+    // TODO: a way to define depends_on via command-line?
     const size_t length = pass_names->size();
     std::vector<OptimizationDef> optimizations;
     for (const std::string& pass_name : *pass_names) {
@@ -596,36 +608,63 @@
   }
 
   OptimizationDef optimizations[] = {
+    // Initial optimizations.
     OptDef(OptimizationPass::kIntrinsicsRecognizer),
     OptDef(OptimizationPass::kSharpening),
     OptDef(OptimizationPass::kConstantFolding),
     OptDef(OptimizationPass::kInstructionSimplifier),
-    OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$initial"),
+    // Inlining.
     OptDef(OptimizationPass::kInliner),
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_gvn"),
+    // Simplification (only if inlining occurred).
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_inlining",
+           OptimizationPass::kInliner),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_inlining",
+           OptimizationPass::kInliner),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$after_inlining",
+           OptimizationPass::kInliner),
+    // GVN.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_gvn"),
     OptDef(OptimizationPass::kGlobalValueNumbering),
+    // Simplification (TODO: only if GVN occurred).
     OptDef(OptimizationPass::kSelectGenerator),
-    OptDef(OptimizationPass::kConstantFolding,       "constant_folding$after_inlining"),
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"),
-    OptDef(OptimizationPass::kDeadCodeElimination,   "dead_code_elimination$after_inlining"),
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_licm"),
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_gvn"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_gvn"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$after_gvn"),
+    // High-level optimizations.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_licm"),
     OptDef(OptimizationPass::kInvariantCodeMotion),
     OptDef(OptimizationPass::kInductionVarAnalysis),
     OptDef(OptimizationPass::kBoundsCheckElimination),
     OptDef(OptimizationPass::kLoopOptimization),
-    // Evaluates code generated by dynamic bce.
-    OptDef(OptimizationPass::kConstantFolding,       "constant_folding$after_bce"),
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"),
-    OptDef(OptimizationPass::kSideEffectsAnalysis,   "side_effects$before_lse"),
+    // Simplification.
+    OptDef(OptimizationPass::kConstantFolding,
+           "constant_folding$after_bce"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$after_bce"),
+    // Other high-level optimizations.
+    OptDef(OptimizationPass::kSideEffectsAnalysis,
+           "side_effects$before_lse"),
     OptDef(OptimizationPass::kLoadStoreAnalysis),
     OptDef(OptimizationPass::kLoadStoreElimination),
     OptDef(OptimizationPass::kCHAGuardOptimization),
-    OptDef(OptimizationPass::kDeadCodeElimination,   "dead_code_elimination$final"),
+    OptDef(OptimizationPass::kDeadCodeElimination,
+           "dead_code_elimination$final"),
     OptDef(OptimizationPass::kCodeSinking),
     // The codegen has a few assumptions that only the instruction simplifier
     // can satisfy. For example, the code generator does not expect to see a
     // HTypeConversion from a type to the same type.
-    OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"),
+    OptDef(OptimizationPass::kInstructionSimplifier,
+           "instruction_simplifier$before_codegen"),
     // Eliminate constructor fences after code sinking to avoid
     // complicated sinking logic to split a fence with many inputs.
     OptDef(OptimizationPass::kConstructorFenceRedundancyElimination)