Support for synchronized native methods.

This change adds support for synchronized native methods by using
calls to MonitorEnter and MonitorExit on the JNIEnv*. There is
some tidying of the assembler and a straw man JNIEnv implementation.
The JNIEnv implementation just warns when MonitorEnter/Exit are called
and doesn't adhere to the correct JNIEnv layout.

Change-Id: I90ed6ec8f85f5b01b929f16e0dbdecadd0b01359
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 67f8bc8..5385a8b 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -4,6 +4,7 @@
 #include <sys/mman.h>
 #include "src/assembler.h"
 #include "src/calling_convention.h"
+#include "src/jni_internal.h"
 #include "src/macros.h"
 #include "src/managed_register.h"
 #include "src/logging.h"
@@ -85,26 +86,54 @@
     jni_conv.Next();
   }
 
-  // 5. Acquire lock for synchronized methods. Done here as references are held
-  //    live in handle block but we're in managed code and can work on
-  //    references
-  if (native_method->IsSynchronized()) {
-    jni_conv.ResetIterator(FrameOffset(0));
-    jni_conv.Next();  // skip JNI environment
-    jni_asm->LockReferenceOnStack(jni_conv.CurrentParamHandleOffset());
-  }
-
-  // 6. Transition from being in managed to native code
+  // 5. Transition from being in managed to native code
   // TODO: write out anchor, ensure the transition to native follow a store
   //       fence.
   jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kNative,
                                   mr_conv.InterproceduralScratchRegister());
 
-  // 7. Move frame down to allow space for out going args. Do for as short a
+  // 6. Move frame down to allow space for out going args. Do for as short a
   //    time as possible to aid profiling..
   const size_t out_arg_size = jni_conv.OutArgSize();
   jni_asm->IncreaseFrameSize(out_arg_size);
 
+  // 7. Acquire lock for synchronized methods.
+  if (native_method->IsSynchronized()) {
+    mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
+    jni_conv.ResetIterator(FrameOffset(out_arg_size));
+    jni_conv.Next();  // Skip JNIEnv*
+    // Get stack handle for 1st argument
+    if (is_static) {
+      FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
+      if (jni_conv.IsCurrentParamOnStack()) {
+        FrameOffset out_off = jni_conv.CurrentParamStackOffset();
+        jni_asm->CreateStackHandle(out_off, handle_offset,
+                                   mr_conv.InterproceduralScratchRegister(),
+                                   false);
+      } else {
+        ManagedRegister out_reg = jni_conv.CurrentParamRegister();
+        jni_asm->CreateStackHandle(out_reg, handle_offset,
+                                   ManagedRegister::NoRegister(), false);
+      }
+    } else {
+      CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
+    }
+    // Generate JNIEnv* in place and leave a copy in jni_env_register
+    ManagedRegister jni_env_register =
+        jni_conv.InterproceduralScratchRegister();
+    if (jni_conv.IsCurrentParamInRegister()) {
+      jni_env_register = jni_conv.CurrentParamRegister();
+    }
+    jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
+    if (!jni_conv.IsCurrentParamInRegister()) {
+      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
+      jni_asm->StoreRawPtr(out_off, jni_env_register);
+    }
+    // Call JNIEnv*->MonitorEnter(JNIEnv*, object)
+    jni_asm->Call(jni_env_register, JniEnvironment::MonitorEnterOffset(),
+                  jni_conv.InterproceduralScratchRegister());
+  }
+
   // 8. Iterate over arguments placing values from managed calling convention in
   //    to the convention required for a native call (shuffling). For references
   //    place an index/pointer to the reference after checking whether it is
@@ -130,79 +159,7 @@
   }
   while (mr_conv.HasNext()) {
     CHECK(jni_conv.HasNext());
-    bool input_in_reg = mr_conv.IsCurrentParamInRegister();
-    bool output_in_reg = jni_conv.IsCurrentParamInRegister();
-    FrameOffset handle_offset(0);
-    bool null_allowed = false;
-    bool ref_param = jni_conv.IsCurrentParamAReference();
-    CHECK(!ref_param || mr_conv.IsCurrentParamAReference());
-    CHECK(input_in_reg || mr_conv.IsCurrentParamOnStack());
-    CHECK(output_in_reg || jni_conv.IsCurrentParamOnStack());
-    // References need handlerization and the handle address passing
-    if (ref_param) {
-      null_allowed = mr_conv.IsCurrentParamPossiblyNull();
-      // Compute handle offset. Note null is placed in the SHB but the jobject
-      // passed to the native code must be null (not a pointer into the SHB
-      // as with regular references).
-      handle_offset = jni_conv.CurrentParamHandleOffset();
-      // Check handle offset is within frame.
-      CHECK_LT(handle_offset.Uint32Value(), (frame_size+out_arg_size));
-    }
-    if (input_in_reg && output_in_reg) {
-      LOG(FATAL) << "UNTESTED";
-      ManagedRegister in_reg = mr_conv.CurrentParamRegister();
-      ManagedRegister out_reg = jni_conv.CurrentParamRegister();
-      if (ref_param) {
-        jni_asm->CreateStackHandle(out_reg, handle_offset, in_reg,
-                                   null_allowed);
-      } else {
-        jni_asm->Move(out_reg, in_reg);
-      }
-    } else if (!input_in_reg && !output_in_reg) {
-      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
-      if (ref_param) {
-        jni_asm->CreateStackHandle(out_off, handle_offset,
-                                   mr_conv.InterproceduralScratchRegister(),
-                                   null_allowed);
-      } else {
-        FrameOffset in_off = mr_conv.CurrentParamStackOffset();
-        size_t param_size = mr_conv.CurrentParamSizeInBytes();
-        CHECK_EQ(param_size, jni_conv.CurrentParamSizeInBytes());
-        jni_asm->Copy(out_off, in_off, mr_conv.InterproceduralScratchRegister(),
-                      param_size);
-      }
-    } else if (!input_in_reg && output_in_reg) {
-      LOG(FATAL) << "UNTESTED";
-      FrameOffset in_off = mr_conv.CurrentParamStackOffset();
-      ManagedRegister out_reg = jni_conv.CurrentParamRegister();
-      // Check that incoming stack arguments are above the current stack frame.
-      CHECK_GT(in_off.Uint32Value(), frame_size);
-      if (ref_param) {
-        jni_asm->CreateStackHandle(out_reg, handle_offset,
-                                   ManagedRegister::NoRegister(), null_allowed);
-      } else {
-        unsigned int param_size = mr_conv.CurrentParamSizeInBytes();
-        CHECK_EQ(param_size, jni_conv.CurrentParamSizeInBytes());
-        jni_asm->Load(out_reg, in_off, param_size);
-      }
-    } else {
-      LOG(FATAL) << "UNTESTED";
-      CHECK(input_in_reg && !output_in_reg);
-      ManagedRegister in_reg = mr_conv.CurrentParamRegister();
-      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
-      // Check outgoing argument is within frame
-      CHECK_LT(out_off.Uint32Value(), frame_size);
-      if (ref_param) {
-        // TODO: recycle value in in_reg rather than reload from handle
-        jni_asm->CreateStackHandle(out_off, handle_offset,
-                                   mr_conv.InterproceduralScratchRegister(),
-                                   null_allowed);
-      } else {
-        size_t param_size = mr_conv.CurrentParamSizeInBytes();
-        CHECK_EQ(param_size, jni_conv.CurrentParamSizeInBytes());
-        jni_asm->Store(out_off, in_reg, param_size);
-      }
-    }
+    CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
     mr_conv.Next();
     jni_conv.Next();
   }
@@ -221,8 +178,55 @@
   jni_asm->Call(mr_conv.MethodRegister(), Method::NativeMethodOffset(),
                 mr_conv.InterproceduralScratchRegister());
 
+  // 11. Release lock for synchronized methods.
+  if (native_method->IsSynchronized()) {
+    mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
+    jni_conv.ResetIterator(FrameOffset(out_arg_size));
+    jni_conv.Next();  // Skip JNIEnv*
+    // Save return value
+    FrameOffset return_save_location = jni_conv.ReturnValueSaveLocation();
+    CHECK_LT(return_save_location.Uint32Value(), frame_size+out_arg_size);
+    jni_asm->Store(return_save_location, jni_conv.ReturnRegister(),
+                   jni_conv.SizeOfReturnValue());
+    // Get stack handle for 1st argument
+    if (is_static) {
+      FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
+      if (jni_conv.IsCurrentParamOnStack()) {
+        FrameOffset out_off = jni_conv.CurrentParamStackOffset();
+        jni_asm->CreateStackHandle(out_off, handle_offset,
+                                   mr_conv.InterproceduralScratchRegister(),
+                                   false);
+      } else {
+        ManagedRegister out_reg = jni_conv.CurrentParamRegister();
+        jni_asm->CreateStackHandle(out_reg, handle_offset,
+                                   ManagedRegister::NoRegister(), false);
+      }
+    } else {
+      CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
+    }
+    // Generate JNIEnv* in place and leave a copy in jni_env_register
+    ManagedRegister jni_env_register =
+        jni_conv.InterproceduralScratchRegister();
+    if (jni_conv.IsCurrentParamInRegister()) {
+      jni_env_register = jni_conv.CurrentParamRegister();
+    }
+    jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
+    if (!jni_conv.IsCurrentParamInRegister()) {
+      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
+      jni_asm->StoreRawPtr(out_off, jni_env_register);
+    }
+    // Call JNIEnv*->MonitorExit(JNIEnv*, object)
+    jni_asm->Call(jni_env_register, JniEnvironment::MonitorExitOffset(),
+                  jni_conv.InterproceduralScratchRegister());
+    // Reload return value
+    jni_asm->Load(jni_conv.ReturnRegister(), return_save_location,
+                  jni_conv.SizeOfReturnValue());
+  }
+
   // 11. Release outgoing argument area
   jni_asm->DecreaseFrameSize(out_arg_size);
+  mr_conv.ResetIterator(FrameOffset(frame_size));
+  jni_conv.ResetIterator(FrameOffset(0));
 
   // 12. Transition from being in native to managed code, possibly entering a
   //     safepoint
@@ -230,21 +234,10 @@
                                   mr_conv.InterproceduralScratchRegister());
   // TODO: check for safepoint transition
 
-  // 13. Move to first handle offset
-  jni_conv.ResetIterator(FrameOffset(0));
-  jni_conv.Next();  // skip JNI environment
-
-  // 14. Release lock for synchronized methods (done in the managed state so
-  //     references can be touched)
-  if (native_method->IsSynchronized()) {
-    jni_asm->UnLockReferenceOnStack(jni_conv.CurrentParamHandleOffset());
-  }
-
   // 15. Place result in correct register possibly dehandlerizing
   if (jni_conv.IsReturnAReference()) {
     jni_asm->LoadReferenceFromStackHandle(mr_conv.ReturnRegister(),
-                                          jni_conv.ReturnRegister(),
-                                          jni_conv.CurrentParamHandleOffset());
+                                          jni_conv.ReturnRegister());
   } else {
     jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
   }
@@ -263,6 +256,86 @@
   native_method->SetCode(code.pointer());
 }
 
+// Copy a single parameter from the managed to the JNI calling convention
+void JniCompiler::CopyParameter(Assembler* jni_asm,
+                                ManagedRuntimeCallingConvention* mr_conv,
+                                JniCallingConvention* jni_conv,
+                                size_t frame_size, size_t out_arg_size) {
+  bool input_in_reg = mr_conv->IsCurrentParamInRegister();
+  bool output_in_reg = jni_conv->IsCurrentParamInRegister();
+  FrameOffset handle_offset(0);
+  bool null_allowed = false;
+  bool ref_param = jni_conv->IsCurrentParamAReference();
+  CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
+  CHECK(input_in_reg || mr_conv->IsCurrentParamOnStack());
+  CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
+  // References need handlerization and the handle address passing
+  if (ref_param) {
+    null_allowed = mr_conv->IsCurrentParamPossiblyNull();
+    // Compute handle offset. Note null is placed in the SHB but the jobject
+    // passed to the native code must be null (not a pointer into the SHB
+    // as with regular references).
+    handle_offset = jni_conv->CurrentParamHandleOffset();
+    // Check handle offset is within frame.
+    CHECK_LT(handle_offset.Uint32Value(), (frame_size+out_arg_size));
+  }
+  if (input_in_reg && output_in_reg) {
+    LOG(FATAL) << "UNTESTED";
+    ManagedRegister in_reg = mr_conv->CurrentParamRegister();
+    ManagedRegister out_reg = jni_conv->CurrentParamRegister();
+    if (ref_param) {
+      jni_asm->CreateStackHandle(out_reg, handle_offset, in_reg,
+                                 null_allowed);
+    } else {
+      jni_asm->Move(out_reg, in_reg);
+    }
+  } else if (!input_in_reg && !output_in_reg) {
+    FrameOffset out_off = jni_conv->CurrentParamStackOffset();
+    if (ref_param) {
+      jni_asm->CreateStackHandle(out_off, handle_offset,
+                                 mr_conv->InterproceduralScratchRegister(),
+                                 null_allowed);
+    } else {
+      FrameOffset in_off = mr_conv->CurrentParamStackOffset();
+      size_t param_size = mr_conv->CurrentParamSize();
+      CHECK_EQ(param_size, jni_conv->CurrentParamSize());
+      jni_asm->Copy(out_off, in_off, mr_conv->InterproceduralScratchRegister(),
+                    param_size);
+    }
+  } else if (!input_in_reg && output_in_reg) {
+    LOG(FATAL) << "UNTESTED";
+    FrameOffset in_off = mr_conv->CurrentParamStackOffset();
+    ManagedRegister out_reg = jni_conv->CurrentParamRegister();
+    // Check that incoming stack arguments are above the current stack frame.
+    CHECK_GT(in_off.Uint32Value(), frame_size);
+    if (ref_param) {
+      jni_asm->CreateStackHandle(out_reg, handle_offset,
+                                 ManagedRegister::NoRegister(), null_allowed);
+    } else {
+      unsigned int param_size = mr_conv->CurrentParamSize();
+      CHECK_EQ(param_size, jni_conv->CurrentParamSize());
+      jni_asm->Load(out_reg, in_off, param_size);
+    }
+  } else {
+    LOG(FATAL) << "UNTESTED";
+    CHECK(input_in_reg && !output_in_reg);
+    ManagedRegister in_reg = mr_conv->CurrentParamRegister();
+    FrameOffset out_off = jni_conv->CurrentParamStackOffset();
+    // Check outgoing argument is within frame
+    CHECK_LT(out_off.Uint32Value(), frame_size);
+    if (ref_param) {
+      // TODO: recycle value in in_reg rather than reload from handle
+      jni_asm->CreateStackHandle(out_off, handle_offset,
+                                 mr_conv->InterproceduralScratchRegister(),
+                                 null_allowed);
+    } else {
+      size_t param_size = mr_conv->CurrentParamSize();
+      CHECK_EQ(param_size, jni_conv->CurrentParamSize());
+      jni_asm->Store(out_off, in_reg, param_size);
+    }
+  }
+}
+
 void* JniCompiler::AllocateCode(size_t size) {
   CHECK_LT(((jni_code_top_ - jni_code_) + size), jni_code_size_);
   void *result = jni_code_top_;