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_ABI_ARCH_H_
18 #define BERBERIS_GUEST_ABI_GUEST_ABI_ARCH_H_
19 
20 #include <array>
21 
22 #include "berberis/guest_abi/guest_type.h"
23 
24 namespace berberis {
25 
26 class GuestAbi {
27  public:
28   enum CallingConventionsVariant { kAapcs, kAapcsVfp, kDefaultAbi = kAapcs };
29 
30  protected:
31   enum class ArgumentClass { kInteger, kVFP, kReturnedViaIndirectPointer };
32 
33   template <typename Type, CallingConventionsVariant = kDefaultAbi, typename = void>
34   struct GuestArgumentInfo;
35 
36   template <typename IntegerType, CallingConventionsVariant kCallingConventionsVarіant>
37   struct GuestArgumentInfo<
38       IntegerType,
39       kCallingConventionsVarіant,
40       std::enable_if_t<std::is_integral_v<IntegerType> && std::is_signed_v<IntegerType> &&
41                        sizeof(IntegerType) < 4>> {
42     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
43     constexpr static unsigned kSize = sizeof(IntegerType);
44     // Use sizeof, not alignof for kAlignment because all integer types are naturally aligned on
45     // ARM, which is not guaranteed to be true for host.
46     constexpr static unsigned kAlignment = sizeof(IntegerType);
47     using GuestType = GuestType<int32_t>;
48     using HostType = int32_t;
49   };
50 
51   template <typename IntegerType, CallingConventionsVariant kCallingConventionsVarіant>
52   struct GuestArgumentInfo<
53       IntegerType,
54       kCallingConventionsVarіant,
55       std::enable_if_t<std::is_integral_v<IntegerType> && !std::is_signed_v<IntegerType> &&
56                        sizeof(IntegerType) < 4>> {
57     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
58     constexpr static unsigned kSize = sizeof(IntegerType);
59     // Use sizeof, not alignof for kAlignment because all integer types are naturally aligned on
60     // ARM, which is not guaranteed to be true for host.
61     constexpr static unsigned kAlignment = sizeof(IntegerType);
62     using GuestType = GuestType<uint32_t>;
63     using HostType = uint32_t;
64   };
65 
66   template <typename IntegerType, CallingConventionsVariant kCallingConventionsVarіant>
67   struct GuestArgumentInfo<
68       IntegerType,
69       kCallingConventionsVarіant,
70       std::enable_if_t<std::is_integral_v<IntegerType> && sizeof(IntegerType) >= 4>> {
71     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
72     constexpr static unsigned kSize = sizeof(IntegerType);
73     // Use sizeof, not alignof for kAlignment because all integer types are naturally aligned on
74     // ARM, which is not guaranteed to be true for host.
75     constexpr static unsigned kAlignment = sizeof(IntegerType);
76     using GuestType = GuestType<IntegerType>;
77     using HostType = IntegerType;
78   };
79 
80   template <typename EnumType, CallingConventionsVariant kCallingConventionsVarіant>
81   struct GuestArgumentInfo<EnumType,
82                            kCallingConventionsVarіant,
83                            std::enable_if_t<std::is_enum_v<EnumType>>>
84       : GuestArgumentInfo<std::underlying_type_t<EnumType>, kCallingConventionsVarіant> {
85     using GuestType = GuestType<EnumType>;
86     using HostType = EnumType;
87   };
88 
89   template <typename PointeeType, CallingConventionsVariant kCallingConventionsVarіant>
90   struct GuestArgumentInfo<PointeeType*, kCallingConventionsVarіant> {
91     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
92     constexpr static unsigned kSize = 4;
93     constexpr static unsigned kAlignment = 4;
94     using GuestType = GuestType<PointeeType*>;
95     using HostType = PointeeType*;
96   };
97 
98   template <typename ResultType,
99             typename... ArgumentType,
100             CallingConventionsVariant kCallingConventionsVarіant>
101   struct GuestArgumentInfo<ResultType (*)(ArgumentType...), kCallingConventionsVarіant> {
102     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
103     constexpr static unsigned kSize = 4;
104     constexpr static unsigned kAlignment = 4;
105     using GuestType = GuestType<ResultType (*)(ArgumentType...)>;
106     using HostType = ResultType (*)(ArgumentType...);
107   };
108 
109   template <>
110   struct GuestArgumentInfo<float, CallingConventionsVariant::kAapcs> {
111     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
112     constexpr static unsigned kSize = 4;
113     constexpr static unsigned kAlignment = 4;
114     // using GuestType = intrinsics::Float32;
115     using GuestType = GuestType<float>;
116     // using HostType = intrinsics::Float32;
117     using HostType = float;
118   };
119 
120   template <>
121   struct GuestArgumentInfo<float, CallingConventionsVariant::kAapcsVfp>
122       : GuestArgumentInfo<float, CallingConventionsVariant::kAapcs> {
123     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kVFP;
124   };
125 
126   template <>
127   struct GuestArgumentInfo<double, CallingConventionsVariant::kAapcs> {
128     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kInteger;
129     constexpr static unsigned kSize = 8;
130     constexpr static unsigned kAlignment = 8;
131     constexpr static bool fp_argument = false;
132     // using GuestType = intrinsics::Float64;
133     using GuestType = GuestType<double>;
134     // using HostType = intrinsics::Float64;
135     using HostType = double;
136   };
137 
138   template <>
139   struct GuestArgumentInfo<double, CallingConventionsVariant::kAapcsVfp>
140       : GuestArgumentInfo<double, CallingConventionsVariant::kAapcs> {
141     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kVFP;
142   };
143 
144   template <typename LargeStructType, CallingConventionsVariant kCallingConventionsVarіant>
145   struct GuestArgumentInfo<
146       LargeStructType,
147       kCallingConventionsVarіant,
148       std::enable_if_t<std::is_class_v<LargeStructType> && (sizeof(LargeStructType) > 4)>> {
149     // Note: this is a kludge for now.  When large structures are returned from function they are
150     // passed via hidden first argument.  But when they are passed into function rules are quite
151     // complicated — we don't support them yet.
152     //
153     // Attempt to use it as an argument of function would cause compile-time error thus we can be
154     // sure this wouldn't affect us without us knowing it happened.
155     //
156     // Currently this class doesn't provide kSize and kAlignment members which means compilation
157     // error would happen during construction of kArgumentsInfo array in the ArgumentsInfoHelper.
158     //
159     // If, in the future, such members would be added then another error would be detected during
160     // processing of that array since ArgumentsInfoHelper explicitly parses all the possible values
161     // of kArgumentClass which is can process.
162     constexpr static ArgumentClass kArgumentClass = ArgumentClass::kReturnedViaIndirectPointer;
163     using GuestType = GuestType<LargeStructType>;
164     using HostType = LargeStructType;
165   };
166 };
167 
168 }  // namespace berberis
169 
170 #endif  // BERBERIS_GUEST_ABI_GUEST_ABI_ARCH_H_
171