1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef COMPILER_OPTIMIZER_CODEGEN_TYPE_INFO_H
17 #define COMPILER_OPTIMIZER_CODEGEN_TYPE_INFO_H
18
19 /*
20 Arch-feature definitions
21 */
22 #include <bitset>
23 #include <cstdint>
24 #include <type_traits>
25
26 #include "utils/arch.h"
27 #include "utils/arena_containers.h"
28 #include "utils/bit_field.h"
29 #include "utils/bit_utils.h"
30 #include "utils/regmask.h"
31 #include "compiler/optimizer/ir/constants.h"
32 #include "compiler/optimizer/ir/datatype.h"
33 #include "utils/type_helpers.h"
34
35 namespace ark::compiler {
36 constexpr uint8_t BYTE_SIZE = 8;
37 constexpr uint8_t HALF_SIZE = 16;
38 constexpr uint8_t WORD_SIZE = 32;
39 constexpr uint8_t DOUBLE_WORD_SIZE = 64;
40 constexpr uint8_t HALF_WORD_SIZE_BYTES = 2;
41 constexpr uint8_t WORD_SIZE_BYTES = 4;
42 constexpr uint8_t DOUBLE_WORD_SIZE_BYTES = 8;
43 constexpr uint8_t QUAD_WORD_SIZE_BYTES = 16;
44 /// Maximum possible registers count (for scalar and for vector):
45 constexpr uint8_t MAX_NUM_REGS = 32;
46 constexpr uint8_t MAX_NUM_VREGS = 32;
47
48 constexpr uint64_t NAN_DOUBLE = uint64_t(0x7ff8000000000000);
49 constexpr uint32_t NAN_FLOAT = uint32_t(0x7fc00000);
50 constexpr uint32_t NAN_FLOAT_BITS = NAN_FLOAT >> 16U;
51
52 // Constants for cast from float to int64:
53 // The number of the bit from which exponential part starts in float
54 constexpr uint8_t START_EXP_FLOAT = 23;
55 // Size exponential part in float
56 constexpr uint8_t SIZE_EXP_FLOAT = 8;
57 // The maximum exponential part of float that can be loaded in int64
58 constexpr uint32_t POSSIBLE_EXP_FLOAT = 0xbe;
59 // Mask say that float number is NaN by IEEE 754
60 constexpr uint32_t UP_BITS_NAN_FLOAT = 0xff;
61
62 // Constants for cast from double to int64:
63 // The number of the bit from which exponential part starts in double
64 constexpr uint8_t START_EXP_DOUBLE = 20;
65 // Size exponential part in double
66 constexpr uint8_t SIZE_EXP_DOUBLE = 11;
67 // The maximum exponential part of double that can be loaded in int64
68 constexpr uint32_t POSSIBLE_EXP_DOUBLE = 0x43e;
69 // Mask say that double number is NaN by IEEE 754
70 constexpr uint32_t UP_BITS_NAN_DOUBLE = 0x7ff;
71
72 constexpr uint32_t SHIFT_BITS_DOUBLE = 12;
73 constexpr uint32_t SHIFT_BITS_FLOAT = 9;
74
75 // Return true, if architecture can be encoded.
76 #ifdef PANDA_WITH_CODEGEN
77 bool BackendSupport(Arch arch);
78 #else
BackendSupport(Arch arch)79 inline bool BackendSupport([[maybe_unused]] Arch arch)
80 {
81 return false;
82 }
83 #endif
84
85 // Arch-independent access types
86
87 /**
88 * Template class for identify types compile-time (nortti - can't use typeid).
89 * Used in register class. Immediate class support conversion to it.
90 */
91 class TypeInfo final {
92 public:
93 enum TypeId : uint8_t { INT8 = 0, INT16 = 1, INT32 = 2, INT64 = 3, FLOAT32 = 4, FLOAT64 = 5, INVALID = 6 };
94
95 /// Template constructor - use template parameter for create object.
96 template <class T>
TypeInfo(T)97 constexpr explicit TypeInfo(T /* unused */)
98 {
99 #ifndef __clang_analyzer__
100 static_assert(std::is_arithmetic_v<T>);
101 if constexpr (std::is_same<T, uint8_t>()) {
102 typeId_ = INT8;
103 } else if constexpr (std::is_same<T, int8_t>()) {
104 typeId_ = INT8;
105 } else if constexpr (std::is_same<T, uint16_t>()) {
106 typeId_ = INT16;
107 } else if constexpr (std::is_same<T, int16_t>()) {
108 typeId_ = INT16;
109 } else if constexpr (std::is_same<T, uint32_t>()) {
110 typeId_ = INT32;
111 } else if constexpr (std::is_same<T, int32_t>()) {
112 typeId_ = INT32;
113 } else if constexpr (std::is_same<T, uint64_t>()) {
114 typeId_ = INT64;
115 } else if constexpr (std::is_same<T, int64_t>()) {
116 typeId_ = INT64;
117 } else if constexpr (std::is_same<T, float>()) {
118 typeId_ = FLOAT32;
119 } else if constexpr (std::is_same<T, double>()) {
120 typeId_ = FLOAT64;
121 } else {
122 typeId_ = INVALID;
123 }
124 #endif
125 }
126
TypeInfo(TypeId type)127 constexpr explicit TypeInfo(TypeId type) : typeId_(type) {}
128
129 DEFAULT_MOVE_SEMANTIC(TypeInfo);
130 DEFAULT_COPY_SEMANTIC(TypeInfo);
131 ~TypeInfo() = default;
132
133 /// Constructor for create invalid TypeInfo
134 constexpr TypeInfo() = default;
135
136 /// Validation check
IsValid()137 constexpr bool IsValid() const
138 {
139 return typeId_ != INVALID;
140 }
141
142 /// Type expected size
GetSize()143 constexpr size_t GetSize() const
144 {
145 ASSERT(IsValid());
146 switch (typeId_) {
147 case INT8:
148 return BYTE_SIZE;
149 case INT16:
150 return HALF_SIZE;
151 case INT32:
152 case FLOAT32:
153 return WORD_SIZE;
154 case INT64:
155 case FLOAT64:
156 return DOUBLE_WORD_SIZE;
157 default:
158 return 0;
159 }
160 return 0;
161 }
162
IsFloat()163 constexpr bool IsFloat() const
164 {
165 ASSERT(IsValid());
166 return typeId_ == FLOAT32 || typeId_ == FLOAT64;
167 }
168
IsScalar()169 constexpr bool IsScalar() const
170 {
171 // VOID - is scalar type here
172 return !IsFloat();
173 }
174
175 constexpr bool operator==(const TypeInfo &other) const
176 {
177 return (typeId_ == other.typeId_);
178 }
179
180 constexpr bool operator!=(const TypeInfo &other) const
181 {
182 return !operator==(other);
183 }
184
FromDataType(DataType::Type type,Arch arch)185 static TypeInfo FromDataType(DataType::Type type, Arch arch)
186 {
187 switch (type) {
188 case DataType::BOOL:
189 case DataType::UINT8:
190 case DataType::INT8: {
191 return TypeInfo(INT8);
192 }
193 case DataType::UINT16:
194 case DataType::INT16: {
195 return TypeInfo(INT16);
196 }
197 case DataType::UINT32:
198 case DataType::INT32: {
199 return TypeInfo(INT32);
200 }
201 case DataType::UINT64:
202 case DataType::INT64:
203 case DataType::ANY: {
204 return TypeInfo(INT64);
205 }
206 case DataType::FLOAT32: {
207 return TypeInfo(FLOAT32);
208 }
209 case DataType::FLOAT64: {
210 return TypeInfo(FLOAT64);
211 }
212 case DataType::REFERENCE: {
213 return FromDataType(DataType::GetIntTypeForReference(arch), arch);
214 }
215 case DataType::POINTER: {
216 return Is64BitsArch(arch) ? TypeInfo(INT64) : TypeInfo(INT32);
217 }
218 default:
219 UNREACHABLE();
220 }
221 }
222
ToDataType()223 DataType::Type ToDataType() const
224 {
225 switch (typeId_) {
226 case INT8:
227 return DataType::INT8;
228 case INT16:
229 return DataType::INT16;
230 case INT32:
231 return DataType::INT32;
232 case INT64:
233 return DataType::INT64;
234 case FLOAT32:
235 return DataType::FLOAT32;
236 case FLOAT64:
237 return DataType::FLOAT64;
238 default:
239 UNREACHABLE();
240 }
241 }
242
243 static constexpr TypeInfo GetScalarTypeBySize(size_t sizeBits);
244
Dump()245 void Dump()
246 {
247 std::cerr << "TypeInfo:";
248 switch (typeId_) {
249 case INT8:
250 std::cerr << "INT8";
251 break;
252 case INT16:
253 std::cerr << "INT16";
254 break;
255 case INT32:
256 std::cerr << "INT32";
257 break;
258 case FLOAT32:
259 std::cerr << "FLOAT32";
260 break;
261 case INT64:
262 std::cerr << "INT64";
263 break;
264 case FLOAT64:
265 std::cerr << "FLOAT64";
266 break;
267 default:
268 std::cerr << "INVALID";
269 break;
270 }
271 std::cerr << ", size = " << GetSize();
272 }
273
274 private:
275 TypeId typeId_ {INVALID};
276 };
277
278 constexpr TypeInfo INT8_TYPE {TypeInfo::INT8};
279 constexpr TypeInfo INT16_TYPE {TypeInfo::INT16};
280 constexpr TypeInfo INT32_TYPE {TypeInfo::INT32};
281 constexpr TypeInfo INT64_TYPE {TypeInfo::INT64};
282 constexpr TypeInfo FLOAT32_TYPE {TypeInfo::FLOAT32};
283 constexpr TypeInfo FLOAT64_TYPE {TypeInfo::FLOAT64};
284 constexpr TypeInfo INVALID_TYPE;
285
GetScalarTypeBySize(size_t sizeBits)286 constexpr TypeInfo TypeInfo::GetScalarTypeBySize(size_t sizeBits)
287 {
288 auto type = INT64_TYPE;
289 if (sizeBits == BYTE_SIZE) {
290 type = INT8_TYPE;
291 } else if (sizeBits == HALF_SIZE) {
292 type = INT16_TYPE;
293 } else if (sizeBits == WORD_SIZE) {
294 type = INT32_TYPE;
295 }
296 return type;
297 }
298
299 // Condition also used for tell comparison registers type
300 enum Condition {
301 EQ, // equal to 0
302 NE, // not equal to 0
303 // signed
304 LT, // less
305 LE, // less than or equal
306 GT, // greater
307 GE, // greater than or equal
308 // unsigned - checked from registers
309 LO, // less
310 LS, // less than or equal
311 HI, // greater
312 HS, // greater than or equal
313 // Special arch-dependecy NOTE (igorban) Fix them
314 MI, // N set Negative
315 PL, // N clear Positive or zero
316 VS, // V set Overflow.
317 VC, // V clear No overflow.
318 AL, // Always.
319 NV, // Behaves as always/al.
320
321 TST_EQ,
322 TST_NE,
323
324 INVALID_COND
325 };
326
IsTestCc(Condition cond)327 inline bool IsTestCc(Condition cond)
328 {
329 return cond == TST_EQ || cond == TST_NE;
330 }
331
332 } // namespace ark::compiler
333 #endif // COMPILER_OPTIMIZER_CODEGEN_TYPE_INFO_H
334