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