Add an arm32 port of nterp.

Currently using arm32, will try thumb once this CL lands to compare
performance.

Test: test.py, run-libcore-tests, device boots
Bug: 112676029

Change-Id: I6535e2982a3ceed83eba6664fc8ba8609974bc08
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index 74e49e7..3d92473 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -118,6 +118,111 @@
   UpdateCache(self, dex_pc_ptr, reinterpret_cast<size_t>(value));
 }
 
+#ifdef __arm__
+
+extern "C" void NterpStoreArm32Fprs(const char* shorty,
+                                    uint32_t* registers,
+                                    uint32_t* stack_args,
+                                    const uint32_t* fprs) {
+  // Note `shorty` has already the returned type removed.
+  ScopedAssertNoThreadSuspension sants("In nterp");
+  uint32_t arg_index = 0;
+  uint32_t fpr_double_index = 0;
+  uint32_t fpr_index = 0;
+  for (uint32_t shorty_index = 0; shorty[shorty_index] != '\0'; ++shorty_index) {
+    char arg_type = shorty[shorty_index];
+    switch (arg_type) {
+      case 'D': {
+        // Double should not overlap with float.
+        fpr_double_index = std::max(fpr_double_index, RoundUp(fpr_index, 2));
+        if (fpr_double_index < 16) {
+          registers[arg_index] = fprs[fpr_double_index++];
+          registers[arg_index + 1] = fprs[fpr_double_index++];
+        } else {
+          registers[arg_index] = stack_args[arg_index];
+          registers[arg_index + 1] = stack_args[arg_index + 1];
+        }
+        arg_index += 2;
+        break;
+      }
+      case 'F': {
+        if (fpr_index % 2 == 0) {
+          fpr_index = std::max(fpr_double_index, fpr_index);
+        }
+        if (fpr_index < 16) {
+          registers[arg_index] = fprs[fpr_index++];
+        } else {
+          registers[arg_index] = stack_args[arg_index];
+        }
+        arg_index++;
+        break;
+      }
+      case 'J': {
+        arg_index += 2;
+        break;
+      }
+      default: {
+        arg_index++;
+        break;
+      }
+    }
+  }
+}
+
+extern "C" void NterpSetupArm32Fprs(const char* shorty,
+                                    uint32_t dex_register,
+                                    uint32_t stack_index,
+                                    uint32_t* fprs,
+                                    uint32_t* registers,
+                                    uint32_t* stack_args) {
+  // Note `shorty` has already the returned type removed.
+  ScopedAssertNoThreadSuspension sants("In nterp");
+  uint32_t fpr_double_index = 0;
+  uint32_t fpr_index = 0;
+  for (uint32_t shorty_index = 0; shorty[shorty_index] != '\0'; ++shorty_index) {
+    char arg_type = shorty[shorty_index];
+    switch (arg_type) {
+      case 'D': {
+        // Double should not overlap with float.
+        fpr_double_index = std::max(fpr_double_index, RoundUp(fpr_index, 2));
+        if (fpr_double_index < 16) {
+          fprs[fpr_double_index++] = registers[dex_register++];
+          fprs[fpr_double_index++] = registers[dex_register++];
+          stack_index += 2;
+        } else {
+          stack_args[stack_index++] = registers[dex_register++];
+          stack_args[stack_index++] = registers[dex_register++];
+        }
+        break;
+      }
+      case 'F': {
+        if (fpr_index % 2 == 0) {
+          fpr_index = std::max(fpr_double_index, fpr_index);
+        }
+        if (fpr_index < 16) {
+          fprs[fpr_index++] = registers[dex_register++];
+          stack_index++;
+        } else {
+          stack_args[stack_index++] = registers[dex_register++];
+        }
+        break;
+      }
+      case 'J': {
+        stack_index += 2;
+        dex_register += 2;
+        break;
+      }
+      default: {
+        stack_index++;
+        dex_register++;
+        break;
+      }
+    }
+  }
+}
+
+#endif
+
 extern "C" const dex::CodeItem* NterpGetCodeItem(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedAssertNoThreadSuspension sants("In nterp");
@@ -294,6 +399,7 @@
   } else if (resolved_method->GetDeclaringClass()->IsStringClass()
              && !resolved_method->IsStatic()
              && resolved_method->IsConstructor()) {
+    CHECK_NE(invoke_type, kSuper);
     resolved_method = WellKnownClasses::StringInitToStringFactory(resolved_method);
     // Or the result with 1 to notify to nterp this is a string init method. We
     // also don't cache the result as we don't want nterp to have its fast path always