Optimizing: Rewrite instruction visiting.

Improve performance by rewriting the instruction visiting in
a way that clang++ can optimize as an indexed virtual call.
This is better than dispatching via `Accept()` as we have
less branching and do not need to bring `Accept()` functions
scattered across the `libart.so` to instruction cache.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Bug: 413605257
Flag: EXEMPT refactor
Change-Id: I8e3f6fdd880a44679e4b96e35457a18cdf0bdb34
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index b44e3b0..6fd1d0e 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -540,13 +540,13 @@
     for (HInstruction* instruction = block->GetFirstPhi(); instruction != nullptr;) {
       DCHECK(instruction->IsInBlock());
       next_ = instruction->GetNext();
-      instruction->Accept(this);
+      VisitPhi(instruction->AsPhi());
       instruction = next_;
     }
     for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
       DCHECK(instruction->IsInBlock());
       next_ = instruction->GetNext();
-      instruction->Accept(this);
+      Dispatch(instruction);
       instruction = next_;
     }
     // We should never deoptimize from an osr method, otherwise we might wrongly optimize
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
index e7e34d8..2f009fa 100644
--- a/compiler/optimizing/cha_guard_optimization.cc
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -81,7 +81,7 @@
   instruction_iterator_ = ⁢
   for (; !it.Done(); it.Advance()) {
     DCHECK(it.Current()->IsInBlock());
-    it.Current()->Accept(this);
+    Dispatch(it.Current());
   }
 }
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 935cf72..d013467 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -365,7 +365,7 @@
       }
       DisassemblyScope disassembly_scope(current, *this);
       DCHECK(CheckTypeConsistency(current));
-      current->Accept(instruction_visitor);
+      instruction_visitor->Dispatch(current);
     }
   }
 
@@ -914,7 +914,7 @@
   for (HEnvironment* env = instruction->GetEnvironment(); env != nullptr; env = env->GetParent()) {
     env->AllocateLocations(allocator);
   }
-  instruction->Accept(GetLocationBuilder());
+  GetLocationBuilder()->Dispatch(instruction);
   DCHECK(CheckTypeConsistency(instruction));
   LocationSummary* locations = instruction->GetLocations();
   if (!instruction->IsSuspendCheckEntry()) {
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 66fb04e..12cfe08 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -205,7 +205,7 @@
     // Already replaced inside TryRemoveBinaryOperationViaSelect.
   } else {
     InstructionWithAbsorbingInputSimplifier simplifier(GetGraph());
-    inst->Accept(&simplifier);
+    simplifier.Dispatch(inst);
   }
 }
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 3ad0cd2..0b2e916 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -292,7 +292,7 @@
                             current_block_->GetBlockId(),
                             current->GetId()));
     }
-    current->Accept(this);
+    Dispatch(current);
   }
 
   // Visit this block's list of instructions.
@@ -310,7 +310,7 @@
                        current_block_->GetBlockId(),
                        current->GetId()));
     }
-    current->Accept(this);
+    Dispatch(current);
   }
 
   // Ensure that catch blocks are not normal successors, and normal blocks are
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 3c3e3a0..3984ea6 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -702,7 +702,7 @@
     HBasicBlock* block = instruction->GetBlock();
     StartAttributeStream("block") << namer_.GetName(block);
 
-    instruction->Accept(this);
+    Dispatch(instruction);
     if (instruction->HasEnvironment()) {
       StringList envs;
       for (HEnvironment* environment = instruction->GetEnvironment();
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 277d459..25b7405 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -64,7 +64,7 @@
     for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       if (instruction->IsInBlock()) {
-        instruction->Accept(this);
+        Dispatch(instruction);
       }
     }
   }
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 339b006..69b2e95 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -66,7 +66,7 @@
     for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       if (instruction->IsInBlock()) {
-        instruction->Accept(this);
+        Dispatch(instruction);
       }
     }
   }
diff --git a/compiler/optimizing/instruction_simplifier_riscv64.cc b/compiler/optimizing/instruction_simplifier_riscv64.cc
index 91b1598..789c431 100644
--- a/compiler/optimizing/instruction_simplifier_riscv64.cc
+++ b/compiler/optimizing/instruction_simplifier_riscv64.cc
@@ -36,7 +36,7 @@
     for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       if (instruction->IsInBlock()) {
-        instruction->Accept(this);
+        Dispatch(instruction);
       }
     }
   }
diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc
index 7dec4c5..a80dde8 100644
--- a/compiler/optimizing/instruction_simplifier_x86.cc
+++ b/compiler/optimizing/instruction_simplifier_x86.cc
@@ -42,7 +42,7 @@
     for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       if (instruction->IsInBlock()) {
-        instruction->Accept(this);
+        Dispatch(instruction);
       }
     }
   }
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.cc b/compiler/optimizing/instruction_simplifier_x86_64.cc
index 175369b..eb3227f 100644
--- a/compiler/optimizing/instruction_simplifier_x86_64.cc
+++ b/compiler/optimizing/instruction_simplifier_x86_64.cc
@@ -42,7 +42,7 @@
     for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       if (instruction->IsInBlock()) {
-        instruction->Accept(this);
+        Dispatch(instruction);
       }
     }
   }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 184b2a6..1c48834 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1718,15 +1718,6 @@
   return nullptr;
 }
 
-#define DEFINE_ACCEPT(name, super)                                             \
-void H##name::Accept(HGraphVisitor* visitor) {                                 \
-  visitor->Visit##name(this);                                                  \
-}
-
-FOR_EACH_CONCRETE_INSTRUCTION(DEFINE_ACCEPT)
-
-#undef DEFINE_ACCEPT
-
 void HGraphVisitor::VisitInsertionOrder() {
   for (HBasicBlock* block : graph_->GetActiveBlocks()) {
     VisitBasicBlock(block);
@@ -1754,14 +1745,14 @@
 void HGraphVisitor::VisitNonPhiInstructions(HBasicBlock* block) {
   for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
     DCHECK(!it.Current()->IsPhi());
-    it.Current()->Accept(this);
+    Dispatch(it.Current());
   }
 }
 
 void HGraphVisitor::VisitNonPhiInstructionsHandleChanges(HBasicBlock* block) {
   for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
     DCHECK(!it.Current()->IsPhi());
-    it.Current()->Accept(this);
+    Dispatch(it.Current());
   }
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 513e37f..7f8f87e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1519,8 +1519,7 @@
   HInstruction* Clone(ArenaAllocator* arena) const override {             \
     DCHECK(IsClonable());                                                 \
     return new (arena) H##type(*this);                                    \
-  }                                                                       \
-  void Accept(HGraphVisitor* visitor) override
+  }
 
 #define DECLARE_ABSTRACT_INSTRUCTION(type)                              \
   private:                                                              \
@@ -2154,7 +2153,6 @@
     SetRawInputRecordAt(index, HUserRecord<HInstruction*>(input));
   }
 
-  virtual void Accept(HGraphVisitor* visitor) = 0;
   virtual const char* DebugName() const = 0;
 
   DataType::Type GetType() const {
@@ -8417,6 +8415,30 @@
   FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
 #undef DECLARE_VISIT_INSTRUCTION
 
+  ALWAYS_INLINE void Dispatch(HInstruction* insn) {
+    HInstruction::InstructionKind kind;
+    // Use `asm volatile` to prevent clang++ from optimizing the `kind = insn->GetKind()`
+    // together with the `switch`. The simple expression can somehow derail the
+    // `switch` optimization and result in a much worse compiled code. b/413605257
+    asm volatile("" : "=r"(kind) : "0"(insn->GetKind()));
+
+    switch (kind) {
+    #define DEFINE_DISPATCH_CASE(kind, super)                 \
+      case HInstruction::k##kind:                             \
+        Visit##kind(insn->As##kind());                        \
+        break;
+      FOR_EACH_CONCRETE_INSTRUCTION(DEFINE_DISPATCH_CASE)
+    #undef DEFINE_DISPATCH_CASE
+      default:
+        // Note: clang++ can optimize this `switch` to a virtual dispatch with indexed
+        // load from the vtable using an adjusted `invoke->GetKind()` as the index.
+        // However, a non-empty `default` or `case` causes clang++ to produce much
+        // worse code, so we want to limit this check to debug builds only.
+        DCHECK(false) << "UNREACHABLE";
+        UNREACHABLE();
+    }
+  }
+
  protected:
   void VisitPhis(HBasicBlock* block);
   void VisitNonPhiInstructions(HBasicBlock* block);
@@ -8436,7 +8458,7 @@
       : HGraphVisitor(graph, stats) {}
   virtual ~HGraphDelegateVisitor() {}
 
-  // Visit functions that delegate to to super class.
+  // Visit functions that delegate to super class.
 #define DECLARE_VISIT_ABSTRACT_INSTRUCTION(name, super)               \
   virtual void Visit##name(H##name* instr) { Visit##super(instr); }
   FOR_EACH_ABSTRACT_INSTRUCTION(DECLARE_VISIT_ABSTRACT_INSTRUCTION)
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index cc9ba6e..8a9e09a 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -63,7 +63,7 @@
     // No need to visit the phis.
     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
          inst_it.Advance()) {
-      inst_it.Current()->Accept(&visitor);
+      visitor.Dispatch(inst_it.Current());
     }
   }
   return true;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 5414345..3b3324b 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -124,7 +124,7 @@
 
 void ReferenceTypePropagation::Visit(HInstruction* instruction) {
   RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
-  instruction->Accept(&visitor);
+  visitor.Dispatch(instruction);
 }
 
 void ReferenceTypePropagation::Visit(ArrayRef<HInstruction* const> instructions) {
@@ -136,7 +136,7 @@
     }
   }
   for (HInstruction* instruction : instructions) {
-    instruction->Accept(&visitor);
+    visitor.Dispatch(instruction);
     // We don't know if the instruction list is ordered in the same way normal
     // visiting would be so we need to process every instruction manually.
     if (RTPVisitor::IsUpdateable(instruction)) {
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index 324e1dd..f781190 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -414,14 +414,10 @@
     UNREACHABLE();
   }
 
-  void Visit(HInstruction* instruction) {
-    instruction->Accept(this);
-  }
-
   void CalculateLatency(SchedulingNode* node) {
     // By default nodes have no internal latency.
     last_visited_internal_latency_ = 0;
-    Visit(node->GetInstruction());
+    Dispatch(node->GetInstruction());
   }
 
   uint32_t GetLastVisitedLatency() const { return last_visited_latency_; }