Correctly perform read-barriers on cross-thread reg access

For debug workloads we will sometimes read stack registers on other
threads. This generally only happens when the target thread is
suspended due to a running GC and the debugger thread tries to access
stack registers from a compiled method. This could lead to a missed
read-barrier and a From-Space reference being returned. To fix this we
manually perform a read-barrier after pulling the reference out of the
compiled stack.

Test: ./test.py --host
Test: ./art/tools/run-libjdwp-tests.sh --mode=host
Test: ./test.py --host --all-compiler --all-gc -t 1966
Bug: 141590021

Change-Id: I5540c03bfdaecddbc1329ff6c7c58c6f18d6a090
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 6d1e384..2a07051 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -15,6 +15,7 @@
  */
 
 #include "stack.h"
+#include <limits>
 
 #include "android-base/stringprintf.h"
 
@@ -224,7 +225,15 @@
       DCHECK_EQ(*val, val2);
       return ok;
     }
-    return GetVRegFromOptimizedCode(m, vreg, kind, val);
+    bool res = GetVRegFromOptimizedCode(m, vreg, kind, val);
+    if (kind == kReferenceVReg) {
+      // Perform a read barrier in case we are in a different thread and GC is ongoing.
+      mirror::Object* out = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(*val));
+      uintptr_t ptr_out = reinterpret_cast<uintptr_t>(GcRoot<mirror::Object>(out).Read());
+      DCHECK_LT(ptr_out, std::numeric_limits<uint32_t>::max());
+      *val = static_cast<uint32_t>(ptr_out);
+    }
+    return res;
   } else {
     DCHECK(cur_shadow_frame_ != nullptr);
     if (kind == kReferenceVReg) {