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_ARM_H_ 18 #define BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_ARM_H_ 19 20 #include "berberis/base/bit_util.h" 21 #include "berberis/base/logging.h" 22 23 namespace berberis::arm { 24 25 enum ArgLocationKind { 26 kArgLocationNone = 0, 27 kArgLocationStack, 28 kArgLocationInt, 29 kArgLocationIntAndStack, 30 kArgLocationSimd, 31 }; 32 33 struct ArgLocation { 34 ArgLocationKind kind; 35 unsigned offset; // meaning of offset depends on kind! 36 }; 37 38 class CallingConventions { 39 public: 40 static constexpr unsigned kStackAlignmentBeforeCall = 8; 41 42 CallingConventions() = default; 43 CallingConventions(const CallingConventions&) = default; 44 CallingConventions(CallingConventions&&) = default; 45 // These are used for va_list initialization, where stack may not be aligned. CallingConventions(const CallingConventions & base,unsigned stack)46 CallingConventions(const CallingConventions& base, unsigned stack) 47 : int_byte_offset_(base.int_byte_offset_), 48 simd_mask_(base.simd_mask_), 49 init_stack_offset_(base.init_stack_offset_ + stack), 50 stack_offset_(base.stack_offset_ + stack) {} CallingConventions(unsigned stack)51 CallingConventions(unsigned stack) : init_stack_offset_(stack), stack_offset_(stack) {} 52 static constexpr struct StackOnly { 53 } kStackOnly; CallingConventions(StackOnly,unsigned stack)54 CallingConventions(StackOnly, unsigned stack) 55 : int_byte_offset_(kMaxIntByteOffset), 56 simd_mask_(0), 57 init_stack_offset_(stack), 58 stack_offset_(stack) {} 59 GetNextIntArgLoc(unsigned size,unsigned alignment)60 constexpr ArgLocation GetNextIntArgLoc(unsigned size, unsigned alignment) { 61 unsigned param_alignment = 0; 62 unsigned param_size = 0; 63 64 // Handle under- and over-aligned parameters. 65 if (alignment < 4) { 66 param_alignment = 4; 67 param_size = AlignUp(size, 4); 68 } else if (alignment > 8) { 69 param_alignment = 8; 70 param_size = size; 71 } else { 72 param_alignment = alignment; 73 param_size = size; 74 } 75 76 unsigned param_offset = AlignUp(int_byte_offset_, param_alignment); 77 78 if (param_offset + param_size <= kMaxIntByteOffset) { 79 // Parameter on int register. 80 int_byte_offset_ = param_offset + param_size; 81 82 return {kArgLocationInt, param_offset / 4}; 83 } 84 85 if (param_offset < kMaxIntByteOffset && stack_offset_ == init_stack_offset_) { 86 // Parameter on int register and stack. 87 int_byte_offset_ = kMaxIntByteOffset; 88 stack_offset_ = param_offset + param_size - kMaxIntByteOffset; 89 90 return {kArgLocationIntAndStack, param_offset / 4}; 91 } 92 93 // Parameter on stack. 94 int_byte_offset_ = kMaxIntByteOffset; 95 96 param_offset = AlignUp(stack_offset_, param_alignment); 97 stack_offset_ = param_offset + param_size; 98 99 return {kArgLocationStack, param_offset}; 100 } 101 GetNextFpArgLoc(unsigned size,unsigned alignment)102 constexpr ArgLocation GetNextFpArgLoc(unsigned size, unsigned alignment) { 103 if (simd_mask_) { 104 unsigned param_size_mask = (1u << (size / 4)) - 1; 105 for (unsigned index = 0; index < kMaxSimdOffset; index += alignment / 4) { 106 unsigned param_mask = (param_size_mask << index); 107 if ((simd_mask_ & param_mask) == param_mask) { 108 // Parameter is on simd registers. 109 simd_mask_ &= ~param_mask; 110 111 return {kArgLocationSimd, index}; 112 } 113 } 114 115 // No available simd registers, this and next params are on stack. 116 simd_mask_ = 0; 117 } 118 119 // Parameter is on stack. 120 unsigned param_offset = AlignUp(stack_offset_, alignment); 121 stack_offset_ = param_offset + size; 122 123 return {kArgLocationStack, param_offset}; 124 } 125 GetIntResLoc(unsigned size)126 constexpr ArgLocation GetIntResLoc(unsigned size) { 127 // Fundamental integer or pointer type - 1/1, 2/2, 3/3, 4/4, 8/8, 16/16. 128 CHECK_LE(size, 16u); 129 130 // Use r0. 131 return {kArgLocationInt, 0u}; 132 } 133 GetFpResLoc(unsigned size)134 constexpr ArgLocation GetFpResLoc(unsigned size) { 135 // Fundamental floating-point type - 2/2, 4/4, 8/8, 16/16. 136 CHECK_LE(size, 16u); 137 138 // Use v0. 139 return {kArgLocationSimd, 0u}; 140 } 141 142 private: 143 static constexpr unsigned kMaxIntByteOffset = 16u; 144 static constexpr unsigned kMaxSimdOffset = 16u; 145 146 unsigned int_byte_offset_ = 0; 147 unsigned simd_mask_ = (1u << kMaxSimdOffset) - 1; 148 unsigned init_stack_offset_ = 0; 149 unsigned stack_offset_ = 0; 150 }; 151 152 } // namespace berberis::arm 153 154 #endif // BERBERIS_CALLING_CONVENTIONS_CALLING_CONVENTIONS_ARM_H_ 155