[vm, compiler] Match the C frame pointer convention on RISC-V.

The RISC-V frame pointer convention is different from that of x86 and ARM. FP is the caller's SP, and the saved FP is at FP[-2] instead of FP[0].

Making Dart frames match the convention of C frames allows stack walkers to continue their traversals through transitions between Dart and C (e.g., for stack dumps or profiling).

TEST=ci
Change-Id: I463348beba70c1a75bfb0d902b3391be524de0fe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/235960
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc
index 897c9d7..7ff6002 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.cc
+++ b/runtime/vm/compiler/assembler/assembler_riscv.cc
@@ -3474,12 +3474,12 @@
   subi(SP, SP, frame_size + 2 * target::kWordSize);
   sx(RA, Address(SP, frame_size + 1 * target::kWordSize));
   sx(FP, Address(SP, frame_size + 0 * target::kWordSize));
-  addi(FP, SP, frame_size + 0 * target::kWordSize);
+  addi(FP, SP, frame_size + 2 * target::kWordSize);
 }
 void Assembler::LeaveFrame() {
   // N.B. The ordering here is important. We must never read beyond SP or
   // it may have already been clobbered by a signal handler.
-  mv(SP, FP);
+  subi(SP, FP, 2 * target::kWordSize);
   lx(FP, Address(SP, 0 * target::kWordSize));
   lx(RA, Address(SP, 1 * target::kWordSize));
   addi(SP, SP, 2 * target::kWordSize);
@@ -3666,7 +3666,7 @@
     subi(SP, SP, frame_size + 2 * target::kWordSize);
     sx(RA, Address(SP, frame_size + 1 * target::kWordSize));
     sx(FP, Address(SP, frame_size + 0 * target::kWordSize));
-    addi(FP, SP, frame_size + 0 * target::kWordSize);
+    addi(FP, SP, frame_size + 2 * target::kWordSize);
   } else {
     subi(SP, SP, frame_size + 4 * target::kWordSize);
     sx(RA, Address(SP, frame_size + 3 * target::kWordSize));
@@ -3674,7 +3674,7 @@
     sx(CODE_REG, Address(SP, frame_size + 1 * target::kWordSize));
     addi(PP, PP, kHeapObjectTag);
     sx(PP, Address(SP, frame_size + 0 * target::kWordSize));
-    addi(FP, SP, frame_size + 2 * target::kWordSize);
+    addi(FP, SP, frame_size + 4 * target::kWordSize);
     if (new_pp == kNoRegister) {
       LoadPoolPointer();
     } else {
@@ -3711,7 +3711,7 @@
     }
   }
   set_constant_pool_allowed(false);
-  mv(SP, FP);
+  subi(SP, FP, 2 * target::kWordSize);
   lx(FP, Address(SP, 0 * target::kWordSize));
   lx(RA, Address(SP, 1 * target::kWordSize));
   addi(SP, SP, 2 * target::kWordSize);
@@ -3730,13 +3730,13 @@
   subi(SP, SP, frame_space + 2 * target::kWordSize);
   sx(RA, Address(SP, frame_space + 1 * target::kWordSize));
   sx(FP, Address(SP, frame_space + 0 * target::kWordSize));
-  addi(FP, SP, frame_space);
+  addi(FP, SP, frame_space + 2 * target::kWordSize);
 }
 
 void Assembler::LeaveCFrame() {
   // N.B. The ordering here is important. We must never read beyond SP or
   // it may have already been clobbered by a signal handler.
-  mv(SP, FP);
+  subi(SP, FP, 2 * target::kWordSize);
   lx(FP, Address(SP, 0 * target::kWordSize));
   lx(RA, Address(SP, 1 * target::kWordSize));
   addi(SP, SP, 2 * target::kWordSize);
@@ -4179,7 +4179,7 @@
     subi(SP, SP, 2 * target::kWordSize + frame_size);
     sx(RA, Address(SP, 1 * target::kWordSize + frame_size));
     sx(FP, Address(SP, 0 * target::kWordSize + frame_size));
-    addi(FP, SP, 0 * target::kWordSize + frame_size);
+    addi(FP, SP, 2 * target::kWordSize + frame_size);
   } else {
     subi(SP, SP, 4 * target::kWordSize + frame_size);
     sx(RA, Address(SP, 3 * target::kWordSize + frame_size));
@@ -4187,7 +4187,7 @@
     sx(CODE_REG, Address(SP, 1 * target::kWordSize + frame_size));
     addi(PP, PP, kHeapObjectTag);
     sx(PP, Address(SP, 0 * target::kWordSize + frame_size));
-    addi(FP, SP, 2 * target::kWordSize + frame_size);
+    addi(FP, SP, 4 * target::kWordSize + frame_size);
   }
 
   PushRegisters(kRuntimeCallSavedRegisters);
@@ -4201,8 +4201,7 @@
   const intptr_t kPushedRegistersSize =
       kRuntimeCallSavedRegisters.CpuRegisterCount() * target::kWordSize +
       kRuntimeCallSavedRegisters.FpuRegisterCount() * kFpuRegisterSize +
-      (target::frame_layout.dart_fixed_frame_size - 2) *
-          target::kWordSize;  // From EnterStubFrame (excluding PC / FP)
+      (target::frame_layout.dart_fixed_frame_size * target::kWordSize);
 
   subi(SP, FP, kPushedRegistersSize);
 
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 256e184..5a742d0 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -4400,15 +4400,15 @@
 }
 
 void NativeParameterInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // The native entry frame has size -kExitLinkSlotFromFp. In order to access
-  // the top of stack from above the entry frame, we add a constant to account
-  // for the two frame pointers and two return addresses of the entry frame.
-  constexpr intptr_t kEntryFramePadding = 4;
-  compiler::ffi::FrameRebase rebase(
-      compiler->zone(),
-      /*old_base=*/SPREG, /*new_base=*/FPREG,
-      (-kExitLinkSlotFromEntryFp + kEntryFramePadding) *
-          compiler::target::kWordSize);
+  // There are two frames between SaveArguments and the NativeParameterInstr
+  // moves.
+  constexpr intptr_t delta =
+      kCallerSpSlotFromFp          // second frame FP to exit link slot
+      + -kExitLinkSlotFromEntryFp  // exit link slot to first frame FP
+      + kCallerSpSlotFromFp;       // first frame FP to argument save SP
+  compiler::ffi::FrameRebase rebase(compiler->zone(),
+                                    /*old_base=*/SPREG, /*new_base=*/FPREG,
+                                    delta * compiler::target::kWordSize);
   const auto& location =
       marshaller_.NativeLocationOfNativeParameter(def_index_);
   const auto& src =
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 2d14822..637569c 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -1590,7 +1590,8 @@
   // Save the argument registers, in reverse order.
   SaveArguments(compiler);
 
-  // Enter the entry frame.
+  // Enter the entry frame. NativeParameterInstr expects this frame has size
+  // -exit_link_slot_from_entry_fp, verified below.
   SPILLS_LR_TO_FRAME(__ EnterFrame((1 << FP) | (1 << LR), 0));
 
   // Save a space for the code object.
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 33b02c1..db2aa45 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1431,7 +1431,8 @@
   // Save the argument registers, in reverse order.
   SaveArguments(compiler);
 
-  // Enter the entry frame.
+  // Enter the entry frame. NativeParameterInstr expects this frame has size
+  // -exit_link_slot_from_entry_fp, verified below.
   __ EnterFrame(0);
 
   // Save a space for the code object.
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 99b20f7..b410840 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -1140,7 +1140,8 @@
 void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   __ Bind(compiler->GetJumpLabel(this));
 
-  // Enter the entry frame.
+  // Enter the entry frame. NativeParameterInstr expects this frame has size
+  // -exit_link_slot_from_entry_fp, verified below.
   __ EnterFrame(0);
 
   // Save a space for the code object.
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index 51aa2b4..d4e8b39 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -1585,7 +1585,8 @@
   // Save the argument registers, in reverse order.
   SaveArguments(compiler);
 
-  // Enter the entry frame.
+  // Enter the entry frame. NativeParameterInstr expects this frame has size
+  // -exit_link_slot_from_entry_fp, verified below.
   __ EnterFrame(0);
 
   // Save a space for the code object.
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index de70052..d5ca6ba 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1322,7 +1322,8 @@
   SaveArguments(compiler);
 
   // Enter the entry frame. Push a dummy return address for consistency with
-  // EnterFrame on ARM(64).
+  // EnterFrame on ARM(64). NativeParameterInstr expects this frame has size
+  // -exit_link_slot_from_entry_fp, verified below.
   __ PushImmediate(compiler::Immediate(0));
   __ EnterFrame(0);
 
diff --git a/runtime/vm/compiler/stub_code_compiler.h b/runtime/vm/compiler/stub_code_compiler.h
index 6c4b3ae..b1ee80e 100644
--- a/runtime/vm/compiler/stub_code_compiler.h
+++ b/runtime/vm/compiler/stub_code_compiler.h
@@ -135,11 +135,11 @@
   static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
 #elif defined(TARGET_ARCH_RISCV32)
   static constexpr intptr_t kNativeCallbackTrampolineSize = 8;
-  static constexpr intptr_t kNativeCallbackSharedStubSize = 192;
+  static constexpr intptr_t kNativeCallbackSharedStubSize = 198;
   static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
 #elif defined(TARGET_ARCH_RISCV64)
   static constexpr intptr_t kNativeCallbackTrampolineSize = 8;
-  static constexpr intptr_t kNativeCallbackSharedStubSize = 196;
+  static constexpr intptr_t kNativeCallbackSharedStubSize = 202;
   static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
 #else
 #error What architecture?
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 8c3ce77..2bb3db2 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -584,7 +584,9 @@
 
   // Set retval in NativeArgs.
   ASSERT(retval_offset == 3 * target::kWordSize);
-  __ add(R3, FP, Operand(2 * target::kWordSize));
+  __ add(R3, FP,
+         Operand((target::frame_layout.param_end_from_fp + 1) *
+                 target::kWordSize));
 
   // Passing the structure by value as in runtime calls would require changing
   // Dart API for native functions.
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index fe3b109..13a3f8f 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -700,7 +700,8 @@
 
   // Set retval in NativeArgs.
   ASSERT(retval_offset == 3 * target::kWordSize);
-  __ AddImmediate(R3, FP, 2 * target::kWordSize);
+  __ AddImmediate(
+      R3, FP, (target::frame_layout.param_end_from_fp + 1) * target::kWordSize);
 
   // Passing the structure by value as in runtime calls would require changing
   // Dart API for native functions.
@@ -1411,6 +1412,9 @@
   ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -23);
 #endif
   __ Push(R6);
+  // In debug mode, verify that we've pushed the top exit frame info at the
+  // correct offset from FP.
+  __ EmitEntryFrameVerification();
 
   // Mark that the thread is executing Dart code. Do this after initializing the
   // exit link for the profiler.
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 52f6ed0..352036d 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -436,16 +436,21 @@
   }
 
   // Pass NativeArguments structure by value and call native function.
-  __ movl(Address(ESP, thread_offset), THR);    // Set thread in NativeArgs.
-  __ movl(Address(ESP, argc_tag_offset), EDX);  // Set argc in NativeArguments.
-  __ movl(Address(ESP, argv_offset), EAX);      // Set argv in NativeArguments.
-  __ leal(EAX,
-          Address(EBP, 2 * target::kWordSize));  // Compute return value addr.
-  __ movl(Address(ESP, retval_offset), EAX);  // Set retval in NativeArguments.
-  __ leal(
-      EAX,
-      Address(ESP, 2 * target::kWordSize));  // Pointer to the NativeArguments.
-  __ movl(Address(ESP, 0), EAX);  // Pass the pointer to the NativeArguments.
+  // Set thread in NativeArgs.
+  __ movl(Address(ESP, thread_offset), THR);
+  // Set argc in NativeArguments.
+  __ movl(Address(ESP, argc_tag_offset), EDX);
+  // Set argv in NativeArguments.
+  __ movl(Address(ESP, argv_offset), EAX);
+  // Compute return value addr.
+  __ leal(EAX, Address(EBP, (target::frame_layout.param_end_from_fp + 1) *
+                                target::kWordSize));
+  // Set retval in NativeArguments.
+  __ movl(Address(ESP, retval_offset), EAX);
+  // Pointer to the NativeArguments.
+  __ leal(EAX, Address(ESP, 2 * target::kWordSize));
+  // Pass the pointer to the NativeArguments.
+  __ movl(Address(ESP, 0), EAX);
 
   __ movl(Address(ESP, target::kWordSize), ECX);  // Function to call.
   __ call(wrapper_address);
diff --git a/runtime/vm/compiler/stub_code_compiler_riscv.cc b/runtime/vm/compiler/stub_code_compiler_riscv.cc
index 8283688..934a2b4 100644
--- a/runtime/vm/compiler/stub_code_compiler_riscv.cc
+++ b/runtime/vm/compiler/stub_code_compiler_riscv.cc
@@ -322,11 +322,11 @@
          kNativeCallbackTrampolineSize *
              NativeCallbackTrampolines::NumCallbackTrampolinesPerPage());
 
+  const intptr_t shared_stub_start = __ CodeSize();
+
   __ Bind(&loaded_callback_id_hi);
   __ srai(T1, T1, 12);
 
-  const intptr_t shared_stub_start = __ CodeSize();
-
   // Save THR (callee-saved) and RA. Keeps stack aligned.
   COMPILE_ASSERT(StubCodeCompiler::kNativeCallbackTrampolineStackDelta == 2);
   __ PushRegisterPair(RA, THR);
@@ -630,7 +630,8 @@
   // Set argv in target::NativeArguments: R2 already contains argv.
   // Set retval in NativeArgs.
   ASSERT(retval_offset == 3 * target::kWordSize);
-  __ AddImmediate(T3, FP, 2 * target::kWordSize);
+  __ AddImmediate(
+      T3, FP, (target::frame_layout.param_end_from_fp + 1) * target::kWordSize);
 
   // Passing the structure by value as in runtime calls would require changing
   // Dart API for native functions.
@@ -843,11 +844,11 @@
 //   +------------------+
 //   | PC marker        | <- TOS
 //   +------------------+
-//   | Saved FP         | <- FP of stub
+//   | Saved FP         |
 //   +------------------+
 //   | return-address   |  (deoptimization point)
 //   +------------------+
-//   | Saved CODE_REG   |
+//   | Saved CODE_REG   | <- FP of stub
 //   +------------------+
 //   | ...              | <- SP of optimized frame
 //
@@ -880,7 +881,7 @@
       // Save the original value of CODE_REG pushed before invoking this stub
       // instead of the value used to call this stub.
       COMPILE_ASSERT(TMP > CODE_REG);  // TMP saved first
-      __ lx(TMP, Address(FP, 2 * target::kWordSize));
+      __ lx(TMP, Address(FP, 0 * target::kWordSize));
       __ sx(TMP, Address(SP, i * target::kWordSize));
     } else {
       __ sx(r, Address(SP, i * target::kWordSize));
@@ -1318,10 +1319,13 @@
   // target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
   // with the code below.
 #if XLEN == 32
-  ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -40);
+  ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -42);
 #elif XLEN == 64
-  ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -28);
+  ASSERT_EQUAL(target::frame_layout.exit_link_slot_from_entry_fp, -30);
 #endif
+  // In debug mode, verify that we've pushed the top exit frame info at the
+  // correct offset from FP.
+  __ EmitEntryFrameVerification();
 
   // Mark that the thread is executing Dart code. Do this after initializing the
   // exit link for the profiler.
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 0268696..7ae8fbc 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -729,15 +729,17 @@
     }
 
     // Pass target::NativeArguments structure by value and call native function.
-    __ movq(Address(RSP, thread_offset), THR);  // Set thread in NativeArgs.
-    __ movq(Address(RSP, argc_tag_offset),
-            R10);  // Set argc in target::NativeArguments.
-    __ movq(Address(RSP, argv_offset),
-            R13);  // Set argv in target::NativeArguments.
-    __ leaq(RAX,
-            Address(RBP, 2 * target::kWordSize));  // Compute return value addr.
-    __ movq(Address(RSP, retval_offset),
-            RAX);  // Set retval in target::NativeArguments.
+    // Set thread in NativeArgs.
+    __ movq(Address(RSP, thread_offset), THR);
+    // Set argc in target::NativeArguments.
+    __ movq(Address(RSP, argc_tag_offset), R10);
+    // Set argv in target::NativeArguments.
+    __ movq(Address(RSP, argv_offset), R13);
+    // Compute return value addr.
+    __ leaq(RAX, Address(RBP, (target::frame_layout.param_end_from_fp + 1) *
+                                  target::kWordSize));
+    // Set retval in target::NativeArguments.
+    __ movq(Address(RSP, retval_offset), RAX);
 
     // Pass the pointer to the target::NativeArguments.
     __ movq(CallingConventions::kArg1Reg, RSP);
diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc
index 0a63ce4..76ee165c 100644
--- a/runtime/vm/deopt_instructions.cc
+++ b/runtime/vm/deopt_instructions.cc
@@ -201,7 +201,7 @@
 
 intptr_t DeoptContext::GetSourceFp() const {
   return source_frame_[source_frame_size_ - 1 - num_args_ -
-                       kParamEndSlotFromFp];
+                       kParamEndSlotFromFp + kSavedCallerFpSlotFromFp];
 }
 
 intptr_t DeoptContext::GetSourcePp() const {
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 55eebc9..0297144 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -722,7 +722,7 @@
   for (intptr_t index = index_; index < deopt_instructions_.length(); index++) {
     DeoptInstr* deopt_instr = deopt_instructions_[index];
     if (deopt_instr->kind() == DeoptInstr::kCallerFp) {
-      return index - num_materializations_;
+      return index - num_materializations_ - kSavedCallerFpSlotFromFp;
     }
   }
   UNREACHABLE();
diff --git a/runtime/vm/stack_frame_riscv.h b/runtime/vm/stack_frame_riscv.h
index 4bf58ab..58b31a6 100644
--- a/runtime/vm/stack_frame_riscv.h
+++ b/runtime/vm/stack_frame_riscv.h
@@ -23,10 +23,10 @@
                | first local       T|
                | caller's PP       T|
                | code object       T|    (current frame's code object)
-               | caller's FP        | <- FP of current frame
+               | caller's FP        |
                | caller's RA        |    (PC of caller frame)
                +--------------------+
-Caller frame   | last parameter     | <- SP of caller frame
+Caller frame   | last parameter     | <- SP of caller frame, FP of current frame
                |  ...               |
 
                T against a slot indicates it needs to be traversed during GC.
@@ -35,24 +35,24 @@
 static const int kDartFrameFixedSize = 4;  // PP, FP, RA, PC marker.
 static const int kSavedPcSlotFromSp = -1;
 
-static const int kFirstObjectSlotFromFp = -1;  // Used by GC to traverse stack.
-static const int kLastFixedObjectSlotFromFp = -2;
+static const int kFirstObjectSlotFromFp = -3;  // Used by GC to traverse stack.
+static const int kLastFixedObjectSlotFromFp = -4;
 
-static const int kFirstLocalSlotFromFp = -3;
-static const int kSavedCallerPpSlotFromFp = -2;
-static const int kPcMarkerSlotFromFp = -1;
-static const int kSavedCallerFpSlotFromFp = 0;
-static const int kSavedCallerPcSlotFromFp = 1;
+static const int kFirstLocalSlotFromFp = -5;
+static const int kSavedCallerPpSlotFromFp = -4;
+static const int kPcMarkerSlotFromFp = -3;
+static const int kSavedCallerFpSlotFromFp = -2;
+static const int kSavedCallerPcSlotFromFp = -1;
 
-static const int kParamEndSlotFromFp = 1;  // One slot past last parameter.
-static const int kCallerSpSlotFromFp = 2;
+static const int kParamEndSlotFromFp = -1;  // One slot past last parameter.
+static const int kCallerSpSlotFromFp = 0;
 static const int kLastParamSlotFromEntrySp = 0;
 
 // Entry and exit frame layout.
 #if defined(TARGET_ARCH_RISCV64)
-static const int kExitLinkSlotFromEntryFp = -28;
+static const int kExitLinkSlotFromEntryFp = -30;
 #elif defined(TARGET_ARCH_RISCV32)
-static const int kExitLinkSlotFromEntryFp = -40;
+static const int kExitLinkSlotFromEntryFp = -42;
 #endif
 COMPILE_ASSERT(kAbiPreservedCpuRegCount == 11);
 COMPILE_ASSERT(kAbiPreservedFpuRegCount == 12);