Improve interpreter handler table management.

We still have two handlers table:
- the "main" table which holds execution handlers of each instruction,
- the "alternative" table which holds handlers supporting instrumentation
before jumping to the corresponding instruction handler from the "main" table.

Instrumentation holds the index of the handler table the interpreter must use.
This index is represented by the InterpreterHandlerTable enum and is stored in
the Instrumentation::interpreter_handler_table_ field.

Interpreter's current handler table update happens:
- on backward branch
- after invoke
- when throwing exception.
In the case of the backward branch and exception, we only update the table if
any thread's flags is set. This allows to only do one test for handling thread
suspension and handler table update.

This CL also removes the local variable "instrumentation". Every handler which
needs it will get it from Runtime::Current()->GetInstrumentation().

Change-Id: Id886ea7ebf3dac1285f0ca701c098aee7ebaab8d
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 481cbad..8316bc5 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -293,6 +293,7 @@
     have_exception_caught_listeners_ = true;
   }
   ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+  UpdateInterpreterHandlerTable();
 }
 
 void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
@@ -341,6 +342,7 @@
     have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0;
   }
   ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+  UpdateInterpreterHandlerTable();
 }
 
 void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 28f9555..7a0aaf7 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,6 +38,14 @@
 
 const bool kVerboseInstrumentation = false;
 
+// Interpreter handler tables.
+enum InterpreterHandlerTable {
+  kMainHandlerTable = 0,          // Main handler table: no suspend check, no instrumentation.
+  kAlternativeHandlerTable = 1,   // Alternative handler table: suspend check and/or instrumentation
+                                  // enabled.
+  kNumHandlerTables
+};
+
 // Instrumentation event listener API. Registered listeners will get the appropriate call back for
 // the events they are listening for. The call backs supply the thread, method and dex_pc the event
 // occurred upon. The thread may or may not be Thread::Current().
@@ -95,7 +103,8 @@
       interpret_only_(false), forced_interpret_only_(false),
       have_method_entry_listeners_(false), have_method_exit_listeners_(false),
       have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
-      have_exception_caught_listeners_(false) {}
+      have_exception_caught_listeners_(false),
+      interpreter_handler_table_(kMainHandlerTable) {}
 
   // Add a listener to be notified of the masked together sent of instrumentation events. This
   // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
@@ -110,6 +119,10 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
+  InterpreterHandlerTable GetInterpreterHandlerTable() const {
+    return interpreter_handler_table_;
+  }
+
   // Update the code of a method respecting any installed stubs.
   void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const;
 
@@ -149,6 +162,11 @@
     return have_dex_pc_listeners_;
   }
 
+  bool IsActive() const {
+    return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
+        have_exception_caught_listeners_ || have_method_unwind_listeners_;
+  }
+
   // Inform listeners that a method has been entered. A dex PC is provided as we may install
   // listeners into executing code and get method enter events for methods already on the stack.
   void MethodEnterEvent(Thread* thread, mirror::Object* this_object,
@@ -215,6 +233,10 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
+  void UpdateInterpreterHandlerTable() {
+    interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable;
+  }
+
   void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
                             const mirror::ArtMethod* method, uint32_t dex_pc) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -267,6 +289,9 @@
   std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Current interpreter handler table. This is updated each time the thread state flags are modified.
+  InterpreterHandlerTable interpreter_handler_table_;
+
   DISALLOW_COPY_AND_ASSIGN(Instrumentation);
 };
 
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index daf6f93..608cd41 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -51,14 +51,8 @@
     }                                                                       \
   } while (false)
 
-#define UPDATE_HANDLER_TABLE()                              \
-  do {                                                      \
-    if (UNLIKELY(instrumentation->HasDexPcListeners())) {   \
-      currentHandlersTable = instrumentationHandlersTable;  \
-    } else {                                                \
-      currentHandlersTable = handlersTable;                 \
-    }                                                       \
-  } while (false);
+#define UPDATE_HANDLER_TABLE() \
+  currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
 
 #define UNREACHABLE_CODE_CHECK()                \
   do {                                          \
@@ -70,10 +64,77 @@
 #define HANDLE_INSTRUCTION_START(opcode) op_##opcode:  // NOLINT(whitespace/labels)
 #define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()
 
+/**
+ * Interpreter based on computed goto tables.
+ *
+ * Each instruction is associated to a handler. This handler is responsible for executing the
+ * instruction and jump to the next instruction's handler.
+ * In order to limit the cost of instrumentation, we have two handler tables:
+ * - the "main" handler table: it contains handlers for normal execution of each instruction without
+ * handling of instrumentation.
+ * - the "alternative" handler table: it contains alternative handlers which first handle
+ * instrumentation before jumping to the corresponding "normal" instruction's handler.
+ *
+ * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise
+ * it uses the "main" handler table.
+ *
+ * The current handler table is the handler table being used by the interpreter. It is updated:
+ * - on backward branch (goto, if and switch instructions)
+ * - after invoke
+ * - when an exception is thrown.
+ * This allows to support an attaching debugger to an already running application for instance.
+ *
+ * For a fast handler table update, handler tables are stored in an array of handler tables. Each
+ * handler table is represented by the InterpreterHandlerTable enum which allows to associate it
+ * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable).
+ *
+ * Here's the current layout of this array of handler tables:
+ *
+ * ---------------------+---------------+
+ *                      |     NOP       | (handler for NOP instruction)
+ *                      +---------------+
+ *       "main"         |     MOVE      | (handler for MOVE instruction)
+ *    handler table     +---------------+
+ *                      |      ...      |
+ *                      +---------------+
+ *                      |   UNUSED_FF   | (handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ *                      |     NOP       | (alternative handler for NOP instruction)
+ *                      +---------------+
+ *    "alternative"     |     MOVE      | (alternative handler for MOVE instruction)
+ *    handler table     +---------------+
+ *                      |      ...      |
+ *                      +---------------+
+ *                      |   UNUSED_FF   | (alternative handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ *
+ */
 template<bool do_access_check>
 JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
                        ShadowFrame& shadow_frame, JValue result_register) {
-  bool do_assignability_check = do_access_check;
+  // Define handler tables:
+  // - The main handler table contains execution handlers for each instruction.
+  // - The alternative handler table contains prelude handlers which check for thread suspend and
+  //   manage instrumentation before jumping to the execution handler.
+  static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
+    {
+    // Main handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
+#include "dex_instruction_list.h"
+      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+    }, {
+    // Alternative handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
+#include "dex_instruction_list.h"
+      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+    }
+  };
+
+  const bool do_assignability_check = do_access_check;
   if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
     LOG(FATAL) << "Invalid shadow frame for interpreter use";
     return JValue();
@@ -81,35 +142,17 @@
   self->VerifyStack();
 
   uint32_t dex_pc = shadow_frame.GetDexPC();
-  const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
+  const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+  uint16_t inst_data;
+  const void* const* currentHandlersTable;
+  UPDATE_HANDLER_TABLE();
   if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing..
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
       instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                         shadow_frame.GetMethod(), 0);
     }
   }
-  const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
-  uint16_t inst_data;
-
-  // Define handlers table.
-  static const void* handlersTable[kNumPackedOpcodes] = {
-#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
-#include "dex_instruction_list.h"
-      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_HANDLER
-  };
-
-  static const void* instrumentationHandlersTable[kNumPackedOpcodes] = {
-#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&instrumentation_op_##code,
-#include "dex_instruction_list.h"
-      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_HANDLER
-  };
-
-  const void** currentHandlersTable;
-  UPDATE_HANDLER_TABLE();
 
   // Jump to first instruction.
   ADVANCE(0);
@@ -207,6 +250,7 @@
     if (UNLIKELY(self->TestAllFlags())) {
       CheckSuspend(self);
     }
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                        shadow_frame.GetMethod(), dex_pc,
@@ -222,6 +266,7 @@
     if (UNLIKELY(self->TestAllFlags())) {
       CheckSuspend(self);
     }
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                        shadow_frame.GetMethod(), dex_pc,
@@ -238,6 +283,7 @@
     if (UNLIKELY(self->TestAllFlags())) {
       CheckSuspend(self);
     }
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                        shadow_frame.GetMethod(), dex_pc,
@@ -253,6 +299,7 @@
     if (UNLIKELY(self->TestAllFlags())) {
       CheckSuspend(self);
     }
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                        shadow_frame.GetMethod(), dex_pc,
@@ -286,6 +333,7 @@
         HANDLE_PENDING_EXCEPTION();
       }
     }
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
       instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                        shadow_frame.GetMethod(), dex_pc,
@@ -547,8 +595,8 @@
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
         CheckSuspend(self);
+        UPDATE_HANDLER_TABLE();
       }
-      UPDATE_HANDLER_TABLE();
     }
     ADVANCE(offset);
   }
@@ -559,8 +607,8 @@
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
         CheckSuspend(self);
+        UPDATE_HANDLER_TABLE();
       }
-      UPDATE_HANDLER_TABLE();
     }
     ADVANCE(offset);
   }
@@ -571,8 +619,8 @@
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
         CheckSuspend(self);
+        UPDATE_HANDLER_TABLE();
       }
-      UPDATE_HANDLER_TABLE();
     }
     ADVANCE(offset);
   }
@@ -583,8 +631,8 @@
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
         CheckSuspend(self);
+        UPDATE_HANDLER_TABLE();
       }
-      UPDATE_HANDLER_TABLE();
     }
     ADVANCE(offset);
   }
@@ -595,8 +643,8 @@
     if (IsBackwardBranch(offset)) {
       if (UNLIKELY(self->TestAllFlags())) {
         CheckSuspend(self);
+        UPDATE_HANDLER_TABLE();
       }
-      UPDATE_HANDLER_TABLE();
     }
     ADVANCE(offset);
   }
@@ -688,8 +736,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -704,8 +752,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -720,8 +768,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -736,8 +784,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -752,8 +800,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -768,8 +816,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -784,8 +832,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -800,8 +848,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -816,8 +864,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -832,8 +880,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -848,8 +896,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -864,8 +912,8 @@
       if (IsBackwardBranch(offset)) {
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
+          UPDATE_HANDLER_TABLE();
         }
-        UPDATE_HANDLER_TABLE();
       }
       ADVANCE(offset);
     } else {
@@ -2306,8 +2354,10 @@
     CHECK(self->IsExceptionPending());
     if (UNLIKELY(self->TestAllFlags())) {
       CheckSuspend(self);
+      UPDATE_HANDLER_TABLE();
     }
     Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
                                                                   this_object,
                                                                   instrumentation);
@@ -2320,11 +2370,15 @@
   }
 
   // Create alternative instruction handlers dedicated to instrumentation.
-#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v)                        \
-  instrumentation_op_##code: {                                                                \
-    instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),  \
-                                     shadow_frame.GetMethod(), dex_pc);                       \
-    goto *handlersTable[Instruction::code];                                                   \
+#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v)                              \
+  alt_op_##code: {                                                                                  \
+      instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \
+      if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                         \
+        instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),    \
+                                         shadow_frame.GetMethod(), dex_pc);                         \
+      }                                                                                             \
+      UPDATE_HANDLER_TABLE();                                                                       \
+      goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code];                   \
   }
 #include "dex_instruction_list.h"
       DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)