[VM interpreter] Provide arm64 stubs. Minor cleanups.

Change-Id: I4262df67765543ce4da12f1e53e84d52f3dbd4c3
Reviewed-on: https://dart-review.googlesource.com/61114
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
diff --git a/pkg/vm/tool/test_bytecode b/pkg/vm/tool/test_bytecode
index 7b05997..3c1d0e1 100755
--- a/pkg/vm/tool/test_bytecode
+++ b/pkg/vm/tool/test_bytecode
@@ -68,7 +68,7 @@
 #   $BUILD_DIR/test_bytecode.dill $BUILD_DIR/test_bytecode.txt
 
 # Required flags.
-DART_VM_FLAGS="--preview-dart-2 --optimization-counter-threshold=-1 $DART_VM_FLAGS"
+DART_VM_FLAGS="--optimization-counter-threshold=-1 $DART_VM_FLAGS"
 
 # Optional flags examples. Uncomment as needed.
 # DART_VM_FLAGS="--force-log-flush --isolate-log-filter=\"\" $DART_VM_FLAGS"
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 5dbbdd5..2d4305b 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -911,7 +911,13 @@
     RawCode* code = function->ptr()->code_;
     ASSERT(code != StubCode::LazyCompile_entry()->code());
     // TODO(regis): Once we share the same stack, try to invoke directly.
-
+#if defined(DEBUG)
+    if (IsTracingExecution()) {
+      THR_Print("%" Pu64 " ", icount_);
+      THR_Print("invoking compiled %s\n",
+                Function::Handle(function).ToCString());
+    }
+#endif
     // On success, returns a RawInstance.  On failure, a RawError.
     typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
                                      RawObject * *arg0, Thread * thread);
@@ -953,6 +959,12 @@
   ASSERT(Function::HasBytecode(function));
   // Bytecode was loaded in the above compilation step.
   // Stay in interpreter.
+#if defined(DEBUG)
+  if (IsTracingExecution()) {
+    THR_Print("%" Pu64 " ", icount_);
+    THR_Print("invoking %s\n", Function::Handle(function).ToCString());
+  }
+#endif
   RawCode* bytecode = function->ptr()->bytecode_;
   RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
   callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
@@ -978,17 +990,17 @@
   RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
 
   RawFunction* function = FrameFunction(callee_fp);
+  if (Function::HasCode(function) || !Function::HasBytecode(function)) {
+    // TODO(regis): If the function is a dispatcher, execute the dispatch here.
+    return InvokeCompiled(thread, function, argdesc_, call_base, call_top, pc,
+                          FP, SP);
+  }
 #if defined(DEBUG)
   if (IsTracingExecution()) {
     THR_Print("%" Pu64 " ", icount_);
     THR_Print("invoking %s\n", Function::Handle(function).ToCString());
   }
 #endif
-  if (Function::HasCode(function) || !Function::HasBytecode(function)) {
-    // TODO(regis): If the function is a dispatcher, execute the dispatch here.
-    return InvokeCompiled(thread, function, argdesc_, call_base, call_top, pc,
-                          FP, SP);
-  }
   RawCode* bytecode = function->ptr()->bytecode_;
   callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
   callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 4069e998..2cbab48 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -90,9 +90,9 @@
 #if !defined(TARGET_ARCH_DBC)
   ASSERT(HasBeenInitialized());
 #if defined(DART_USE_INTERPRETER)
-  // Recognize special marker set up by interpreter in entry frame.
-  if (is_interpreted_frame && (pc & 2) != 0) {
-    return true;
+  if (is_interpreted_frame) {
+    // Recognize special marker set up by interpreter in entry frame.
+    return (pc & 2) != 0;
   }
   {
     uword entry = StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint();
@@ -106,7 +106,7 @@
   uword size = StubCode::InvokeDartCodeSize();
   return (pc >= entry) && (pc < (entry + size));
 #elif defined(DART_USE_INTERPRETER)
-#error "Simultaneous usage of simulator and interpreter not yet supported."
+#error "Simultaneous usage of DBC simulator and interpreter not yet supported."
 #else
   // On DBC we use a special marker PC to signify entry frame because there is
   // no such thing as invocation stub.
diff --git a/runtime/vm/stub_code_arm64.cc b/runtime/vm/stub_code_arm64.cc
index e83970e..97a107b 100644
--- a/runtime/vm/stub_code_arm64.cc
+++ b/runtime/vm/stub_code_arm64.cc
@@ -958,8 +958,138 @@
   __ ret();
 }
 
+// Called when invoking compiled Dart code from interpreted Dart code.
+// Input parameters:
+//   LR : points to return address.
+//   R0 : raw code object of the Dart function to call.
+//   R1 : arguments raw descriptor array.
+//   R2 : address of first argument.
+//   R3 : current thread.
 void StubCode::GenerateInvokeDartCodeFromBytecodeStub(Assembler* assembler) {
-  __ Unimplemented("Interpreter not yet supported");
+#if defined(DART_USE_INTERPRETER)
+  // Copy the C stack pointer (R31) into the stack pointer we'll actually use
+  // to access the stack.
+  __ SetupDartSP();
+  __ EnterFrame(0);
+
+  // Push code object to PC marker slot.
+  __ ldr(TMP,
+         Address(R3, Thread::invoke_dart_code_from_bytecode_stub_offset()));
+  __ Push(TMP);
+
+  // Save the callee-saved registers.
+  for (int i = kAbiFirstPreservedCpuReg; i <= kAbiLastPreservedCpuReg; i++) {
+    const Register r = static_cast<Register>(i);
+    // We use str instead of the Push macro because we will be pushing the PP
+    // register when it is not holding a pool-pointer since we are coming from
+    // C++ code.
+    __ str(r, Address(SP, -1 * kWordSize, Address::PreIndex));
+  }
+
+  // Save the bottom 64-bits of callee-saved V registers.
+  for (int i = kAbiFirstPreservedFpuReg; i <= kAbiLastPreservedFpuReg; i++) {
+    const VRegister r = static_cast<VRegister>(i);
+    __ PushDouble(r);
+  }
+
+  // Set up THR, which caches the current thread in Dart code.
+  if (THR != R3) {
+    __ mov(THR, R3);
+  }
+
+  // Save the current VMTag on the stack.
+  __ LoadFromOffset(R4, THR, Thread::vm_tag_offset());
+  __ Push(R4);
+
+  // Mark that the thread is executing Dart code.
+  __ LoadImmediate(R6, VMTag::kDartTagId);
+  __ StoreToOffset(R6, THR, Thread::vm_tag_offset());
+
+  // Save top resource and top exit frame info. Use R6 as a temporary register.
+  // StackFrameIterator reads the top exit frame info saved in this frame.
+  __ LoadFromOffset(R6, THR, Thread::top_resource_offset());
+  __ StoreToOffset(ZR, THR, Thread::top_resource_offset());
+  __ Push(R6);
+  __ LoadFromOffset(R6, THR, Thread::top_exit_frame_info_offset());
+  __ StoreToOffset(ZR, THR, Thread::top_exit_frame_info_offset());
+  // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
+  ASSERT(kExitLinkSlotFromEntryFp == -22);
+  __ Push(R6);
+
+  // Load arguments descriptor array into R4, which is passed to Dart code.
+  __ mov(R4, R1);
+
+  // Load number of arguments into R5 and adjust count for type arguments.
+  __ LoadFieldFromOffset(R5, R4, ArgumentsDescriptor::count_offset());
+  __ LoadFieldFromOffset(R3, R4, ArgumentsDescriptor::type_args_len_offset());
+  __ AddImmediate(TMP, R5, 1);  // Include the type arguments.
+  __ cmp(R3, Operand(0));
+  __ csinc(R5, R5, TMP, EQ);  // R5 <- (R3 == 0) ? R5 : TMP + 1 (R5 : R5 + 2).
+  __ SmiUntag(R5);
+
+  // R2 points to first argument.
+  // Set up arguments for the Dart call.
+  Label push_arguments;
+  Label done_push_arguments;
+  __ cmp(R5, Operand(0));
+  __ b(&done_push_arguments, EQ);  // check if there are arguments.
+  __ LoadImmediate(R1, 0);
+  __ Bind(&push_arguments);
+  __ ldr(R3, Address(R2));
+  __ Push(R3);
+  __ add(R1, R1, Operand(1));
+  __ add(R2, R2, Operand(kWordSize));
+  __ cmp(R1, Operand(R5));
+  __ b(&push_arguments, LT);
+  __ Bind(&done_push_arguments);
+
+  // We now load the pool pointer(PP) with a GC safe value as we are about to
+  // invoke dart code. We don't need a real object pool here.
+  // Smi zero does not work because ARM64 assumes PP to be untagged.
+  __ LoadObject(PP, Object::null_object());
+
+  // Call the Dart code entrypoint.
+  __ mov(CODE_REG, R0);
+  __ ldr(R0, FieldAddress(CODE_REG, Code::entry_point_offset()));
+  __ blr(R0);  // R4 is the arguments descriptor array.
+
+  // Get rid of arguments pushed on the stack.
+  __ AddImmediate(SP, FP, kExitLinkSlotFromEntryFp * kWordSize);
+
+  // Restore the saved top exit frame info and top resource back into the
+  // Isolate structure. Uses R6 as a temporary register for this.
+  __ Pop(R6);
+  __ StoreToOffset(R6, THR, Thread::top_exit_frame_info_offset());
+  __ Pop(R6);
+  __ StoreToOffset(R6, THR, Thread::top_resource_offset());
+
+  // Restore the current VMTag from the stack.
+  __ Pop(R4);
+  __ StoreToOffset(R4, THR, Thread::vm_tag_offset());
+
+  // Restore the bottom 64-bits of callee-saved V registers.
+  for (int i = kAbiLastPreservedFpuReg; i >= kAbiFirstPreservedFpuReg; i--) {
+    const VRegister r = static_cast<VRegister>(i);
+    __ PopDouble(r);
+  }
+
+  // Restore C++ ABI callee-saved registers.
+  for (int i = kAbiLastPreservedCpuReg; i >= kAbiFirstPreservedCpuReg; i--) {
+    Register r = static_cast<Register>(i);
+    // We use ldr instead of the Pop macro because we will be popping the PP
+    // register when it is not holding a pool-pointer since we are returning to
+    // C++ code. We also skip the dart stack pointer SP, since we are still
+    // using it as the stack pointer.
+    __ ldr(r, Address(SP, 1 * kWordSize, Address::PostIndex));
+  }
+
+  // Restore the frame pointer and C stack pointer and return.
+  __ LeaveFrame();
+  __ RestoreCSP();
+  __ ret();
+#else
+  __ Stop("Not using interpreter");
+#endif  // defined(DART_USE_INTERPRETER)
 }
 
 // Called for inline allocation of contexts.
@@ -1749,8 +1879,115 @@
   __ br(R2);
 }
 
+// Stub for interpreting a function call.
+// R5: IC-Data (for methods).
+// R4: Arguments descriptor.
+// R0: Function.
 void StubCode::GenerateInterpretCallStub(Assembler* assembler) {
-  __ Unimplemented("Interpreter not yet supported");
+#if defined(DART_USE_INTERPRETER)
+  const intptr_t thread_offset = NativeArguments::thread_offset();
+  const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
+  const intptr_t argv_offset = NativeArguments::argv_offset();
+  const intptr_t retval_offset = NativeArguments::retval_offset();
+
+  __ SetPrologueOffset();
+  __ EnterStubFrame();
+
+  // Save exit frame information to enable stack walking as we are about
+  // to transition to Dart VM C++ code.
+  __ StoreToOffset(FP, THR, Thread::top_exit_frame_info_offset());
+
+#if defined(DEBUG)
+  {
+    Label ok;
+    // Check that we are always entering from Dart code.
+    __ LoadFromOffset(R8, THR, Thread::vm_tag_offset());
+    __ CompareImmediate(R8, VMTag::kDartTagId);
+    __ b(&ok, EQ);
+    __ Stop("Not coming from Dart code.");
+    __ Bind(&ok);
+  }
+#endif
+
+  // Mark that the thread is executing VM code.
+  __ StoreToOffset(R5, THR, Thread::vm_tag_offset());
+
+  // Setup space on stack for result of the interpreted function call.
+  __ Push(ZR);
+
+  // Set callee-saved R23 to point to return value slot.
+  __ mov(R23, SP);
+
+  // Push first 3 arguments of the interpreted function call.
+  __ Push(R0);  // Function.
+  __ Push(R5);  // ICData/MegamorphicCache.
+  __ Push(R4);  // Arguments descriptor array.
+
+  // Adjust arguments count.
+  __ LoadFieldFromOffset(R3, R4, ArgumentsDescriptor::type_args_len_offset());
+  __ AddImmediate(TMP, R2, 1);  // Include the type arguments.
+  __ cmp(R3, Operand(0));
+  __ csinc(R2, R2, TMP, EQ);  // R2 <- (R3 == 0) ? R2 : TMP + 1 (R2 : R2 + 2).
+
+  // Push 4th Dart argument of the interpreted function call.
+  // R2: Smi-tagged arguments array length.
+  PushArrayOfArguments(assembler);
+  const intptr_t kNumArgs = 4;
+
+  // Reserve space for arguments and align frame before entering C++ world.
+  // NativeArguments are passed in registers.
+  ASSERT(sizeof(NativeArguments) == 4 * kWordSize);
+  __ ReserveAlignedFrameSpace(sizeof(NativeArguments));
+
+  // Pass NativeArguments structure by value and call runtime.
+  // Registers R0, R1, R2, and R3 are used.
+
+  ASSERT(thread_offset == 0 * kWordSize);
+  __ mov(R0, THR);  // Set thread in NativeArgs.
+
+  ASSERT(argc_tag_offset == 1 * kWordSize);
+  __ LoadImmediate(R1, kNumArgs);  // Set argc in NativeArguments.
+
+  ASSERT(argv_offset == 2 * kWordSize);
+  __ AddImmediate(R2, R23, -kWordSize);  // Set argv in NativeArguments.
+
+  ASSERT(retval_offset == 3 * kWordSize);
+  __ mov(R3, R23);  // Set retval in NativeArguments.
+
+  __ StoreToOffset(R0, SP, thread_offset);
+  __ StoreToOffset(R1, SP, argc_tag_offset);
+  __ StoreToOffset(R2, SP, argv_offset);
+  __ StoreToOffset(R3, SP, retval_offset);
+  __ mov(R0, SP);  // Pass the pointer to the NativeArguments.
+
+  // We are entering runtime code, so the C stack pointer must be restored from
+  // the stack limit to the top of the stack. We cache the stack limit address
+  // in a callee-saved register.
+  __ mov(R25, CSP);
+  __ mov(CSP, SP);
+
+  __ LoadImmediate(R5, kInterpretCallRuntimeEntry.GetEntryPoint());
+  __ blr(R5);
+
+  // Restore SP and CSP.
+  __ mov(SP, CSP);
+  __ mov(CSP, R25);
+
+  // Mark that the thread is executing Dart code.
+  __ LoadImmediate(R2, VMTag::kDartTagId);
+  __ StoreToOffset(R2, THR, Thread::vm_tag_offset());
+
+  // Reset exit frame information in Isolate structure.
+  __ StoreToOffset(ZR, THR, Thread::top_exit_frame_info_offset());
+
+  // Load result of interpreted function call into R0.
+  __ LoadFromOffset(R0, R23, 0);
+
+  __ LeaveStubFrame();
+  __ ret();
+#else
+  __ Stop("Not using interpreter");
+#endif  // defined(DART_USE_INTERPRETER)
 }
 
 // R5: Contains an ICData.
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index 9c55324..6b0b539 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -980,6 +980,7 @@
   __ movq(Address(THR, Thread::top_resource_offset()), Immediate(0));
   __ movq(RAX, Address(THR, Thread::top_exit_frame_info_offset()));
   __ pushq(RAX);
+  __ movq(Address(THR, Thread::top_exit_frame_info_offset()), Immediate(0));
 
 // The constant kExitLinkSlotFromEntryFp must be kept in sync with the
 // code below.
@@ -994,8 +995,6 @@
   }
 #endif
 
-  __ movq(Address(THR, Thread::top_exit_frame_info_offset()), Immediate(0));
-
   // Load arguments descriptor array into R10, which is passed to Dart code.
   __ movq(R10, kArgDescReg);
 
@@ -1015,7 +1014,9 @@
   __ SmiUntag(RBX);
 
   // Compute address of first argument into RDX.
-  ASSERT(kArg0Reg == RDX);
+  if (kArg0Reg != RDX) {  // Different registers on WIN64.
+    __ movq(RDX, kArg0Reg);
+  }
 
   // Set up arguments for the Dart call.
   Label push_arguments;
@@ -1060,7 +1061,7 @@
   __ ret();
 #else
   __ Stop("Not using interpreter");
-#endif
+#endif  // defined(DART_USE_INTERPRETER)
 }
 
 // Called for inline allocation of contexts.
@@ -1953,7 +1954,7 @@
   __ ret();
 #else
   __ Stop("Not using interpreter");
-#endif
+#endif  // defined(DART_USE_INTERPRETER)
 }
 
 // RBX: Contains an ICData.