1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_RISCV64_H_ 18 #define BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_RISCV64_H_ 19 20 #include "berberis/base/bit_util.h" 21 #include "berberis/base/logging.h" 22 23 namespace berberis::riscv64 { 24 25 enum ArgLocationKind { 26 kArgLocationNone = 0, 27 kArgLocationStack, 28 kArgLocationInt, // x10 - x17 29 kArgLocationFp, // f10 - f17 30 }; 31 32 // The meaning of offset depends on kind. 33 // 34 // Stack argument locations are byte offsets from the stack frame. 35 // Register argument locations are register offsets from the first ABI argument register, which is 36 // x10/a0 for int arguments and f10/fa0 for fp arguments. 37 struct ArgLocation { 38 ArgLocationKind kind; 39 unsigned offset; 40 }; 41 42 class CallingConventions { 43 public: 44 static constexpr unsigned kStackAlignmentBeforeCall = 16; 45 46 CallingConventions() = default; 47 CallingConventions(const CallingConventions&) = default; 48 CallingConventions(CallingConventions&&) = default; 49 static constexpr struct StackOnly { 50 } kStackOnly; CallingConventions(StackOnly)51 CallingConventions(StackOnly) : int_offset_(kMaxIntOffset), fp_offset_(kMaxFpOffset) {} 52 GetNextIntArgLoc(unsigned size,unsigned alignment)53 constexpr ArgLocation GetNextIntArgLoc(unsigned size, unsigned alignment) { 54 // Fundamental integer type - 1/1, 2/2, 4/4, 8/8. 55 CHECK_LE(size, 8u); 56 CHECK_EQ(size, alignment); 57 58 if (int_offset_ < kMaxIntOffset) { 59 // Use 1 int reg. 60 ArgLocation loc{kArgLocationInt, int_offset_}; 61 ++int_offset_; 62 return loc; 63 } 64 65 return GetNextStackArgLoc(size, alignment); 66 } 67 GetNextFpArgLoc(unsigned size,unsigned alignment)68 constexpr ArgLocation GetNextFpArgLoc(unsigned size, unsigned alignment) { 69 // Fundamental floating-point type - 4/4, 8/8. 70 CHECK_LE(size, 8u); 71 CHECK_EQ(size, alignment); 72 73 if (fp_offset_ < kMaxFpOffset) { 74 // Use 1 fp reg. 75 ArgLocation loc{kArgLocationFp, fp_offset_}; 76 ++fp_offset_; 77 return loc; 78 } 79 80 // Once the floating-point registers have been exhausted, pass floating-point parameters 81 // according to the integer calling convention. 82 return GetNextIntArgLoc(size, alignment); 83 } 84 GetIntResLoc(unsigned size)85 constexpr ArgLocation GetIntResLoc(unsigned size) { 86 // Fundamental integer type - 1/1, 2/2, 4/4, 8/8, 16/16. 87 CHECK_LE(size, 16u); 88 89 // Use x10/a0. 90 return {kArgLocationInt, 0u}; 91 } 92 GetFpResLoc(unsigned size)93 constexpr ArgLocation GetFpResLoc(unsigned size) { 94 // Fundamental floating-point type - 4/4, 8/8. 95 CHECK_LE(size, 8u); 96 97 // Use f10/fa0. 98 return {kArgLocationFp, 0u}; 99 } 100 101 private: GetNextStackArgLoc(unsigned size,unsigned alignment)102 constexpr ArgLocation GetNextStackArgLoc(unsigned size, unsigned alignment) { 103 CHECK_LE(size, 16u); 104 CHECK_EQ(size, alignment); 105 106 // Arguments that fit in a pointer word are aligned at 8 bytes. Larger 107 // arguments are naturally aligned (i.e. 16-byte alignment for 16 byte 108 // arguments). 109 unsigned alignment_in_stack = alignment > 8 ? alignment : 8; 110 unsigned size_in_stack = AlignUp(size, alignment_in_stack); 111 112 unsigned aligned_stack_offset = AlignUp(stack_offset_, alignment_in_stack); 113 114 ArgLocation loc{kArgLocationStack, aligned_stack_offset}; 115 stack_offset_ = aligned_stack_offset + size_in_stack; 116 return loc; 117 } 118 119 static constexpr unsigned kMaxIntOffset = 8u; 120 static constexpr unsigned kMaxFpOffset = 8u; 121 122 unsigned int_offset_ = 0; 123 unsigned fp_offset_ = 0; 124 unsigned stack_offset_ = 0; 125 }; 126 127 } // namespace berberis::riscv64 128 129 #endif // BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_RISCV64_H_ 130