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_;