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_X86_64_H_
18 #define BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_X86_64_H_
19 
20 #include "berberis/base/bit_util.h"
21 #include "berberis/base/logging.h"
22 
23 namespace berberis::x86_64 {
24 
25 enum ArgLocationKind {
26   kArgLocationNone = 0,
27   kArgLocationStack,
28   kArgLocationInt,     // rdi, rsi, rdx, rcx, r8, r9
29   kArgLocationIntOut,  // rax, rdx
30   kArgLocationSimd,    // xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7
31   kArgLocationFp,      // st0, st1
32 };
33 
34 struct ArgLocation {
35   ArgLocationKind kind;
36   unsigned offset;  // meaning of offset depends on kind!
37 };
38 
39 class CallingConventions {
40  public:
41   // ATTENTION: if passing __m256 (__m512) on stack, alignment should be 32 (64)!
42   static constexpr unsigned kStackAlignmentBeforeCall = 16;
43 
GetNextIntArgLoc(unsigned size,unsigned alignment)44   constexpr ArgLocation GetNextIntArgLoc(unsigned size, unsigned alignment) {
45     // Fundamental integer type - 1/1, 2/2, 4/4, 8/8, 16/16.
46     CHECK_LE(size, 16u);
47     CHECK_EQ(size, alignment);
48 
49     unsigned size_in_regs = size > 8 ? 2 : 1;
50 
51     if (int_offset_ + size_in_regs <= kMaxIntOffset) {
52       ArgLocation loc{kArgLocationInt, int_offset_};
53       int_offset_ += size_in_regs;
54       return loc;
55     }
56 
57     return GetNextStackArgLoc(size, alignment);
58   }
59 
GetNextFpArgLoc(unsigned size,unsigned alignment)60   constexpr ArgLocation GetNextFpArgLoc(unsigned size, unsigned alignment) {
61     // Fundamental floating-point type - 4/4, 8/8, 16/16.
62     // TODO: Handle 16/16 if used in a public Android API. Is it SSE or FP?
63     CHECK_LE(size, 8u);
64     CHECK_EQ(size, alignment);
65 
66     if (simd_offset_ < kMaxSimdOffset) {
67       // Use next available xmm.
68       ArgLocation loc{kArgLocationSimd, simd_offset_};
69       ++simd_offset_;
70       return loc;
71     }
72 
73     return GetNextStackArgLoc(size, alignment);
74   }
75 
GetIntResLoc(unsigned size)76   constexpr ArgLocation GetIntResLoc(unsigned size) {
77     // Fundamental integer type - 1/1, 2/2, 4/4, 8/8, 16/16.
78     CHECK_LE(size, 16u);
79 
80     return {kArgLocationIntOut, 0u};
81   }
82 
GetFpResLoc(unsigned size)83   constexpr ArgLocation GetFpResLoc(unsigned size) {
84     // Fundamental floating-point type - 4/4, 8/8, 16/16.
85     // TODO: Handle 16/16 if used in a public Android API. Is it SSE or FP?
86     CHECK_LE(size, 8u);
87 
88     // Use xmm0.
89     return {kArgLocationSimd, 0u};
90   }
91 
92  private:
GetNextStackArgLoc(unsigned size,unsigned)93   constexpr ArgLocation GetNextStackArgLoc(unsigned size, unsigned /*alignment*/) {
94     // TODO(b/136170145): even for 16-byte aligned types, clang aligns on 8???
95     // unsigned alignment_in_stack = alignment > 8 ? alignment : 8;
96     unsigned alignment_in_stack = 8;
97     unsigned size_in_stack = AlignUp(size, alignment_in_stack);
98 
99     unsigned aligned_stack_offset = AlignUp(stack_offset_, alignment_in_stack);
100 
101     ArgLocation loc{kArgLocationStack, aligned_stack_offset};
102     stack_offset_ = aligned_stack_offset + size_in_stack;
103     return loc;
104   }
105 
106   static constexpr unsigned kMaxIntOffset = 6u;
107   static constexpr unsigned kMaxSimdOffset = 8u;
108 
109   unsigned int_offset_ = 0;
110   unsigned simd_offset_ = 0;
111   unsigned stack_offset_ = 0;
112 };
113 
114 }  // namespace berberis::x86_64
115 
116 #endif  // BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_X86_64_H_
117