Add a nterp trampoline in the oat file, replaced at runtime.

To avoid checking whether an ArtMethod can run nterp when loading an
image, record that information directly in the image, and patch the
entrypoint so nterp can do direct pointer checks when calling an
ArtMethod.

Test: test.py
Bug: 177444058
Change-Id: Ia87367c37848e1efe33336b3677490c07d7e1767
diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc
index 7e76bc3..9670e6e 100644
--- a/runtime/nterp_helpers.cc
+++ b/runtime/nterp_helpers.cc
@@ -85,32 +85,55 @@
 
 static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize);
 
-static constexpr size_t NterpGetFrameEntrySize() {
-  uint32_t core_spills =
-      RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
-  uint32_t fp_spills =
-      RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+static constexpr size_t NterpGetFrameEntrySize(InstructionSet isa) {
+  uint32_t core_spills = 0;
+  uint32_t fp_spills = 0;
   // Note: the return address is considered part of the callee saves.
-  return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * kPointerSize;
+  switch (isa) {
+    case InstructionSet::kX86:
+      core_spills = x86::X86CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      fp_spills = x86::X86CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      break;
+    case InstructionSet::kX86_64:
+      core_spills =
+          x86_64::X86_64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      fp_spills = x86_64::X86_64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      break;
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+      core_spills = arm::ArmCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      fp_spills = arm::ArmCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      break;
+    case InstructionSet::kArm64:
+      core_spills = arm64::Arm64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      fp_spills = arm64::Arm64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+      break;
+    default:
+      InstructionSetAbort(isa);
+  }
+  // Note: the return address is considered part of the callee saves.
+  return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) *
+      static_cast<size_t>(InstructionSetPointerSize(isa));
 }
 
-size_t NterpGetFrameSize(ArtMethod* method) {
+size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa) {
   CodeItemDataAccessor accessor(method->DexInstructionData());
   const uint16_t num_regs = accessor.RegistersSize();
   const uint16_t out_regs = accessor.OutsSize();
+  size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa));
 
   // Note: There may be two pieces of alignment but there is no need to align
   // out args to `kPointerSize` separately before aligning to kStackAlignment.
-  static_assert(IsAligned<kPointerSize>(kStackAlignment));
-  static_assert(IsAligned<kPointerSize>(NterpGetFrameEntrySize()));
-  static_assert(IsAligned<kPointerSize>(kVRegSize * 2));
+  DCHECK(IsAlignedParam(kStackAlignment, pointer_size));
+  DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size));
+  DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size));
   size_t frame_size =
-      NterpGetFrameEntrySize() +
+      NterpGetFrameEntrySize(isa) +
       (num_regs * kVRegSize) * 2 +  // dex registers and reference registers
-      kPointerSize +  // previous frame
-      kPointerSize +  // saved dex pc
+      pointer_size +  // previous frame
+      pointer_size +  // saved dex pc
       (out_regs * kVRegSize) +  // out arguments
-      kPointerSize;  // method
+      pointer_size;  // method
   return RoundUp(frame_size, kStackAlignment);
 }
 
@@ -164,4 +187,16 @@
   return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd);
 }
 
+bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) {
+  return !method->IsNative() &&
+      method->IsInvokable() &&
+      // Nterp supports the same methods the compiler supports.
+      method->IsCompilable() &&
+      !method->MustCountLocks() &&
+      // Proxy methods do not go through the JIT like other methods, so we don't
+      // run them with nterp.
+      !method->IsProxyMethod() &&
+      NterpGetFrameSize(method, isa) <= interpreter::kNterpMaxFrame;
+}
+
 }  // namespace art