1 /* 2 * Copyright (C) 2020 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_GUEST_ABI_GUEST_ARGUMENTS_ARCH_H_ 18 #define BERBERIS_GUEST_ABI_GUEST_ARGUMENTS_ARCH_H_ 19 20 #include <array> 21 22 #include "berberis/base/dependent_false.h" 23 #include "berberis/calling_conventions/calling_conventions_arm64.h" 24 #include "berberis/guest_abi/guest_abi.h" 25 26 namespace berberis { 27 28 struct GuestArgumentBuffer { 29 int argc; // in general registers 30 int resc; // in general registers 31 int simd_argc; // in simd registers 32 int simd_resc; // in simd registers 33 int stack_argc; // in bytes 34 35 // Basically a quote from GuestState. 36 uint64_t argv[8]; 37 __uint128_t simd_argv[8]; 38 uint64_t stack_argv[1]; // VLA 39 }; 40 41 template <typename, GuestAbi::CallingConventionsVariant = GuestAbi::kAapcs64> 42 class GuestArgumentsAndResult; 43 44 // GuestArguments is typesafe wrapper around GuestArgumentBuffer. 45 // Usage looks like this: 46 // GuestArguments<double(int, double, int, double)> args(*buf); 47 // int x = args.Arguments<0>(); 48 // float y = args.Arguments<1>(); 49 // args.Result() = x * y; 50 51 template <typename ResultType, typename... ArgumentType, bool kNoexcept> 52 class GuestArgumentsAndResult<ResultType(ArgumentType...) noexcept(kNoexcept), GuestAbi::kAapcs64> 53 : GuestAbi { 54 public: GuestArgumentsAndResult(GuestArgumentBuffer * buffer)55 GuestArgumentsAndResult(GuestArgumentBuffer* buffer) : buffer_(buffer) {} 56 57 template <size_t index> GuestArgument()58 auto& GuestArgument() const { 59 static_assert(index < sizeof...(ArgumentType)); 60 using Type = std::tuple_element_t<index, std::tuple<ArgumentType...>>; 61 using GuestType = typename GuestArgumentInfo<Type>::GuestType; 62 if constexpr (GuestArgumentInfo<Type>::kArgumentClass == ArgumentClass::kLargeStructType) { 63 return **reinterpret_cast<const GuestType**>(ArgLocationAddress(kArgumentsLocations[index])); 64 } else { 65 return *reinterpret_cast<GuestType*>(ArgLocationAddress(kArgumentsLocations[index])); 66 } 67 } 68 69 template <size_t index> HostArgument()70 auto& HostArgument() const { 71 static_assert(index < sizeof...(ArgumentType)); 72 using Type = std::tuple_element_t<index, std::tuple<ArgumentType...>>; 73 using HostType = typename GuestArgumentInfo<Type>::HostType; 74 if constexpr (GuestArgumentInfo<Type>::kArgumentClass == ArgumentClass::kLargeStructType) { 75 return **reinterpret_cast<const HostType**>(ArgLocationAddress(kArgumentsLocations[index])); 76 } else { 77 return *reinterpret_cast<HostType*>(ArgLocationAddress(kArgumentsLocations[index])); 78 } 79 } 80 GuestResult()81 auto& GuestResult() const { 82 static_assert(!std::is_same_v<ResultType, void>); 83 if constexpr (GuestArgumentInfo<ResultType>::kArgumentClass == 84 ArgumentClass::kLargeStructType) { 85 return **reinterpret_cast<typename GuestArgumentInfo<ResultType>::GuestType**>( 86 ArgLocationAddress(kResultLocation)); 87 } else { 88 return *reinterpret_cast<typename GuestArgumentInfo<ResultType>::GuestType*>( 89 ArgLocationAddress(kResultLocation)); 90 } 91 } 92 HostResult()93 auto& HostResult() const { 94 static_assert(!std::is_same_v<ResultType, void>); 95 if constexpr (GuestArgumentInfo<ResultType>::kArgumentClass == 96 ArgumentClass::kLargeStructType) { 97 return **reinterpret_cast<typename GuestArgumentInfo<ResultType>::HostType**>( 98 ArgLocationAddress(kResultLocation)); 99 } else { 100 return *reinterpret_cast<typename GuestArgumentInfo<ResultType>::HostType*>( 101 ArgLocationAddress(kResultLocation)); 102 } 103 } 104 105 private: 106 constexpr static const std::array<arm64::ArgLocation, sizeof...(ArgumentType)> ArgumentsInfoHelper()107 ArgumentsInfoHelper() { 108 struct { 109 const ArgumentClass kArgumentClass; 110 const unsigned kSize; 111 const unsigned kAlignment; 112 } const kArgumentsInfo[] = {{.kArgumentClass = GuestArgumentInfo<ArgumentType>::kArgumentClass, 113 .kSize = GuestArgumentInfo<ArgumentType>::kSize, 114 .kAlignment = GuestArgumentInfo<ArgumentType>::kAlignment}...}; 115 116 arm64::CallingConventions conv; 117 std::array<arm64::ArgLocation, sizeof...(ArgumentType)> result{}; 118 for (const auto& kArgInfo : kArgumentsInfo) { 119 if (kArgInfo.kArgumentClass == ArgumentClass::kInteger || 120 kArgInfo.kArgumentClass == ArgumentClass::kLargeStructType) { 121 result[&kArgInfo - kArgumentsInfo] = 122 conv.GetNextIntArgLoc(kArgInfo.kSize, kArgInfo.kAlignment); 123 } else if (kArgInfo.kArgumentClass == ArgumentClass::kVFP) { 124 result[&kArgInfo - kArgumentsInfo] = 125 conv.GetNextFpArgLoc(kArgInfo.kSize, kArgInfo.kAlignment); 126 } else { 127 LOG_ALWAYS_FATAL("Unsupported ArgumentClass"); 128 } 129 } 130 131 return result; 132 } 133 ResultInfoHelper()134 constexpr static arm64::ArgLocation ResultInfoHelper() { 135 arm64::CallingConventions conv; 136 using ResultInfo = GuestArgumentInfo<ResultType>; 137 if constexpr (std::is_same_v<ResultType, void>) { 138 return {arm64::kArgLocationNone, 0}; 139 } else if constexpr (ResultInfo::kArgumentClass == ArgumentClass::kInteger) { 140 return conv.GetIntResLoc(ResultInfo::kSize); 141 } else if constexpr (ResultInfo::kArgumentClass == ArgumentClass::kVFP) { 142 return conv.GetFpResLoc(ResultInfo::kSize); 143 } else { 144 static_assert(kDependentTypeFalse<ResultType>, "Unsupported ArgumentClass"); 145 } 146 } 147 ArgLocationAddress(arm64::ArgLocation loc)148 constexpr void* ArgLocationAddress(arm64::ArgLocation loc) const { 149 if (loc.kind == arm64::kArgLocationStack) { 150 return reinterpret_cast<char*>(buffer_->stack_argv) + loc.offset; 151 } else if (loc.kind == arm64::kArgLocationInt) { 152 return buffer_->argv + loc.offset; 153 } else if (loc.kind == arm64::kArgLocationSimd) { 154 return buffer_->simd_argv + loc.offset; 155 } else { 156 CHECK(false); 157 } 158 } 159 160 constexpr static arm64::ArgLocation kResultLocation = ResultInfoHelper(); 161 162 constexpr static std::array<arm64::ArgLocation, sizeof...(ArgumentType)> kArgumentsLocations = 163 ArgumentsInfoHelper(); 164 165 GuestArgumentBuffer* const buffer_; 166 }; 167 168 // Partial specialization for GuestArgumentsAndResult<FunctionToPointer> - it acts the same 169 // as the corresponding GuestArgumentsAndResult<Function>. 170 template <typename ResultType, typename... ArgumentType, bool kNoexcept> 171 class GuestArgumentsAndResult<ResultType (*)(ArgumentType...) noexcept(kNoexcept), 172 GuestAbi::kAapcs64> 173 : public GuestArgumentsAndResult<ResultType(ArgumentType...) noexcept(kNoexcept), 174 GuestAbi::kAapcs64> { 175 public: GuestArgumentsAndResult(GuestArgumentBuffer * buffer)176 GuestArgumentsAndResult(GuestArgumentBuffer* buffer) 177 : GuestArgumentsAndResult<ResultType(ArgumentType...) noexcept(kNoexcept), 178 GuestAbi::kAapcs64>(buffer) {} 179 }; 180 181 } // namespace berberis 182 183 #endif // BERBERIS_GUEST_ABI_GUEST_ARGUMENTS_ARCH_H_ 184