riscv64: define callee-saved frame layout.

Define which registers are saved/restored by different types of ART
callee-saved frames. This is based on the standard RISC-V calling
convention (https://github.com/riscv-non-isa/riscv-elf-psabi-doc) and
also ART internal convention (Quick ABI) that defines additional
ART-specific meaning of some registers (e.g. the ART thread register).

Test: lunch aosp_riscv64-userdebug && m dist

Co-authored-by: Ulya Trafimovich <skvadrik@google.com>
Change-Id: Ifd2fafcb94e7fac9da6bf41544c47d576bcc9560
diff --git a/runtime/arch/riscv64/asm_support_riscv64.S b/runtime/arch/riscv64/asm_support_riscv64.S
new file mode 100644
index 0000000..6a1f887
--- /dev/null
+++ b/runtime/arch/riscv64/asm_support_riscv64.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_S_
+#define ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_S_
+
+#include "asm_support_riscv64.h"
+
+// Define special registers.
+
+// Register holding Thread::Current().
+#define xSELF s1
+
+#endif  // ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_S_
diff --git a/runtime/arch/riscv64/asm_support_riscv64.h b/runtime/arch/riscv64/asm_support_riscv64.h
new file mode 100644
index 0000000..8406a53
--- /dev/null
+++ b/runtime/arch/riscv64/asm_support_riscv64.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_H_
+#define ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_H_
+
+#include "asm_support.h"
+
+// FS0 - FS11, S0 - S11, RA and ArtMethod*, total 8*(12 + 12 + 1 + 1) = 208
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVES 208
+// FA0 - FA7, A1 - A7, S0, S2 - S11, RA and ArtMethod* total 8*(8 + 7 + 11 + 1 + 1) = 224
+// A0 is excluded as the ArtMethod*, and S1 is excluded as the ART thread register TR.
+#define FRAME_SIZE_SAVE_REFS_AND_ARGS    224
+// All 32 FPRs, 28 GPRs (no SP, Zero, TP, GP), ArtMethod*, padding, total 8*(32 + 28 + 1 + 1) = 496
+#define FRAME_SIZE_SAVE_EVERYTHING       496
+
+#endif  // ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_H_
diff --git a/runtime/arch/riscv64/callee_save_frame_riscv64.h b/runtime/arch/riscv64/callee_save_frame_riscv64.h
new file mode 100644
index 0000000..ac88881
--- /dev/null
+++ b/runtime/arch/riscv64/callee_save_frame_riscv64.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_RISCV64_CALLEE_SAVE_FRAME_RISCV64_H_
+#define ART_RUNTIME_ARCH_RISCV64_CALLEE_SAVE_FRAME_RISCV64_H_
+
+#include "arch/instruction_set.h"
+#include "base/bit_utils.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "quick/quick_method_frame_info.h"
+#include "registers_riscv64.h"
+#include "runtime_globals.h"
+
+namespace art {
+namespace riscv64 {
+
+static constexpr uint32_t kRiscv64CalleeSaveAlwaysSpills =
+    (1 << art::riscv64::RA);  // Return address
+// Callee-saved registers except for SP and S1 (SP is callee-saved according to RISC-V spec, but
+// it cannot contain object reference, and S1(TR) is excluded as the ART thread register).
+static constexpr uint32_t kRiscv64CalleeSaveRefSpills =
+    (1 << art::riscv64::S0) | (1 << art::riscv64::S2) | (1 << art::riscv64::S3) |
+    (1 << art::riscv64::S4) | (1 << art::riscv64::S5) | (1 << art::riscv64::S6) |
+    (1 << art::riscv64::S7) | (1 << art::riscv64::S8) | (1 << art::riscv64::S9) |
+    (1 << art::riscv64::S10) | (1 << art::riscv64::S11);
+// S1(TR) is included because exception handler checks that the saved TR holds Thread::Current().
+// Stack pointer SP is excluded (although it is callee-saved by calling convention) because it is
+// restored by the code logic and not from a stack frame.
+static constexpr uint32_t kRiscv64CalleeSaveAllSpills = (1 << art::riscv64::S1);
+// Argument registers except X10/A0 (which contains method pointer).
+static constexpr uint32_t kRiscv64CalleeSaveArgSpills =
+    (1 << art::riscv64::A1) | (1 << art::riscv64::A2) | (1 << art::riscv64::A3) |
+    (1 << art::riscv64::A4) | (1 << art::riscv64::A5) | (1 << art::riscv64::A6) |
+    (1 << art::riscv64::A7);
+// All registers except SP, immutable Zero, unallocatable thread pointer TP and global pointer GP.
+static constexpr uint32_t kRiscv64CalleeSaveEverythingSpills =
+    (1 << art::riscv64::TR) | (1 << art::riscv64::T0) | (1 << art::riscv64::T1) |
+    (1 << art::riscv64::T2) | (1 << art::riscv64::T3) | (1 << art::riscv64::T4) |
+    (1 << art::riscv64::T5) | (1 << art::riscv64::T6) | (1 << art::riscv64::A0) |
+    (1 << art::riscv64::A1) | (1 << art::riscv64::A2) | (1 << art::riscv64::A3) |
+    (1 << art::riscv64::A4) | (1 << art::riscv64::A5) | (1 << art::riscv64::A6) |
+    (1 << art::riscv64::A7);
+
+// No references in floating-point registers.
+static constexpr uint32_t kRiscv64CalleeSaveFpSpills = 0;
+// Floating-point argument registers FA0 - FA7.
+static constexpr uint32_t kRiscv64CalleeSaveFpArgSpills =
+    (1 << art::riscv64::FA0) | (1 << art::riscv64::FA1) | (1 << art::riscv64::FA2) |
+    (1 << art::riscv64::FA3) | (1 << art::riscv64::FA4) | (1 << art::riscv64::FA5) |
+    (1 << art::riscv64::FA6) | (1 << art::riscv64::FA7);
+// Floating-point callee-saved registers FS0 - FS11.
+static constexpr uint32_t kRiscv64CalleeSaveFpAllSpills =
+    (1 << art::riscv64::FS0) | (1 << art::riscv64::FS1) | (1 << art::riscv64::FS2) |
+    (1 << art::riscv64::FS3) | (1 << art::riscv64::FS4) | (1 << art::riscv64::FS5) |
+    (1 << art::riscv64::FS6) | (1 << art::riscv64::FS7) | (1 << art::riscv64::FS8) |
+    (1 << art::riscv64::FS9) | (1 << art::riscv64::FS10) | (1 << art::riscv64::FS11);
+// All floating-point registers.
+static constexpr uint32_t kRiscv64CalleeSaveFpEverythingSpills =
+    (1 << art::riscv64::FT0) | (1 << art::riscv64::FT1) | (1 << art::riscv64::FT2) |
+    (1 << art::riscv64::FT3) | (1 << art::riscv64::FT4) | (1 << art::riscv64::FT5) |
+    (1 << art::riscv64::FT6) | (1 << art::riscv64::FT7) | (1 << art::riscv64::FT8) |
+    (1 << art::riscv64::FT9) | (1 << art::riscv64::FT10) | (1 << art::riscv64::FT11) |
+    (1 << art::riscv64::FS0) | (1 << art::riscv64::FS1) | (1 << art::riscv64::FS2) |
+    (1 << art::riscv64::FS3) | (1 << art::riscv64::FS4) | (1 << art::riscv64::FS5) |
+    (1 << art::riscv64::FS6) | (1 << art::riscv64::FS7) | (1 << art::riscv64::FS8) |
+    (1 << art::riscv64::FS9) | (1 << art::riscv64::FS10) | (1 << art::riscv64::FS11) |
+    (1 << art::riscv64::FA0) | (1 << art::riscv64::FA1) | (1 << art::riscv64::FA2) |
+    (1 << art::riscv64::FA3) | (1 << art::riscv64::FA4) | (1 << art::riscv64::FA5) |
+    (1 << art::riscv64::FA6) | (1 << art::riscv64::FA7);
+
+class Riscv64CalleeSaveFrame {
+ public:
+  static constexpr uint32_t GetCoreSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kRiscv64CalleeSaveAlwaysSpills | kRiscv64CalleeSaveRefSpills |
+           (type == CalleeSaveType::kSaveRefsAndArgs ? kRiscv64CalleeSaveArgSpills : 0) |
+           (type == CalleeSaveType::kSaveAllCalleeSaves ? kRiscv64CalleeSaveAllSpills : 0) |
+           (type == CalleeSaveType::kSaveEverything ? kRiscv64CalleeSaveEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFpSpills(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return kRiscv64CalleeSaveFpSpills |
+           (type == CalleeSaveType::kSaveRefsAndArgs ? kRiscv64CalleeSaveFpArgSpills : 0) |
+           (type == CalleeSaveType::kSaveAllCalleeSaves ? kRiscv64CalleeSaveFpAllSpills : 0) |
+           (type == CalleeSaveType::kSaveEverything ? kRiscv64CalleeSaveFpEverythingSpills : 0);
+  }
+
+  static constexpr uint32_t GetFrameSize(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ +
+                    POPCOUNT(GetFpSpills(type)) /* fprs */ + 1 /* Method* */) *
+                       static_cast<size_t>(kRiscv64PointerSize),
+                   kStackAlignment);
+  }
+
+  static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type));
+  }
+
+  static constexpr size_t GetFpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - (POPCOUNT(GetCoreSpills(type)) + POPCOUNT(GetFpSpills(type))) *
+                                    static_cast<size_t>(kRiscv64PointerSize);
+  }
+
+  static constexpr size_t GetGpr1Offset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) -
+           POPCOUNT(GetCoreSpills(type)) * static_cast<size_t>(kRiscv64PointerSize);
+  }
+
+  static constexpr size_t GetReturnPcOffset(CalleeSaveType type) {
+    type = GetCanonicalCalleeSaveType(type);
+    return GetFrameSize(type) - static_cast<size_t>(kRiscv64PointerSize);
+  }
+};
+
+// Assembly entrypoints rely on these constants.
+static_assert(Riscv64CalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs) == 224);
+static_assert(Riscv64CalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveAllCalleeSaves) == 208);
+static_assert(Riscv64CalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveEverything) == 496);
+
+}  // namespace riscv64
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_RISCV64_CALLEE_SAVE_FRAME_RISCV64_H_
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index 1baccee..7d9b844 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -28,6 +28,7 @@
 // specialize the code.
 #include "arch/arm/callee_save_frame_arm.h"
 #include "arch/arm64/callee_save_frame_arm64.h"
+#include "arch/riscv64/callee_save_frame_riscv64.h"
 #include "arch/x86/callee_save_frame_x86.h"
 #include "arch/x86_64/callee_save_frame_x86_64.h"
 
@@ -73,13 +74,25 @@
 
 // Note: kThumb2 is never the kRuntimeISA.
 template <>
-struct CSFSelector<InstructionSet::kArm> { using type = arm::ArmCalleeSaveFrame; };
+struct CSFSelector<InstructionSet::kArm> {
+  using type = arm::ArmCalleeSaveFrame;
+};
 template <>
-struct CSFSelector<InstructionSet::kArm64> { using type = arm64::Arm64CalleeSaveFrame; };
+struct CSFSelector<InstructionSet::kArm64> {
+  using type = arm64::Arm64CalleeSaveFrame;
+};
 template <>
-struct CSFSelector<InstructionSet::kX86> { using type = x86::X86CalleeSaveFrame; };
+struct CSFSelector<InstructionSet::kRiscv64> {
+  using type = riscv64::Riscv64CalleeSaveFrame;
+};
 template <>
-struct CSFSelector<InstructionSet::kX86_64> { using type = x86_64::X86_64CalleeSaveFrame; };
+struct CSFSelector<InstructionSet::kX86> {
+  using type = x86::X86CalleeSaveFrame;
+};
+template <>
+struct CSFSelector<InstructionSet::kX86_64> {
+  using type = x86_64::X86_64CalleeSaveFrame;
+};
 
 }  // namespace detail