Initial changes towards Generic JNI option

Some initial changes that lead to an UNIMPLEMENTED. Works
by not compiling for JNI right now and tracking native methods
which have neither quick nor portable code. Uses new trampoline.

Change-Id: I5448654044eb2717752fd7359f4ef8bd5c17be6e
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index d034b79..3bdc95e 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -203,8 +203,11 @@
       method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
     } else {
       // No code? You must mean to go into the interpreter.
-      const void* method_code = kUsePortableCompiler ? GetPortableToInterpreterBridge()
-                                                     : GetQuickToInterpreterBridge();
+      // Or the generic JNI...
+      const void* method_code = method->IsNative() ? GetQuickGenericJniTrampoline()
+                                                   : (kUsePortableCompiler
+                                                        ? GetPortableToInterpreterBridge()
+                                                        : GetQuickToInterpreterBridge());
       OatFile::OatMethod oat_method = CreateOatMethod(method_code,
                                                       kStackAlignment,
                                                       0,
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index b9a26d6..ee88041 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -63,6 +63,7 @@
   bool verbose;
   const CompilerBackend* compiler_backend;
   InstructionSet instruction_set;
+  bool target64;
 
   InstructionSetFeatures GetInstructionSetFeatures() {
     return compiler_driver->GetInstructionSetFeatures();
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 2619258..b55b471 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -148,7 +148,9 @@
   cu.compiler_driver = &driver;
   cu.class_linker = class_linker;
   cu.instruction_set = driver.GetInstructionSet();
+  cu.target64 = cu.instruction_set == kX86_64;
   cu.compiler_backend = compiler_backend;
+  // TODO: x86_64 is not yet implemented.
   DCHECK((cu.instruction_set == kThumb2) ||
          (cu.instruction_set == kX86) ||
          (cu.instruction_set == kMips));
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index db7bdc8..eb6f9d1 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -374,21 +374,21 @@
   return AddWordData(constant_list_p, val_lo);
 }
 
-static void PushWord(std::vector<uint8_t>&buf, int data) {
+static void Push32(std::vector<uint8_t>&buf, int data) {
   buf.push_back(data & 0xff);
   buf.push_back((data >> 8) & 0xff);
   buf.push_back((data >> 16) & 0xff);
   buf.push_back((data >> 24) & 0xff);
 }
 
-// Push 8 bytes on 64-bit systems; 4 on 32-bit systems.
-static void PushPointer(std::vector<uint8_t>&buf, void const* pointer) {
-  uintptr_t data = reinterpret_cast<uintptr_t>(pointer);
-  if (sizeof(void*) == sizeof(uint64_t)) {
-    PushWord(buf, (data >> (sizeof(void*) * 4)) & 0xFFFFFFFF);
-    PushWord(buf, data & 0xFFFFFFFF);
+// Push 8 bytes on 64-bit target systems; 4 on 32-bit target systems.
+static void PushPointer(std::vector<uint8_t>&buf, const void* pointer, bool target64) {
+  uint64_t data = reinterpret_cast<uintptr_t>(pointer);
+  if (target64) {
+    Push32(buf, data & 0xFFFFFFFF);
+    Push32(buf, (data >> 32) & 0xFFFFFFFF);
   } else {
-    PushWord(buf, data);
+    Push32(buf, static_cast<uint32_t>(data));
   }
 }
 
@@ -403,7 +403,7 @@
   AlignBuffer(code_buffer_, data_offset_);
   LIR* data_lir = literal_list_;
   while (data_lir != NULL) {
-    PushWord(code_buffer_, data_lir->operands[0]);
+    Push32(code_buffer_, data_lir->operands[0]);
     data_lir = NEXT_LIR(data_lir);
   }
   // Push code and method literals, record offsets for the compiler to patch.
@@ -419,7 +419,7 @@
                                        code_buffer_.size());
     const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
     // unique value based on target to ensure code deduplication works
-    PushPointer(code_buffer_, &id);
+    PushPointer(code_buffer_, &id, cu_->target64);
     data_lir = NEXT_LIR(data_lir);
   }
   data_lir = method_literal_list_;
@@ -434,7 +434,7 @@
                                          code_buffer_.size());
     const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
     // unique value based on target to ensure code deduplication works
-    PushPointer(code_buffer_, &id);
+    PushPointer(code_buffer_, &id, cu_->target64);
     data_lir = NEXT_LIR(data_lir);
   }
   // Push class literals.
@@ -448,7 +448,7 @@
                                         code_buffer_.size());
     const DexFile::TypeId& id = cu_->dex_file->GetTypeId(target);
     // unique value based on target to ensure code deduplication works
-    PushPointer(code_buffer_, &id);
+    PushPointer(code_buffer_, &id, cu_->target64);
     data_lir = NEXT_LIR(data_lir);
   }
 }
@@ -492,8 +492,8 @@
                     << std::hex << keys[elems] << ", disp: 0x"
                     << std::hex << disp;
         }
-        PushWord(code_buffer_, keys[elems]);
-        PushWord(code_buffer_,
+        Push32(code_buffer_, keys[elems]);
+        Push32(code_buffer_,
           tab_rec->targets[elems]->offset - bx_offset);
       }
     } else {
@@ -505,7 +505,7 @@
           LOG(INFO) << "  Case[" << elems << "] disp: 0x"
                     << std::hex << disp;
         }
-        PushWord(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
+        Push32(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
       }
     }
   }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 501ea7c..fc22add 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -440,6 +440,11 @@
                           PORTABLE_ENTRYPOINT_OFFSET(pPortableToInterpreterBridge));
 }
 
+const std::vector<uint8_t>* CompilerDriver::CreateQuickGenericJniTrampoline() const {
+  return CreateTrampoline(instruction_set_, kQuickAbi,
+                          QUICK_ENTRYPOINT_OFFSET(pQuickGenericJniTrampoline));
+}
+
 const std::vector<uint8_t>* CompilerDriver::CreateQuickImtConflictTrampoline() const {
   return CreateTrampoline(instruction_set_, kQuickAbi,
                           QUICK_ENTRYPOINT_OFFSET(pQuickImtConflictTrampoline));
@@ -1920,8 +1925,12 @@
   uint64_t start_ns = NanoTime();
 
   if ((access_flags & kAccNative) != 0) {
+#if defined(__x86_64__)
+    // leaving this empty will trigger the generic JNI version
+#else
     compiled_method = compiler_backend_->JniCompile(*this, access_flags, method_idx, dex_file);
     CHECK(compiled_method != NULL);
+#endif
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
     MethodReference method_ref(&dex_file, method_idx);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 57c2908..80a6796 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -164,6 +164,8 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index aa16885..964cfe9 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -104,6 +104,8 @@
   portable_to_interpreter_bridge_offset_ =
       oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
 
+  quick_generic_jni_trampoline_offset_ =
+      oat_file_->GetOatHeader().GetQuickGenericJniTrampolineOffset();
   quick_imt_conflict_trampoline_offset_ =
       oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset();
   quick_resolution_trampoline_offset_ =
@@ -638,7 +640,12 @@
       if (quick_code != nullptr) {
         copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
       } else {
-        copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
+        if (orig->IsNative() && !orig->IsStatic()) {
+          // non-static native method missing compiled code, use generic JNI version
+          copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_generic_jni_trampoline_offset_));
+        } else {
+          copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
+        }
       }
       const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
       if (portable_code != nullptr) {
@@ -807,6 +814,12 @@
       uintptr_t value = quick_code - patch_location + patch->RelativeOffset();
       SetPatchLocation(patch, value);
     } else {
+      // generic JNI, not interpreter bridge from GetQuickOatCodeFor().
+      if (target->IsNative() &&
+          quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
+        code_offset = quick_generic_jni_trampoline_offset_;
+      }
+
       SetPatchLocation(patch, PointerToLowMemUInt32(GetOatAddress(code_offset)));
     }
   }
@@ -845,7 +858,7 @@
     if (patch->IsCall()) {
       const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
       const DexFile::MethodId& id = cpatch->GetDexFile().GetMethodId(cpatch->GetTargetMethodIdx());
-      uintptr_t expected = reinterpret_cast<uintptr_t>(&id);
+      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
       uint32_t actual = *patch_location;
       CHECK(actual == expected || actual == value) << std::hex
           << "actual=" << actual
@@ -855,7 +868,7 @@
     if (patch->IsType()) {
       const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
       const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
-      uintptr_t expected = reinterpret_cast<uintptr_t>(&id);
+      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
       uint32_t actual = *patch_location;
       CHECK(actual == expected || actual == value) << std::hex
           << "actual=" << actual
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index a1504ee..dff33ba 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -41,8 +41,8 @@
       : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
         oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
         interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
-        portable_resolution_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0),
-        quick_resolution_trampoline_offset_(0) {}
+        portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
+        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
 
   ~ImageWriter() {}
 
@@ -195,6 +195,7 @@
   uint32_t portable_imt_conflict_trampoline_offset_;
   uint32_t portable_resolution_trampoline_offset_;
   uint32_t portable_to_interpreter_bridge_offset_;
+  uint32_t quick_generic_jni_trampoline_offset_;
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 6dbba9f..93c3502 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -175,7 +175,7 @@
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(76U, sizeof(OatHeader));
+  EXPECT_EQ(80U, sizeof(OatHeader));
   EXPECT_EQ(28U, sizeof(OatMethodOffsets));
 }
 
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a400bdd..181240e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -60,6 +60,7 @@
     size_portable_imt_conflict_trampoline_(0),
     size_portable_resolution_trampoline_(0),
     size_portable_to_interpreter_bridge_(0),
+    size_quick_generic_jni_trampoline_(0),
     size_quick_imt_conflict_trampoline_(0),
     size_quick_resolution_trampoline_(0),
     size_quick_to_interpreter_bridge_(0),
@@ -256,6 +257,7 @@
     DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
     DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
     DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
+    DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline);
     DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
     DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
     DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge);
@@ -268,6 +270,7 @@
     oat_header_->SetPortableImtConflictTrampolineOffset(0);
     oat_header_->SetPortableResolutionTrampolineOffset(0);
     oat_header_->SetPortableToInterpreterBridgeOffset(0);
+    oat_header_->SetQuickGenericJniTrampolineOffset(0);
     oat_header_->SetQuickImtConflictTrampolineOffset(0);
     oat_header_->SetQuickResolutionTrampolineOffset(0);
     oat_header_->SetQuickToInterpreterBridgeOffset(0);
@@ -576,6 +579,7 @@
     DO_STAT(size_portable_imt_conflict_trampoline_);
     DO_STAT(size_portable_resolution_trampoline_);
     DO_STAT(size_portable_to_interpreter_bridge_);
+    DO_STAT(size_quick_generic_jni_trampoline_);
     DO_STAT(size_quick_imt_conflict_trampoline_);
     DO_STAT(size_quick_resolution_trampoline_);
     DO_STAT(size_quick_to_interpreter_bridge_);
@@ -675,6 +679,7 @@
     DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
     DO_TRAMPOLINE(portable_resolution_trampoline_);
     DO_TRAMPOLINE(portable_to_interpreter_bridge_);
+    DO_TRAMPOLINE(quick_generic_jni_trampoline_);
     DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
     DO_TRAMPOLINE(quick_resolution_trampoline_);
     DO_TRAMPOLINE(quick_to_interpreter_bridge_);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 3d4b48a..bab1a26 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -243,6 +243,7 @@
   UniquePtr<const std::vector<uint8_t> > portable_imt_conflict_trampoline_;
   UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
   UniquePtr<const std::vector<uint8_t> > portable_to_interpreter_bridge_;
+  UniquePtr<const std::vector<uint8_t> > quick_generic_jni_trampoline_;
   UniquePtr<const std::vector<uint8_t> > quick_imt_conflict_trampoline_;
   UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
   UniquePtr<const std::vector<uint8_t> > quick_to_interpreter_bridge_;
@@ -259,6 +260,7 @@
   uint32_t size_portable_imt_conflict_trampoline_;
   uint32_t size_portable_resolution_trampoline_;
   uint32_t size_portable_to_interpreter_bridge_;
+  uint32_t size_quick_generic_jni_trampoline_;
   uint32_t size_quick_imt_conflict_trampoline_;
   uint32_t size_quick_resolution_trampoline_;
   uint32_t size_quick_to_interpreter_bridge_;
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 5166d29..fc85ae3 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -127,6 +127,9 @@
 extern "C" void art_quick_throw_null_pointer_exception();
 extern "C" void art_quick_throw_stack_overflow(void*);
 
+// Generic JNI downcall
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+
 extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
@@ -182,6 +185,7 @@
   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
 
   // Locks
   qpoints->pLockObject = art_quick_lock_object;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index ed8bc13..71dcd7f 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -939,6 +939,8 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolution_trampoline
 
+UNIMPLEMENTED art_quick_generic_jni_trampoline
+
     .extern artQuickToInterpreterBridge
 ENTRY art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index e1b441a..41d79c2 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -128,6 +128,9 @@
 extern "C" void art_quick_throw_null_pointer_exception();
 extern "C" void art_quick_throw_stack_overflow(void*);
 
+// Generic JNI downcall
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+
 extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
@@ -183,6 +186,7 @@
   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
 
   // Locks
   qpoints->pLockObject = art_quick_lock_object;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d23be47..c3ae563 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1007,6 +1007,8 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_resolution_trampoline
 
+UNIMPLEMENTED art_quick_generic_jni_trampoline
+
     .extern artQuickToInterpreterBridge
 ENTRY art_quick_to_interpreter_bridge
     GENERATE_GLOBAL_POINTER
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 888310a..763cbde 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -109,6 +109,9 @@
 extern "C" void art_quick_throw_null_pointer_exception();
 extern "C" void art_quick_throw_stack_overflow(void*);
 
+// Generic JNI downcall
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+
 extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
@@ -164,6 +167,7 @@
   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
 
   // Locks
   qpoints->pLockObject = art_quick_lock_object;
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 8683a56..b24bfd5 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1170,6 +1170,11 @@
     DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_resolution_trampoline
 
+DEFINE_FUNCTION art_quick_generic_jni_trampoline
+    int3
+    int3
+END_FUNCTION art_quick_generic_jni_trampoline
+
 DEFINE_FUNCTION art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame
     mov %esp, %edx                // remember SP
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 589c7d9..fe298c8 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -110,6 +110,9 @@
 extern "C" void art_quick_throw_null_pointer_exception();
 extern "C" void art_quick_throw_stack_overflow(void*);
 
+// Generic JNI entrypoint
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+
 extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
@@ -165,6 +168,7 @@
   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
 
   // Locks
   qpoints->pLockObject = art_quick_lock_object;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index ac238f0..32e8434 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -635,6 +635,12 @@
 UNIMPLEMENTED art_quick_imt_conflict_trampoline
 UNIMPLEMENTED art_quick_resolution_trampoline
 
+
+    /*
+     * Called to do a generic JNI down-call
+     */
+UNIMPLEMENTED art_quick_generic_jni_trampoline
+
     /*
      * Called to bridge from the quick to interpreter ABI. On entry the arguments match those
      * of a quick call:
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 87323f9..6550532 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -184,7 +184,8 @@
       portable_resolution_trampoline_(nullptr),
       quick_resolution_trampoline_(nullptr),
       portable_imt_conflict_trampoline_(nullptr),
-      quick_imt_conflict_trampoline_(nullptr) {
+      quick_imt_conflict_trampoline_(nullptr),
+      quick_generic_jni_trampoline_(nullptr) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
   memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
@@ -987,6 +988,7 @@
   quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
   portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
   quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
+  quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
   mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
   mirror::ObjectArray<mirror::DexCache>* dex_caches =
       dex_caches_object->AsObjectArray<mirror::DexCache>();
@@ -1623,7 +1625,8 @@
                              const void* portable_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if ((quick_code == nullptr) && (portable_code == nullptr)) {
     // No code: need interpreter.
-    DCHECK(!method->IsNative());
+    // May return true for native code, in the case of generic JNI
+    // DCHECK(!method->IsNative());
     return true;
   }
 #ifdef ART_SEA_IR_MODE
@@ -1678,8 +1681,14 @@
     bool have_portable_code = false;
     if (enter_interpreter) {
       // Use interpreter entry point.
+
+      // check whether the method is native, in which case it's generic JNI
       portable_code = GetPortableToInterpreterBridge();
-      quick_code = GetQuickToInterpreterBridge();
+      if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) {
+        quick_code = GetQuickGenericJniTrampoline();
+      } else {
+        quick_code = GetQuickToInterpreterBridge();
+      }
     } else {
       if (portable_code == nullptr) {
         portable_code = GetPortableToQuickBridge();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 88dbb9c..e31a6cd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -351,6 +351,10 @@
     return portable_resolution_trampoline_;
   }
 
+  const void* GetQuickGenericJniTrampoline() const {
+      return quick_generic_jni_trampoline_;
+    }
+
   const void* GetQuickResolutionTrampoline() const {
     return quick_resolution_trampoline_;
   }
@@ -643,6 +647,7 @@
   const void* quick_resolution_trampoline_;
   const void* portable_imt_conflict_trampoline_;
   const void* quick_imt_conflict_trampoline_;
+  const void* quick_generic_jni_trampoline_;
 
   friend class ImageWriter;  // for GetClassRoots
   FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 2ced942..a8fb6c1 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -733,6 +733,11 @@
   return reinterpret_cast<void*>(art_quick_to_interpreter_bridge);
 }
 
+extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
+static inline const void* GetQuickGenericJniTrampoline() {
+  return reinterpret_cast<void*>(art_quick_generic_jni_trampoline);
+}
+
 static inline const void* GetQuickToPortableBridge() {
   // TODO: quick to portable bridge. Bug: 8196384
   return GetQuickToInterpreterBridge();
@@ -754,6 +759,10 @@
   return class_linker->GetQuickImtConflictTrampoline();
 }
 
+static inline const void* GetQuickGenericJniTrampoline(ClassLinker* class_linker) {
+  return class_linker->GetQuickGenericJniTrampoline();
+}
+
 extern "C" void art_portable_proxy_invoke_handler();
 static inline const void* GetPortableProxyInvokeHandler() {
   return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 011e926..5c3b824 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -87,6 +87,7 @@
   mirror::Object* (*pJniMethodEndWithReference)(jobject result, uint32_t cookie, Thread* self);
   mirror::Object* (*pJniMethodEndWithReferenceSynchronized)(jobject result, uint32_t cookie,
                                                     jobject locked, Thread* self);
+  void (*pQuickGenericJniTrampoline)(mirror::ArtMethod*);
 
   // Locks
   void (*pLockObject)(void*);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 5339b5e..ef40be8 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -817,4 +817,13 @@
   return code;
 }
 
+extern "C" const void* artQuickGenericJniTrampoline(mirror::ArtMethod* called,
+                                                    mirror::Object* receiver,
+                                                    Thread* thread, mirror::ArtMethod** sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  LOG(FATAL) << "artQuickGenericJniTrampoline not implemented: "
+      << PrettyMethod(called);
+  return NULL;
+}
+
 }  // namespace art
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 945cd77..c8eb3e2 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '1', '5', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '1', '6', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
@@ -67,6 +67,7 @@
   portable_imt_conflict_trampoline_offset_ = 0;
   portable_resolution_trampoline_offset_ = 0;
   portable_to_interpreter_bridge_offset_ = 0;
+  quick_generic_jni_trampoline_offset_ = 0;
   quick_imt_conflict_trampoline_offset_ = 0;
   quick_resolution_trampoline_offset_ = 0;
   quick_to_interpreter_bridge_offset_ = 0;
@@ -239,18 +240,37 @@
   UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset));
 }
 
+const void* OatHeader::GetQuickGenericJniTrampoline() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetQuickGenericJniTrampolineOffset();
+}
+
+uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(quick_generic_jni_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+  return quick_generic_jni_trampoline_offset_;
+}
+
+void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
+
+  quick_generic_jni_trampoline_offset_ = offset;
+  UpdateChecksum(&quick_generic_jni_trampoline_offset_, sizeof(offset));
+}
+
 const void* OatHeader::GetQuickImtConflictTrampoline() const {
   return reinterpret_cast<const uint8_t*>(this) + GetQuickImtConflictTrampolineOffset();
 }
 
 uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const {
   DCHECK(IsValid());
-  CHECK_GE(quick_imt_conflict_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+  CHECK_GE(quick_imt_conflict_trampoline_offset_, quick_generic_jni_trampoline_offset_);
   return quick_imt_conflict_trampoline_offset_;
 }
 
 void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) {
-  CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+  CHECK(offset == 0 || offset >= quick_generic_jni_trampoline_offset_);
   DCHECK(IsValid());
   DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
 
diff --git a/runtime/oat.h b/runtime/oat.h
index de840b5..2851f5c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -70,6 +70,9 @@
   uint32_t GetPortableToInterpreterBridgeOffset() const;
   void SetPortableToInterpreterBridgeOffset(uint32_t offset);
 
+  const void* GetQuickGenericJniTrampoline() const;
+  uint32_t GetQuickGenericJniTrampolineOffset() const;
+  void SetQuickGenericJniTrampolineOffset(uint32_t offset);
   const void* GetQuickResolutionTrampoline() const;
   uint32_t GetQuickResolutionTrampolineOffset() const;
   void SetQuickResolutionTrampolineOffset(uint32_t offset);
@@ -103,6 +106,7 @@
   uint32_t portable_imt_conflict_trampoline_offset_;
   uint32_t portable_resolution_trampoline_offset_;
   uint32_t portable_to_interpreter_bridge_offset_;
+  uint32_t quick_generic_jni_trampoline_offset_;
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4fe9169..3862ae2 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1752,6 +1752,7 @@
   QUICK_ENTRY_POINT_INFO(pThrowNoSuchMethod),
   QUICK_ENTRY_POINT_INFO(pThrowNullPointer),
   QUICK_ENTRY_POINT_INFO(pThrowStackOverflow),
+  QUICK_ENTRY_POINT_INFO(pQuickGenericJniTrampoline),
 };
 #undef QUICK_ENTRY_POINT_INFO