Do not create HandleScope for JNI transitions.

We previously crated a HandleScope in the JNI transition
frame to hold references passed as jobject (jclass, etc.)
to the native function and these references were actually
spilled twice during the transition.

We now construct the jobject as a pointer to the reference
spilled in the reserved out vreg area in the caller's frame.
And the jclass for static methods is just a pointer to the
method's declaring class. This reduces the amount of work
required in the JNI transition, both on entry (in compiled
stubs) and exit (in JniMethodEnd*).

Some additional work is required when GC visits references
of a native method as we need to walk over the method's
shorty which was unnecessary for a HandleScope.

Also fix Thread::InitStackHwm() to calculate correct stack
size needed by the new Thread::IsJniTransitionReference().

The results for StringToBytesBenchmark on blueline little
cores running at fixed frequency 1420800 are approximately
arm64 (medians from 3 runs) before after
timeGetBytesAscii EMPTY     447.33 436.86
timeGetBytesIso88591 EMPTY  440.52 431.13
timeGetBytesUtf8 EMPTY      432.31 409.82
arm (medians from 3 runs)   before after
timeGetBytesAscii EMPTY     500.53 490.87
timeGetBytesIso88591 EMPTY  496.45 495.30
timeGetBytesUtf8 EMPTY      488.84 472.68

Test: m test-art-host-gtest
Test: testrunner.py --host
Test: testrunner.py --host --gcstress
Test: testrunner.py --host --jit-on-first-use
Test: testrunner.py --host --jit-on-first-use --gcstress
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Test: boots.
Bug: 172332525
Change-Id: I658f9d87071587b3e89f31c65feca976a11e9cc2
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 38167fb..c7241c1 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -845,85 +845,79 @@
   UNIMPLEMENTED(FATAL);
 }
 
-void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
-                                                      FrameOffset handle_scope_offset,
-                                                      ManagedRegister min_reg,
-                                                      bool null_allowed) {
+void ArmVIXLJNIMacroAssembler::CreateJObject(ManagedRegister mout_reg,
+                                             FrameOffset spilled_reference_offset,
+                                             ManagedRegister min_reg,
+                                             bool null_allowed) {
   vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm());
   vixl::aarch32::Register in_reg =
       min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm());
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   temps.Exclude(out_reg);
   if (null_allowed) {
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+    // Null values get a jobject value null. Otherwise, the jobject is
+    // the address of the spilled reference.
+    // e.g. out_reg = (handle == 0) ? 0 : (SP+spilled_reference_offset)
     if (!in_reg.IsValid()) {
-      asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value());
+      asm_.LoadFromOffset(kLoadWord, out_reg, sp, spilled_reference_offset.Int32Value());
       in_reg = out_reg;
     }
 
     temps.Exclude(in_reg);
     ___ Cmp(in_reg, 0);
 
-    if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
+    if (asm_.ShifterOperandCanHold(ADD, spilled_reference_offset.Int32Value())) {
       if (!out_reg.Is(in_reg)) {
         ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                  3 * vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
         ___ it(eq, 0xc);
         ___ mov(eq, out_reg, 0);
-        asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
+        asm_.AddConstantInIt(out_reg, sp, spilled_reference_offset.Int32Value(), ne);
       } else {
         ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                  2 * vixl32::kMaxInstructionSizeInBytes,
                                  CodeBufferCheckScope::kMaximumSize);
         ___ it(ne, 0x8);
-        asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
+        asm_.AddConstantInIt(out_reg, sp, spilled_reference_offset.Int32Value(), ne);
       }
     } else {
       // TODO: Implement this (old arm assembler would have crashed here).
       UNIMPLEMENTED(FATAL);
     }
   } else {
-    asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value());
+    asm_.AddConstant(out_reg, sp, spilled_reference_offset.Int32Value());
   }
 }
 
-void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
-                                                      FrameOffset handle_scope_offset,
-                                                      bool null_allowed) {
+void ArmVIXLJNIMacroAssembler::CreateJObject(FrameOffset out_off,
+                                             FrameOffset spilled_reference_offset,
+                                             bool null_allowed) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   vixl32::Register scratch = temps.Acquire();
   if (null_allowed) {
-    asm_.LoadFromOffset(kLoadWord, scratch, sp, handle_scope_offset.Int32Value());
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
+    asm_.LoadFromOffset(kLoadWord, scratch, sp, spilled_reference_offset.Int32Value());
+    // Null values get a jobject value null. Otherwise, the jobject is
+    // the address of the spilled reference.
+    // e.g. scratch = (scratch == 0) ? 0 : (SP+spilled_reference_offset)
     ___ Cmp(scratch, 0);
 
-    if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
+    if (asm_.ShifterOperandCanHold(ADD, spilled_reference_offset.Int32Value())) {
       ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
                                2 * vixl32::kMaxInstructionSizeInBytes,
                                CodeBufferCheckScope::kMaximumSize);
       ___ it(ne, 0x8);
-      asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne);
+      asm_.AddConstantInIt(scratch, sp, spilled_reference_offset.Int32Value(), ne);
     } else {
       // TODO: Implement this (old arm assembler would have crashed here).
       UNIMPLEMENTED(FATAL);
     }
   } else {
-    asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value());
+    asm_.AddConstant(scratch, sp, spilled_reference_offset.Int32Value());
   }
   asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value());
 }
 
-void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope(
-    ManagedRegister mout_reg ATTRIBUTE_UNUSED,
-    ManagedRegister min_reg ATTRIBUTE_UNUSED) {
-  UNIMPLEMENTED(FATAL);
-}
-
 void ArmVIXLJNIMacroAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
                                             bool could_be_null ATTRIBUTE_UNUSED) {
   // TODO: not validating references.
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
index 2f6813a..248fc67 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
@@ -153,24 +153,20 @@
   void GetCurrentThread(ManagedRegister dest) override;
   void GetCurrentThread(FrameOffset dest_offset) override;
 
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  void CreateHandleScopeEntry(ManagedRegister out_reg,
-                              FrameOffset handlescope_offset,
-                              ManagedRegister in_reg,
-                              bool null_allowed) override;
+  // Set up `out_reg` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`. `in_reg` holds a possibly
+  // stale reference that can be used to avoid loading the spilled value to
+  // see if the value is null.
+  void CreateJObject(ManagedRegister out_reg,
+                     FrameOffset spilled_reference_offset,
+                     ManagedRegister in_reg,
+                     bool null_allowed) override;
 
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  void CreateHandleScopeEntry(FrameOffset out_off,
-                              FrameOffset handlescope_offset,
-                              bool null_allowed) override;
-
-  // src holds a handle scope entry (Object**) load this into dst.
-  void LoadReferenceFromHandleScope(ManagedRegister dst,
-                                    ManagedRegister src) override;
+  // Set up `out_off` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`.
+  void CreateJObject(FrameOffset out_off,
+                     FrameOffset spilled_reference_offset,
+                     bool null_allowed) override;
 
   // Heap::VerifyObject on src. In some cases (such as a reference to this) we
   // know that src may not be null.
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index bb93a96..ff83828 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -650,70 +650,54 @@
   UNIMPLEMENTED(FATAL) << "Unimplemented Call() variant";
 }
 
-void Arm64JNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister m_out_reg,
-                                                    FrameOffset handle_scope_offs,
-                                                    ManagedRegister m_in_reg,
-                                                    bool null_allowed) {
+void Arm64JNIMacroAssembler::CreateJObject(ManagedRegister m_out_reg,
+                                           FrameOffset spilled_reference_offset,
+                                           ManagedRegister m_in_reg,
+                                           bool null_allowed) {
   Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
   Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
   // For now we only hold stale handle scope entries in x registers.
   CHECK(in_reg.IsNoRegister() || in_reg.IsXRegister()) << in_reg;
   CHECK(out_reg.IsXRegister()) << out_reg;
   if (null_allowed) {
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+    // Null values get a jobject value null. Otherwise, the jobject is
+    // the address of the spilled reference.
+    // e.g. out_reg = (in == 0) ? 0 : (SP+spilled_reference_offset)
     if (in_reg.IsNoRegister()) {
       LoadWFromOffset(kLoadWord, out_reg.AsOverlappingWRegister(), SP,
-                      handle_scope_offs.Int32Value());
+                      spilled_reference_offset.Int32Value());
       in_reg = out_reg;
     }
     ___ Cmp(reg_w(in_reg.AsOverlappingWRegister()), 0);
     if (!out_reg.Equals(in_reg)) {
       LoadImmediate(out_reg.AsXRegister(), 0, eq);
     }
-    AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), ne);
+    AddConstant(out_reg.AsXRegister(), SP, spilled_reference_offset.Int32Value(), ne);
   } else {
-    AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), al);
+    AddConstant(out_reg.AsXRegister(), SP, spilled_reference_offset.Int32Value(), al);
   }
 }
 
-void Arm64JNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
-                                                    FrameOffset handle_scope_offset,
-                                                    bool null_allowed) {
+void Arm64JNIMacroAssembler::CreateJObject(FrameOffset out_off,
+                                           FrameOffset spilled_reference_offset,
+                                           bool null_allowed) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   Register scratch = temps.AcquireX();
   if (null_allowed) {
     Register scratch2 = temps.AcquireW();
-    ___ Ldr(scratch2, MEM_OP(reg_x(SP), handle_scope_offset.Int32Value()));
-    ___ Add(scratch, reg_x(SP), handle_scope_offset.Int32Value());
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
+    ___ Ldr(scratch2, MEM_OP(reg_x(SP), spilled_reference_offset.Int32Value()));
+    ___ Add(scratch, reg_x(SP), spilled_reference_offset.Int32Value());
+    // Null values get a jobject value null. Otherwise, the jobject is
+    // the address of the spilled reference.
+    // e.g. scratch = (scratch == 0) ? 0 : (SP+spilled_reference_offset)
     ___ Cmp(scratch2, 0);
     ___ Csel(scratch, scratch, xzr, ne);
   } else {
-    ___ Add(scratch, reg_x(SP), handle_scope_offset.Int32Value());
+    ___ Add(scratch, reg_x(SP), spilled_reference_offset.Int32Value());
   }
   ___ Str(scratch, MEM_OP(reg_x(SP), out_off.Int32Value()));
 }
 
-void Arm64JNIMacroAssembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg,
-                                                          ManagedRegister m_in_reg) {
-  Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
-  Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
-  CHECK(out_reg.IsXRegister()) << out_reg;
-  CHECK(in_reg.IsXRegister()) << in_reg;
-  vixl::aarch64::Label exit;
-  if (!out_reg.Equals(in_reg)) {
-    // FIXME: Who sets the flags here?
-    LoadImmediate(out_reg.AsXRegister(), 0, eq);
-  }
-  ___ Cbz(reg_x(in_reg.AsXRegister()), &exit);
-  LoadFromOffset(out_reg.AsXRegister(), in_reg.AsXRegister(), 0);
-  ___ Bind(&exit);
-}
-
 void Arm64JNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
   CHECK_ALIGNED(stack_adjust, kStackAlignment);
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 9f3eea2..ad027d3 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -135,23 +135,20 @@
   void GetCurrentThread(ManagedRegister dest) override;
   void GetCurrentThread(FrameOffset dest_offset) override;
 
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  void CreateHandleScopeEntry(ManagedRegister out_reg,
-                              FrameOffset handlescope_offset,
-                              ManagedRegister in_reg,
-                              bool null_allowed) override;
+  // Set up `out_reg` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`. `in_reg` holds a possibly
+  // stale reference that can be used to avoid loading the spilled value to
+  // see if the value is null.
+  void CreateJObject(ManagedRegister out_reg,
+                     FrameOffset spilled_reference_offset,
+                     ManagedRegister in_reg,
+                     bool null_allowed) override;
 
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  void CreateHandleScopeEntry(FrameOffset out_off,
-                              FrameOffset handlescope_offset,
-                              bool null_allowed) override;
-
-  // src holds a handle scope entry (Object**) load this into dst.
-  void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) override;
+  // Set up `out_off` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`.
+  void CreateJObject(FrameOffset out_off,
+                     FrameOffset spilled_reference_offset,
+                     bool null_allowed) override;
 
   // Heap::VerifyObject on src. In some cases (such as a reference to this) we
   // know that src may not be null.
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 9231f9c..5265152 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -169,13 +169,13 @@
   __ Move(hidden_arg_register, method_register, 4);
   __ VerifyObject(scratch_register, false);
 
-  __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, true);
-  __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, false);
-  __ CreateHandleScopeEntry(method_register, FrameOffset(48), scratch_register, true);
-  __ CreateHandleScopeEntry(FrameOffset(48), FrameOffset(64), true);
-  __ CreateHandleScopeEntry(method_register, FrameOffset(0), scratch_register, true);
-  __ CreateHandleScopeEntry(method_register, FrameOffset(1025), scratch_register, true);
-  __ CreateHandleScopeEntry(scratch_register, FrameOffset(1025), scratch_register, true);
+  __ CreateJObject(scratch_register, FrameOffset(48), scratch_register, true);
+  __ CreateJObject(scratch_register, FrameOffset(48), scratch_register, false);
+  __ CreateJObject(method_register, FrameOffset(48), scratch_register, true);
+  __ CreateJObject(FrameOffset(48), FrameOffset(64), true);
+  __ CreateJObject(method_register, FrameOffset(0), scratch_register, true);
+  __ CreateJObject(method_register, FrameOffset(1025), scratch_register, true);
+  __ CreateJObject(scratch_register, FrameOffset(1025), scratch_register, true);
 
   __ ExceptionPoll(0);
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 1470ef1..1678f87 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -1,12 +1,12 @@
 const char* const VixlJniHelpersResults = {
   "       0: 2d e9 e0 4d   push.w {r5, r6, r7, r8, r10, r11, lr}\n"
   "       4: 2d ed 10 8a   vpush {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
-  "       8: 89 b0         sub sp, #36\n"
+  "       8: 85 b0         sub sp, #20\n"
   "       a: 00 90         str r0, [sp]\n"
-  "       c: 21 91         str r1, [sp, #132]\n"
-  "       e: 8d ed 22 0a   vstr s0, [sp, #136]\n"
-  "      12: 23 92         str r2, [sp, #140]\n"
-  "      14: 24 93         str r3, [sp, #144]\n"
+  "       c: 1d 91         str r1, [sp, #116]\n"
+  "       e: 8d ed 1e 0a   vstr s0, [sp, #120]\n"
+  "      12: 1f 92         str r2, [sp, #124]\n"
+  "      14: 20 93         str r3, [sp, #128]\n"
   "      16: 88 b0         sub sp, #32\n"
   "      18: ad f5 80 5d   sub.w sp, sp, #4096\n"
   "      1c: 08 98         ldr r0, [sp, #32]\n"
@@ -147,7 +147,7 @@
   "     208: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     20c: 0d f5 80 5d   add.w sp, sp, #4096\n"
   "     210: 08 b0         add sp, #32\n"
-  "     212: 09 b0         add sp, #36\n"
+  "     212: 05 b0         add sp, #20\n"
   "     214: bd ec 10 8a   vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
   "     218: bd e8 e0 4d   pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
   "     21c: d9 f8 30 80   ldr.w r8, [r9, #48]\n"
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index 3490959..d621122 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -208,23 +208,20 @@
   virtual void GetCurrentThread(ManagedRegister dest) = 0;
   virtual void GetCurrentThread(FrameOffset dest_offset) = 0;
 
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  virtual void CreateHandleScopeEntry(ManagedRegister out_reg,
-                                      FrameOffset handlescope_offset,
-                                      ManagedRegister in_reg,
-                                      bool null_allowed) = 0;
+  // Set up `out_reg` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`. `in_reg` holds a possibly
+  // stale reference that can be used to avoid loading the spilled value to
+  // see if the value is null.
+  virtual void CreateJObject(ManagedRegister out_reg,
+                             FrameOffset spilled_reference_offset,
+                             ManagedRegister in_reg,
+                             bool null_allowed) = 0;
 
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  virtual void CreateHandleScopeEntry(FrameOffset out_off,
-                                      FrameOffset handlescope_offset,
-                                      bool null_allowed) = 0;
-
-  // src holds a handle scope entry (Object**) load this into dst
-  virtual void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) = 0;
+  // Set up `out_off` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`.
+  virtual void CreateJObject(FrameOffset out_off,
+                             FrameOffset spilled_reference_offset,
+                             bool null_allowed) = 0;
 
   // Heap::VerifyObject on src. In some cases (such as a reference to this) we
   // know that src may not be null.
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index 2c7902b..2710eb1 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -466,10 +466,10 @@
   __ mfence();
 }
 
-void X86JNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
-                                                  FrameOffset handle_scope_offset,
-                                                  ManagedRegister min_reg,
-                                                  bool null_allowed) {
+void X86JNIMacroAssembler::CreateJObject(ManagedRegister mout_reg,
+                                         FrameOffset spilled_reference_offset,
+                                         ManagedRegister min_reg,
+                                         bool null_allowed) {
   X86ManagedRegister out_reg = mout_reg.AsX86();
   X86ManagedRegister in_reg = min_reg.AsX86();
   CHECK(in_reg.IsCpuRegister());
@@ -482,47 +482,30 @@
     }
     __ testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister());
     __ j(kZero, &null_arg);
-    __ leal(out_reg.AsCpuRegister(), Address(ESP, handle_scope_offset));
+    __ leal(out_reg.AsCpuRegister(), Address(ESP, spilled_reference_offset));
     __ Bind(&null_arg);
   } else {
-    __ leal(out_reg.AsCpuRegister(), Address(ESP, handle_scope_offset));
+    __ leal(out_reg.AsCpuRegister(), Address(ESP, spilled_reference_offset));
   }
 }
 
-void X86JNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
-                                                  FrameOffset handle_scope_offset,
-                                                  bool null_allowed) {
+void X86JNIMacroAssembler::CreateJObject(FrameOffset out_off,
+                                         FrameOffset spilled_reference_offset,
+                                         bool null_allowed) {
   Register scratch = GetScratchRegister();
   if (null_allowed) {
     Label null_arg;
-    __ movl(scratch, Address(ESP, handle_scope_offset));
+    __ movl(scratch, Address(ESP, spilled_reference_offset));
     __ testl(scratch, scratch);
     __ j(kZero, &null_arg);
-    __ leal(scratch, Address(ESP, handle_scope_offset));
+    __ leal(scratch, Address(ESP, spilled_reference_offset));
     __ Bind(&null_arg);
   } else {
-    __ leal(scratch, Address(ESP, handle_scope_offset));
+    __ leal(scratch, Address(ESP, spilled_reference_offset));
   }
   __ movl(Address(ESP, out_off), scratch);
 }
 
-// Given a handle scope entry, load the associated reference.
-void X86JNIMacroAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
-                                                        ManagedRegister min_reg) {
-  X86ManagedRegister out_reg = mout_reg.AsX86();
-  X86ManagedRegister in_reg = min_reg.AsX86();
-  CHECK(out_reg.IsCpuRegister());
-  CHECK(in_reg.IsCpuRegister());
-  Label null_arg;
-  if (!out_reg.Equals(in_reg)) {
-    __ xorl(out_reg.AsCpuRegister(), out_reg.AsCpuRegister());
-  }
-  __ testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister());
-  __ j(kZero, &null_arg);
-  __ movl(out_reg.AsCpuRegister(), Address(in_reg.AsCpuRegister(), 0));
-  __ Bind(&null_arg);
-}
-
 void X86JNIMacroAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
   // TODO: not validating references
 }
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.h b/compiler/utils/x86/jni_macro_assembler_x86.h
index 0239ff7..448a7f4 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.h
+++ b/compiler/utils/x86/jni_macro_assembler_x86.h
@@ -126,23 +126,20 @@
   void GetCurrentThread(ManagedRegister dest) override;
   void GetCurrentThread(FrameOffset dest_offset) override;
 
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  void CreateHandleScopeEntry(ManagedRegister out_reg,
-                              FrameOffset handlescope_offset,
-                              ManagedRegister in_reg,
-                              bool null_allowed) override;
+  // Set up `out_reg` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`. `in_reg` holds a possibly
+  // stale reference that can be used to avoid loading the spilled value to
+  // see if the value is null.
+  void CreateJObject(ManagedRegister out_reg,
+                     FrameOffset spilled_reference_offset,
+                     ManagedRegister in_reg,
+                     bool null_allowed) override;
 
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  void CreateHandleScopeEntry(FrameOffset out_off,
-                              FrameOffset handlescope_offset,
-                              bool null_allowed) override;
-
-  // src holds a handle scope entry (Object**) load this into dst
-  void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) override;
+  // Set up `out_off` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`.
+  void CreateJObject(FrameOffset out_off,
+                     FrameOffset spilled_reference_offset,
+                     bool null_allowed) override;
 
   // Heap::VerifyObject on src. In some cases (such as a reference to this) we
   // know that src may not be null.
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index 2649084..b5e17d1 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -541,17 +541,17 @@
   __ mfence();
 }
 
-void X86_64JNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
-                                                     FrameOffset handle_scope_offset,
-                                                     ManagedRegister min_reg,
-                                                     bool null_allowed) {
+void X86_64JNIMacroAssembler::CreateJObject(ManagedRegister mout_reg,
+                                            FrameOffset spilled_reference_offset,
+                                            ManagedRegister min_reg,
+                                            bool null_allowed) {
   X86_64ManagedRegister out_reg = mout_reg.AsX86_64();
   X86_64ManagedRegister in_reg = min_reg.AsX86_64();
   if (in_reg.IsNoRegister()) {  // TODO(64): && null_allowed
     // Use out_reg as indicator of null.
     in_reg = out_reg;
     // TODO: movzwl
-    __ movl(in_reg.AsCpuRegister(), Address(CpuRegister(RSP), handle_scope_offset));
+    __ movl(in_reg.AsCpuRegister(), Address(CpuRegister(RSP), spilled_reference_offset));
   }
   CHECK(in_reg.IsCpuRegister());
   CHECK(out_reg.IsCpuRegister());
@@ -563,47 +563,30 @@
     }
     __ testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister());
     __ j(kZero, &null_arg);
-    __ leaq(out_reg.AsCpuRegister(), Address(CpuRegister(RSP), handle_scope_offset));
+    __ leaq(out_reg.AsCpuRegister(), Address(CpuRegister(RSP), spilled_reference_offset));
     __ Bind(&null_arg);
   } else {
-    __ leaq(out_reg.AsCpuRegister(), Address(CpuRegister(RSP), handle_scope_offset));
+    __ leaq(out_reg.AsCpuRegister(), Address(CpuRegister(RSP), spilled_reference_offset));
   }
 }
 
-void X86_64JNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
-                                                     FrameOffset handle_scope_offset,
-                                                     bool null_allowed) {
+void X86_64JNIMacroAssembler::CreateJObject(FrameOffset out_off,
+                                            FrameOffset spilled_reference_offset,
+                                            bool null_allowed) {
   CpuRegister scratch = GetScratchRegister();
   if (null_allowed) {
     Label null_arg;
-    __ movl(scratch, Address(CpuRegister(RSP), handle_scope_offset));
+    __ movl(scratch, Address(CpuRegister(RSP), spilled_reference_offset));
     __ testl(scratch, scratch);
     __ j(kZero, &null_arg);
-    __ leaq(scratch, Address(CpuRegister(RSP), handle_scope_offset));
+    __ leaq(scratch, Address(CpuRegister(RSP), spilled_reference_offset));
     __ Bind(&null_arg);
   } else {
-    __ leaq(scratch, Address(CpuRegister(RSP), handle_scope_offset));
+    __ leaq(scratch, Address(CpuRegister(RSP), spilled_reference_offset));
   }
   __ movq(Address(CpuRegister(RSP), out_off), scratch);
 }
 
-// Given a handle scope entry, load the associated reference.
-void X86_64JNIMacroAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
-                                                           ManagedRegister min_reg) {
-  X86_64ManagedRegister out_reg = mout_reg.AsX86_64();
-  X86_64ManagedRegister in_reg = min_reg.AsX86_64();
-  CHECK(out_reg.IsCpuRegister());
-  CHECK(in_reg.IsCpuRegister());
-  Label null_arg;
-  if (!out_reg.Equals(in_reg)) {
-    __ xorl(out_reg.AsCpuRegister(), out_reg.AsCpuRegister());
-  }
-  __ testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister());
-  __ j(kZero, &null_arg);
-  __ movq(out_reg.AsCpuRegister(), Address(in_reg.AsCpuRegister(), 0));
-  __ Bind(&null_arg);
-}
-
 void X86_64JNIMacroAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
   // TODO: not validating references
 }
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
index 6589544..a5f7bbb 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
@@ -146,23 +146,20 @@
   void GetCurrentThread(ManagedRegister dest) override;
   void GetCurrentThread(FrameOffset dest_offset) override;
 
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  void CreateHandleScopeEntry(ManagedRegister out_reg,
-                              FrameOffset handlescope_offset,
-                              ManagedRegister in_reg,
-                              bool null_allowed) override;
+  // Set up `out_reg` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`. `in_reg` holds a possibly
+  // stale reference that can be used to avoid loading the spilled value to
+  // see if the value is null.
+  void CreateJObject(ManagedRegister out_reg,
+                     FrameOffset spilled_reference_offset,
+                     ManagedRegister in_reg,
+                     bool null_allowed) override;
 
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  void CreateHandleScopeEntry(FrameOffset out_off,
-                              FrameOffset handlescope_offset,
-                              bool null_allowed) override;
-
-  // src holds a handle scope entry (Object**) load this into dst
-  void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) override;
+  // Set up `out_off` to hold a `jobject` (`StackReference<Object>*` to a spilled value),
+  // or to be null if the value is null and `null_allowed`.
+  void CreateJObject(FrameOffset out_off,
+                     FrameOffset spilled_reference_offset,
+                     bool null_allowed) override;
 
   // Heap::VerifyObject on src. In some cases (such as a reference to this) we
   // know that src may not be null.