Fix nested deoptimization.

Handle nested deoptimization cases. Create a stacked shadow frame
records to keep track of deoptimization shadow frames. Shadow frames
under construction can be tracked in the same stack.

Bug: 20845490
Change-Id: I768285792c29e7c3cfcd21e7a2600802506024d8
diff --git a/runtime/thread.h b/runtime/thread.h
index 3f0d0a5..e996fc9 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -99,6 +99,55 @@
   kCheckpointRequest = 2  // Request that the thread do some checkpoint work and then continue.
 };
 
+enum StackedShadowFrameType {
+  kShadowFrameUnderConstruction,
+  kDeoptimizationShadowFrame
+};
+
+class StackedShadowFrameRecord {
+ public:
+  StackedShadowFrameRecord(ShadowFrame* shadow_frame,
+                           StackedShadowFrameType type,
+                           StackedShadowFrameRecord* link)
+      : shadow_frame_(shadow_frame),
+        type_(type),
+        link_(link) {}
+
+  ShadowFrame* GetShadowFrame() const { return shadow_frame_; }
+  bool GetType() const { return type_; }
+  StackedShadowFrameRecord* GetLink() const { return link_; }
+
+ private:
+  ShadowFrame* const shadow_frame_;
+  const StackedShadowFrameType type_;
+  StackedShadowFrameRecord* const link_;
+
+  DISALLOW_COPY_AND_ASSIGN(StackedShadowFrameRecord);
+};
+
+class DeoptimizationReturnValueRecord {
+ public:
+  DeoptimizationReturnValueRecord(const JValue& ret_val,
+                                  bool is_reference,
+                                  DeoptimizationReturnValueRecord* link)
+      : ret_val_(ret_val), is_reference_(is_reference), link_(link) {}
+
+  JValue GetReturnValue() const { return ret_val_; }
+  bool IsReference() const { return is_reference_; }
+  DeoptimizationReturnValueRecord* GetLink() const { return link_; }
+  mirror::Object** GetGCRoot() {
+    DCHECK(is_reference_);
+    return ret_val_.GetGCRoot();
+  }
+
+ private:
+  JValue ret_val_;
+  const bool is_reference_;
+  DeoptimizationReturnValueRecord* const link_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeoptimizationReturnValueRecord);
+};
+
 static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34;
 
 // Thread's stack layout for implicit stack overflow checks:
@@ -790,21 +839,25 @@
     return reinterpret_cast<mirror::Throwable*>(-1);
   }
 
-  void SetDeoptimizationShadowFrame(ShadowFrame* sf);
-  void SetDeoptimizationReturnValue(const JValue& ret_val);
-
-  ShadowFrame* GetAndClearDeoptimizationShadowFrame(JValue* ret_val);
-
-  bool HasDeoptimizationShadowFrame() const {
-    return tlsPtr_.deoptimization_shadow_frame != nullptr;
+  // Currently deoptimization invokes verifier which can trigger class loading
+  // and execute Java code, so there might be nested deoptimizations happening.
+  // We need to save the ongoing deoptimization shadow frames and return
+  // values on stacks.
+  void SetDeoptimizationReturnValue(const JValue& ret_val, bool is_reference) {
+    tls64_.deoptimization_return_value.SetJ(ret_val.GetJ());
+    tls32_.deoptimization_return_value_is_reference = is_reference;
   }
-
-  void SetShadowFrameUnderConstruction(ShadowFrame* sf);
-  void ClearShadowFrameUnderConstruction();
-
-  bool HasShadowFrameUnderConstruction() const {
-    return tlsPtr_.shadow_frame_under_construction != nullptr;
+  bool IsDeoptimizationReturnValueReference() {
+    return tls32_.deoptimization_return_value_is_reference;
   }
+  void ClearDeoptimizationReturnValue() {
+    tls64_.deoptimization_return_value.SetJ(0);
+    tls32_.deoptimization_return_value_is_reference = false;
+  }
+  void PushAndClearDeoptimizationReturnValue();
+  JValue PopDeoptimizationReturnValue();
+  void PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type);
+  ShadowFrame* PopStackedShadowFrame(StackedShadowFrameType type);
 
   std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() {
     return tlsPtr_.instrumentation_stack;
@@ -1048,7 +1101,8 @@
     explicit tls_32bit_sized_values(bool is_daemon) :
       suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
       daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
-      thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false),
+      thread_exit_check_count(0), handling_signal_(false),
+      deoptimization_return_value_is_reference(false), suspended_at_suspend_check(false),
       ready_for_debug_invoke(false), debug_method_entry_(false) {
     }
 
@@ -1089,6 +1143,10 @@
     // True if signal is being handled by this thread.
     bool32_t handling_signal_;
 
+    // True if the return value for interpreter after deoptimization is a reference.
+    // For gc purpose.
+    bool32_t deoptimization_return_value_is_reference;
+
     // True if the thread is suspended in FullSuspendCheck(). This is
     // used to distinguish runnable threads that are suspended due to
     // a normal suspend check from other threads.
@@ -1124,8 +1182,9 @@
       stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
       top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
       instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
-      deoptimization_shadow_frame(nullptr), shadow_frame_under_construction(nullptr), name(nullptr),
-      pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
+      stacked_shadow_frame_record(nullptr), deoptimization_return_value_stack(nullptr),
+      name(nullptr), pthread_self(0),
+      last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
       thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
       thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
       nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr) {
@@ -1201,11 +1260,13 @@
     // JDWP single-stepping support.
     SingleStepControl* single_step_control;
 
-    // Shadow frame stack that is used temporarily during the deoptimization of a method.
-    ShadowFrame* deoptimization_shadow_frame;
+    // For gc purpose, a shadow frame record stack that keeps track of:
+    // 1) shadow frames under construction.
+    // 2) deoptimization shadow frames.
+    StackedShadowFrameRecord* stacked_shadow_frame_record;
 
-    // Shadow frame stack that is currently under construction but not yet on the stack
-    ShadowFrame* shadow_frame_under_construction;
+    // Deoptimization return value record stack.
+    DeoptimizationReturnValueRecord* deoptimization_return_value_stack;
 
     // A cached copy of the java.lang.Thread's name.
     std::string* name;
@@ -1293,6 +1354,23 @@
   const char* const old_cause_;
 };
 
+class ScopedStackedShadowFramePusher {
+ public:
+  ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf, StackedShadowFrameType type)
+    : self_(self), type_(type) {
+    self_->PushStackedShadowFrame(sf, type);
+  }
+  ~ScopedStackedShadowFramePusher() {
+    self_->PopStackedShadowFrame(type_);
+  }
+
+ private:
+  Thread* const self_;
+  const StackedShadowFrameType type_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedStackedShadowFramePusher);
+};
+
 std::ostream& operator<<(std::ostream& os, const Thread& thread);
 
 }  // namespace art