Initial ARM JNI compiler support.

Change-Id: I85183eec9a2645e6cb074b4b18bc6af800a77e06
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index 900d8d4..a728239 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -3,6 +3,7 @@
 #include "assembler.h"
 #include "logging.h"
 #include "offsets.h"
+#include "thread.h"
 #include "utils.h"
 
 namespace art {
@@ -241,7 +242,6 @@
   }
 }
 
-
 void Assembler::and_(Register rd, Register rn, ShifterOperand so,
                      Condition cond) {
   EmitType01(cond, so.type(), AND, 0, rn, rd, so);
@@ -1259,6 +1259,39 @@
   }
 }
 
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldLoadOffset, as expected by JIT::GuardedLoadFromOffset.
+void Assembler::LoadSFromOffset(SRegister reg,
+                                Register base,
+                                int32_t offset,
+                                Condition cond) {
+  if (!Address::CanHoldLoadOffset(kLoadSWord, offset)) {
+    CHECK_NE(base, IP);
+    LoadImmediate(IP, offset, cond);
+    add(IP, IP, ShifterOperand(base), cond);
+    base = IP;
+    offset = 0;
+  }
+  CHECK(Address::CanHoldLoadOffset(kLoadSWord, offset));
+  vldrs(reg, Address(base, offset), cond);
+}
+
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldLoadOffset, as expected by JIT::GuardedLoadFromOffset.
+void Assembler::LoadDFromOffset(DRegister reg,
+                                Register base,
+                                int32_t offset,
+                                Condition cond) {
+  if (!Address::CanHoldLoadOffset(kLoadDWord, offset)) {
+    CHECK_NE(base, IP);
+    LoadImmediate(IP, offset, cond);
+    add(IP, IP, ShifterOperand(base), cond);
+    base = IP;
+    offset = 0;
+  }
+  CHECK(Address::CanHoldLoadOffset(kLoadDWord, offset));
+  vldrd(reg, Address(base, offset), cond);
+}
 
 // Implementation note: this method must emit at most one instruction when
 // Address::CanHoldStoreOffset.
@@ -1294,19 +1327,53 @@
   }
 }
 
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldStoreOffset, as expected by JIT::GuardedStoreToOffset.
+void Assembler::StoreSToOffset(SRegister reg,
+                               Register base,
+                               int32_t offset,
+                               Condition cond) {
+  if (!Address::CanHoldStoreOffset(kStoreSWord, offset)) {
+    CHECK_NE(base, IP);
+    LoadImmediate(IP, offset, cond);
+    add(IP, IP, ShifterOperand(base), cond);
+    base = IP;
+    offset = 0;
+  }
+  CHECK(Address::CanHoldStoreOffset(kStoreSWord, offset));
+  vstrs(reg, Address(base, offset), cond);
+}
+
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldStoreOffset, as expected by JIT::GuardedStoreSToOffset.
+void Assembler::StoreDToOffset(DRegister reg,
+                               Register base,
+                               int32_t offset,
+                               Condition cond) {
+  if (!Address::CanHoldStoreOffset(kStoreDWord, offset)) {
+    CHECK_NE(base, IP);
+    LoadImmediate(IP, offset, cond);
+    add(IP, IP, ShifterOperand(base), cond);
+    base = IP;
+    offset = 0;
+  }
+  CHECK(Address::CanHoldStoreOffset(kStoreDWord, offset));
+  vstrd(reg, Address(base, offset), cond);
+}
+
 // Emit code that will create an activation on the stack
 void Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg) {
   CHECK(IsAligned(frame_size, 16));
   // TODO: use stm/ldm
-  StoreToOffset(kStoreWord, LR, SP, 0);
-  StoreToOffset(kStoreWord, method_reg.AsCoreRegister(), SP, -4);
   AddConstant(SP, -frame_size);
+  StoreToOffset(kStoreWord, LR, SP, frame_size - 4);
+  StoreToOffset(kStoreWord, method_reg.AsCoreRegister(), SP, 0);
 }
 
 // Emit code that will remove an activation from the stack
 void Assembler::RemoveFrame(size_t frame_size) {
   CHECK(IsAligned(frame_size, 16));
-  LoadFromOffset(kLoadWord, LR, SP, 0);
+  LoadFromOffset(kLoadWord, LR, SP, frame_size - 4);
   AddConstant(SP, frame_size);
   mov(PC, ShifterOperand(LR));
 }
@@ -1323,12 +1390,21 @@
 
 // Store bytes from the given register onto the stack
 void Assembler::Store(FrameOffset dest, ManagedRegister src, size_t size) {
-  if (src.IsCoreRegister()) {
+  if (src.IsNoRegister()) {
+    CHECK_EQ(0u, size);
+  } else if (src.IsCoreRegister()) {
     CHECK_EQ(4u, size);
     StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
+  } else if (src.IsRegisterPair()) {
+    CHECK_EQ(8u, size);
+    StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
+    StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
+                  SP, dest.Int32Value() + 4);
+  } else if (src.IsSRegister()) {
+    StoreSToOffset(src.AsSRegister(), SP, dest.Int32Value());
   } else {
-    // VFP
-    LOG(FATAL) << "TODO";
+    CHECK(src.IsDRegister());
+    StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value());
   }
 }
 
@@ -1370,12 +1446,21 @@
 }
 
 void Assembler::Load(ManagedRegister dest, FrameOffset src, size_t size) {
-  if (dest.IsCoreRegister()) {
+  if (dest.IsNoRegister()) {
+    CHECK_EQ(0u, size);
+  } else if (dest.IsCoreRegister()) {
     CHECK_EQ(4u, size);
     LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
+  } else if (dest.IsRegisterPair()) {
+    CHECK_EQ(8u, size);
+    LoadFromOffset(kLoadWord, dest.AsRegisterPairLow(), SP, src.Int32Value());
+    LoadFromOffset(kLoadWord, dest.AsRegisterPairHigh(),
+                   SP, src.Int32Value() + 4);
+  } else if (dest.IsSRegister()) {
+    LoadSFromOffset(dest.AsSRegister(), SP, src.Int32Value());
   } else {
-    // TODO: VFP
-    LOG(FATAL) << "Unimplemented";
+    CHECK(dest.IsDRegister());
+    LoadDFromOffset(dest.AsDRegister(), SP, src.Int32Value());
   }
 }
 
@@ -1386,7 +1471,7 @@
 }
 
 void Assembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs,
-                          ManagedRegister scratch) {
+                                     ManagedRegister scratch) {
   CHECK(scratch.IsCoreRegister());
   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
                  TR, thr_offs.Int32Value());
@@ -1395,7 +1480,7 @@
 }
 
 void Assembler::CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs,
-                        ManagedRegister scratch) {
+                                   ManagedRegister scratch) {
   CHECK(scratch.IsCoreRegister());
   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
                  SP, fr_offs.Int32Value());
@@ -1417,12 +1502,14 @@
 }
 
 void Assembler::Move(ManagedRegister dest, ManagedRegister src) {
-  if (dest.IsCoreRegister()) {
-    CHECK(src.IsCoreRegister());
-    mov(dest.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
-  } else {
-    // TODO: VFP
-    LOG(FATAL) << "Unimplemented";
+  if (!dest.Equals(src)) {
+    if (dest.IsCoreRegister()) {
+      CHECK(src.IsCoreRegister());
+      mov(dest.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
+    } else {
+      // TODO: VFP
+      LOG(FATAL) << "Unimplemented";
+    }
   }
 }
 
@@ -1443,12 +1530,17 @@
 void Assembler::CreateStackHandle(ManagedRegister out_reg,
                                   FrameOffset handle_offset,
                                   ManagedRegister in_reg, bool null_allowed) {
-  CHECK(in_reg.IsCoreRegister());
+  CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister());
   CHECK(out_reg.IsCoreRegister());
   if (null_allowed) {
     // Null values get a handle value of 0.  Otherwise, the handle value is
     // the address in the stack handle block holding the reference.
     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+    if (in_reg.IsNoRegister()) {
+      LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
+                     SP, handle_offset.Int32Value());
+      in_reg = out_reg;
+    }
     cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
     if (!out_reg.Equals(in_reg)) {
       LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
@@ -1508,19 +1600,73 @@
   // TODO: place reference map on call
 }
 
+void Assembler::Call(FrameOffset base, Offset offset,
+                     ManagedRegister scratch) {
+  CHECK(scratch.IsCoreRegister());
+  // Call *(*(SP + base) + offset)
+  LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                 SP, base.Int32Value());
+  LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                 scratch.AsCoreRegister(), offset.Int32Value());
+  blx(scratch.AsCoreRegister());
+  // TODO: place reference map on call
+}
+
 // Generate code to check if Thread::Current()->suspend_count_ is non-zero
 // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
 // at the next instruction.
 void Assembler::SuspendPoll(ManagedRegister scratch, ManagedRegister return_reg,
                             FrameOffset return_save_location,
                             size_t return_size) {
-  LOG(WARNING) << "Unimplemented: Suspend poll";
+  SuspendCountSlowPath* slow = new SuspendCountSlowPath(return_reg,
+                                                        return_save_location,
+                                                        return_size);
+  buffer_.EnqueueSlowPath(slow);
+  LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                 TR, Thread::SuspendCountOffset().Int32Value());
+  cmp(scratch.AsCoreRegister(), ShifterOperand(0));
+  b(slow->Entry(), NE);
+  Bind(slow->Continuation());
+}
+
+void SuspendCountSlowPath::Emit(Assembler* sp_asm) {
+  sp_asm->Bind(&entry_);
+  // Save return value
+  sp_asm->Store(return_save_location_, return_register_, return_size_);
+  // Pass top of stack as argument
+  sp_asm->mov(R0, ShifterOperand(SP));
+  sp_asm->LoadFromOffset(kLoadWord, R12, TR,
+                         Thread::SuspendCountEntryPointOffset().Int32Value());
+  // Note: assume that link register will be spilled/filled on method entry/exit
+  sp_asm->blx(R12);
+  // Reload return value
+  sp_asm->Load(return_register_, return_save_location_, return_size_);
+  sp_asm->b(&continuation_);
 }
 
 // Generate code to check if Thread::Current()->exception_ is non-null
 // and branch to a ExceptionSlowPath if it is.
 void Assembler::ExceptionPoll(ManagedRegister scratch) {
-  LOG(WARNING) << "Unimplemented: Exception poll";
+  ExceptionSlowPath* slow = new ExceptionSlowPath();
+  buffer_.EnqueueSlowPath(slow);
+  LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                 TR, Thread::ExceptionOffset().Int32Value());
+  cmp(scratch.AsCoreRegister(), ShifterOperand(0));
+  b(slow->Entry(), NE);
+  Bind(slow->Continuation());
+}
+
+void ExceptionSlowPath::Emit(Assembler* sp_asm) {
+  sp_asm->Bind(&entry_);
+  // Pass top of stack as argument
+  sp_asm->mov(R0, ShifterOperand(SP));
+  sp_asm->LoadFromOffset(kLoadWord, R12, TR,
+                         Thread::ExceptionEntryPointOffset().Int32Value());
+  // Note: assume that link register will be spilled/filled on method entry/exit
+  sp_asm->blx(R12);
+  // TODO: this call should never return as it should make a long jump to
+  // the appropriate catch block
+  sp_asm->b(&continuation_);
 }
 
 }  // namespace art