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