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_ARM64_H_ 18 #define BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_ARM64_H_ 19 20 #include "berberis/base/bit_util.h" 21 #include "berberis/base/logging.h" 22 23 namespace berberis::arm64 { 24 25 enum ArgLocationKind { 26 kArgLocationNone = 0, 27 kArgLocationStack, 28 kArgLocationInt, // x0 - x7 29 kArgLocationSimd, // v0 - v7 30 }; 31 32 struct ArgLocation { 33 ArgLocationKind kind; 34 unsigned offset; // meaning of offset depends on kind! 35 }; 36 37 class CallingConventions { 38 public: 39 static constexpr unsigned kStackAlignmentBeforeCall = 16; 40 41 CallingConventions() = default; 42 CallingConventions(const CallingConventions&) = default; 43 CallingConventions(CallingConventions&&) = default; CallingConventions(unsigned int_offset,unsigned simd_offset)44 CallingConventions(unsigned int_offset, unsigned simd_offset) 45 : int_offset_(int_offset), simd_offset_(simd_offset) {} 46 GetNextIntArgLoc(unsigned size,unsigned alignment)47 constexpr ArgLocation GetNextIntArgLoc(unsigned size, unsigned alignment) { 48 // Fundamental integer or pointer type - 1/1, 2/2, 3/3, 4/4, 8/8, 16/16. 49 CHECK_LE(size, 16u); 50 CHECK_EQ(size, alignment); 51 52 unsigned size_in_regs = size > 8 ? 2 : 1; 53 unsigned alignment_in_regs = size_in_regs; 54 unsigned aligned_int_offset = AlignUp(int_offset_, alignment_in_regs); 55 56 if (aligned_int_offset + size_in_regs <= kMaxIntOffset) { 57 // Use 1 or 2 int regs. 58 ArgLocation loc{kArgLocationInt, aligned_int_offset}; 59 int_offset_ = aligned_int_offset + size_in_regs; 60 return loc; 61 } 62 63 // Ensure no more integer registers are used for arguments. 64 // For size_in_regs == 2, x7 might remain unused. 65 int_offset_ = kMaxIntOffset; 66 67 return GetNextStackArgLoc(size, alignment); 68 } 69 GetNextFpArgLoc(unsigned size,unsigned alignment)70 constexpr ArgLocation GetNextFpArgLoc(unsigned size, unsigned alignment) { 71 // Fundamental floating-point type - 2/2, 4/4, 8/8, 16/16. 72 CHECK_LE(size, 16u); 73 CHECK_EQ(size, alignment); 74 75 if (simd_offset_ < kMaxSimdOffset) { 76 // Use 1 simd reg. 77 ArgLocation loc{kArgLocationSimd, simd_offset_}; 78 ++simd_offset_; 79 return loc; 80 } 81 82 return GetNextStackArgLoc(size, alignment); 83 } 84 GetIntResLoc(unsigned size)85 constexpr ArgLocation GetIntResLoc(unsigned size) { 86 // Fundamental integer or pointer type - 1/1, 2/2, 3/3, 4/4, 8/8, 16/16. 87 CHECK_LE(size, 16u); 88 89 // Use x0. 90 return {kArgLocationInt, 0u}; 91 } 92 GetFpResLoc(unsigned size)93 constexpr ArgLocation GetFpResLoc(unsigned size) { 94 // Fundamental floating-point type - 2/2, 4/4, 8/8, 16/16. 95 CHECK_LE(size, 16u); 96 97 // Use v0. 98 return {kArgLocationSimd, 0u}; 99 } 100 101 private: GetNextStackArgLoc(unsigned size,unsigned alignment)102 constexpr ArgLocation GetNextStackArgLoc(unsigned size, unsigned alignment) { 103 unsigned alignment_in_stack = alignment > 8 ? alignment : 8; 104 unsigned size_in_stack = AlignUp(size, alignment_in_stack); 105 106 unsigned aligned_stack_offset = AlignUp(stack_offset_, alignment_in_stack); 107 108 ArgLocation loc{kArgLocationStack, aligned_stack_offset}; 109 stack_offset_ = aligned_stack_offset + size_in_stack; 110 return loc; 111 } 112 113 static constexpr unsigned kMaxIntOffset = 8u; 114 static constexpr unsigned kMaxSimdOffset = 8u; 115 116 unsigned int_offset_ = 0; 117 unsigned simd_offset_ = 0; 118 unsigned stack_offset_ = 0; 119 }; 120 121 } // namespace berberis::arm64 122 123 #endif // BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_ARM64_H_ 124