x86 JNI compiler and unit tests.

Change-Id: I4c2e10328961a2e8e27c90777fe2a93737b21143
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
new file mode 100644
index 0000000..67f8bc8
--- /dev/null
+++ b/src/jni_compiler.cc
@@ -0,0 +1,290 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: irogers@google.com (Ian Rogers)
+#include "src/jni_compiler.h"
+#include <sys/mman.h>
+#include "src/assembler.h"
+#include "src/calling_convention.h"
+#include "src/macros.h"
+#include "src/managed_register.h"
+#include "src/logging.h"
+#include "src/thread.h"
+
+namespace art {
+
+// Generate the JNI bridge for the given method, general contract:
+// - Arguments are in the managed runtime format, either on stack or in
+//   registers, a reference to the method object is supplied as part of this
+//   convention.
+//
+void JniCompiler::Compile(Assembler* jni_asm, Method* native_method) {
+  CHECK(native_method->IsNative());
+  JniCallingConvention jni_conv(native_method);
+  ManagedRuntimeCallingConvention mr_conv(native_method);
+  const bool is_static = native_method->IsStatic();
+
+  // 1. Build the frame
+  const size_t frame_size(jni_conv.FrameSize());
+  jni_asm->BuildFrame(frame_size, mr_conv.MethodRegister());
+
+  // 2. Save callee save registers that aren't callee save in the native code
+  // TODO: implement computing the difference of the callee saves
+  // and saving
+
+  // 3. Set up the StackHandleBlock
+  mr_conv.ResetIterator(FrameOffset(frame_size));
+  jni_conv.ResetIterator(FrameOffset(0));
+  jni_asm->StoreImmediateToFrame(jni_conv.ShbNumRefsOffset(),
+                                 jni_conv.HandleCount(),
+                                 mr_conv.InterproceduralScratchRegister());
+  jni_asm->CopyRawPtrFromThread(jni_conv.ShbLinkOffset(),
+                                Thread::TopShbOffset(),
+                                mr_conv.InterproceduralScratchRegister());
+  jni_asm->StoreStackOffsetToThread(Thread::TopShbOffset(),
+                                    jni_conv.ShbOffset(),
+                                    mr_conv.InterproceduralScratchRegister());
+
+  // 4. Place incoming reference arguments into handle block
+  jni_conv.Next();  // Skip JNIEnv*
+  // 4.5. Create Class argument for static methods out of passed method
+  if (is_static) {
+    FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
+    // Check handle offset is within frame
+    CHECK_LT(handle_offset.Uint32Value(), frame_size);
+    jni_asm->LoadRef(jni_conv.InterproceduralScratchRegister(),
+                     mr_conv.MethodRegister(), Method::ClassOffset());
+    jni_asm->ValidateRef(jni_conv.InterproceduralScratchRegister(), false);
+    jni_asm->StoreRef(handle_offset, jni_conv.InterproceduralScratchRegister());
+    jni_conv.Next();  // handlerized so move to next argument
+  }
+  while (mr_conv.HasNext()) {
+    CHECK(jni_conv.HasNext());
+    bool ref_param = jni_conv.IsCurrentParamAReference();
+    CHECK(!ref_param || mr_conv.IsCurrentParamAReference());
+    // References need handlerization and the handle address passing
+    if (ref_param) {
+      // Compute handle offset, note null is handlerized but its boxed value
+      // must be NULL
+      FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
+      // Check handle offset is within frame
+      CHECK_LT(handle_offset.Uint32Value(), frame_size);
+      bool input_in_reg = mr_conv.IsCurrentParamInRegister();
+      CHECK(input_in_reg || mr_conv.IsCurrentParamOnStack());
+      if (input_in_reg) {
+        LOG(FATAL) << "UNTESTED";
+        ManagedRegister in_reg  =  mr_conv.CurrentParamRegister();
+        jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentParamPossiblyNull());
+        jni_asm->StoreRef(handle_offset, in_reg);
+      } else {
+        FrameOffset in_off  = mr_conv.CurrentParamStackOffset();
+        jni_asm->ValidateRef(in_off, mr_conv.IsCurrentParamPossiblyNull());
+        jni_asm->CopyRef(handle_offset, in_off,
+                         mr_conv.InterproceduralScratchRegister());
+      }
+    }
+    mr_conv.Next();
+    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
+  // 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
+  //    time as possible to aid profiling..
+  const size_t out_arg_size = jni_conv.OutArgSize();
+  jni_asm->IncreaseFrameSize(out_arg_size);
+
+  // 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
+  //    NULL (which must be encoded as NULL).
+  //    NB. we do this prior to materializing the JNIEnv* and static's jclass to
+  //    give as many free registers for the shuffle as possible
+  mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
+  jni_conv.ResetIterator(FrameOffset(out_arg_size));
+  jni_conv.Next();  // Skip JNIEnv*
+  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);
+    }
+    jni_conv.Next();
+  }
+  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);
+      }
+    }
+    mr_conv.Next();
+    jni_conv.Next();
+  }
+  // 9. Create 1st argument, the JNI environment ptr
+  jni_conv.ResetIterator(FrameOffset(out_arg_size));
+  if (jni_conv.IsCurrentParamInRegister()) {
+    jni_asm->LoadRawPtrFromThread(jni_conv.CurrentParamRegister(),
+                                  Thread::JniEnvOffset());
+  } else {
+    jni_asm->CopyRawPtrFromThread(jni_conv.CurrentParamStackOffset(),
+                                  Thread::JniEnvOffset(),
+                                  jni_conv.InterproceduralScratchRegister());
+  }
+
+  // 10. Plant call to native code associated with method
+  jni_asm->Call(mr_conv.MethodRegister(), Method::NativeMethodOffset(),
+                mr_conv.InterproceduralScratchRegister());
+
+  // 11. Release outgoing argument area
+  jni_asm->DecreaseFrameSize(out_arg_size);
+
+  // 12. Transition from being in native to managed code, possibly entering a
+  //     safepoint
+  jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kRunnable,
+                                  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());
+  } else {
+    jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
+  }
+
+  // 16. Remove stack handle block from thread
+  jni_asm->CopyRawPtrToThread(Thread::TopShbOffset(), jni_conv.ShbLinkOffset(),
+                              jni_conv.InterproceduralScratchRegister());
+
+  // 17. Remove activation
+  jni_asm->RemoveFrame(frame_size);
+
+  // 18. Finalize code generation
+  size_t cs = jni_asm->CodeSize();
+  MemoryRegion code(AllocateCode(cs), cs);
+  jni_asm->FinalizeInstructions(code);
+  native_method->SetCode(code.pointer());
+}
+
+void* JniCompiler::AllocateCode(size_t size) {
+  CHECK_LT(((jni_code_top_ - jni_code_) + size), jni_code_size_);
+  void *result = jni_code_top_;
+  jni_code_top_ += size;
+  return result;
+}
+
+JniCompiler::JniCompiler() {
+  // TODO: this shouldn't be managed by the JniCompiler, we should have a
+  // code cache.
+  jni_code_size_ = 4096;
+  jni_code_ = static_cast<byte*>(mmap(NULL, jni_code_size_,
+                                 PROT_READ | PROT_WRITE | PROT_EXEC,
+                                 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
+  CHECK_NE(MAP_FAILED, jni_code_);
+  jni_code_top_ = jni_code_;
+}
+
+JniCompiler::~JniCompiler() {
+  // TODO: this shouldn't be managed by the JniCompiler, we should have a
+  // code cache.
+  CHECK_EQ(0, munmap(jni_code_, jni_code_size_));
+}
+
+}  // namespace art