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