1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2017-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 #ifndef IGC_COMMON_TYPES_H
10 #define IGC_COMMON_TYPES_H
11
12 #include "IGC/common/StringMacros.hpp"
13 #include "3d/common/iStdLib/types.h"
14
15 #include "AdaptorCommon/API/igc.h"
16 #include "common/debug/Debug.hpp"
17
18 #include "IGC/common/LLVMWarningsPush.hpp"
19 #include "llvm/Config/llvm-config.h"
20 #if LLVM_VERSION_MAJOR >= 10
21 #include "llvm/Support/TypeSize.h"
22 #endif
23 #include "IGC/common/LLVMWarningsPop.hpp"
24 #include "Probe/Assertion.h"
25 #include "IGC/common/shaderHash.hpp"
26 #include "EmUtils.h"
27
28 namespace USC
29 {
30 struct ShaderD3D;
31 }
32
33 namespace IGC
34 {
35 enum PrecisionType : uint8_t
36 {
37 PRECISION_UNUSED, U8, U4, U2, S8, S4, S2,
38 BF16, FP16
39 };
40
getPrecisionInBits(PrecisionType P)41 inline uint32_t getPrecisionInBits(PrecisionType P)
42 {
43 switch (P)
44 {
45 default:
46 break;
47 case BF16:
48 case FP16:
49 return 16;
50 case U8:
51 case S8:
52 return 8;
53 case U4:
54 case S4:
55 return 4;
56 case U2:
57 case S2:
58 return 2;
59 }
60 return 0;
61 }
62 }
63
64 enum class SIMDMode : unsigned char
65 {
66 UNKNOWN,
67 SIMD1,
68 SIMD2,
69 SIMD4,
70 SIMD8,
71 SIMD16,
72 SIMD32,
73 END,
74 BEGIN = 0
75 };
76
77 enum class SIMDStatus : unsigned char
78 {
79 SIMD_BEGIN = 0,
80 SIMD_PASS,
81 SIMD_FUNC_FAIL,
82 SIMD_PERF_FAIL,
83 SIMD_END
84 };
85
numLanes(SIMDMode width)86 inline uint16_t numLanes(SIMDMode width)
87 {
88 switch(width)
89 {
90 case SIMDMode::SIMD1 : return 1;
91 case SIMDMode::SIMD2 : return 2;
92 case SIMDMode::SIMD4 : return 4;
93 case SIMDMode::SIMD8 : return 8;
94 case SIMDMode::SIMD16 : return 16;
95 case SIMDMode::SIMD32 : return 32;
96 case SIMDMode::UNKNOWN :
97 default:
98 IGC_ASSERT_MESSAGE(0, "unreachable");
99 return 1;
100 }
101 }
102
lanesToSIMDMode(unsigned lanes)103 inline SIMDMode lanesToSIMDMode(unsigned lanes) {
104 switch (lanes) {
105 case 1: return SIMDMode::SIMD1;
106 case 2: return SIMDMode::SIMD2;
107 case 4: return SIMDMode::SIMD4;
108 case 8: return SIMDMode::SIMD8;
109 case 16: return SIMDMode::SIMD16;
110 case 32: return SIMDMode::SIMD32;
111 default:
112 IGC_ASSERT_MESSAGE(0, "Unexpected number of lanes!");
113 return SIMDMode::UNKNOWN;
114 }
115 }
116
117 enum class ShaderType
118 {
119 UNKNOWN,
120 VERTEX_SHADER,
121 HULL_SHADER,
122 DOMAIN_SHADER,
123 GEOMETRY_SHADER,
124 PIXEL_SHADER,
125 COMPUTE_SHADER,
126 OPENCL_SHADER,
127 END,
128 BEGIN = 0
129 };
130
131 enum class ShaderDispatchMode
132 {
133 NOT_APPLICABLE,
134 SINGLE_PATCH,
135 DUAL_PATCH,
136 EIGHT_PATCH,
137 END,
138 BEGIN = 0
139 };
140
141 static const char *ShaderTypeString[] = {
142 "ERROR",
143 "VS",
144 "HS",
145 "DS",
146 "GS",
147 "PS",
148 "CS",
149 "OCL",
150 "ERROR"
151 };
152
153 static_assert(sizeof(ShaderTypeString) / sizeof(*ShaderTypeString) == static_cast<size_t>(ShaderType::END) + 1,
154 "Update the array");
155
156 template <typename TIter>
157 class RangeWrapper
158 {
159 public:
begin()160 TIter& begin() { return m_first; }
end()161 TIter& end() { return m_last; }
162
163 private:
RangeWrapper(TIter first,TIter last)164 RangeWrapper( TIter first, TIter last )
165 : m_first(first)
166 , m_last(last)
167 { }
168
169 TIter m_first;
170 TIter m_last;
171
172 template <typename T>
173 friend inline RangeWrapper<T> range( const T& first, const T& last );
174 };
175
176 /**
177 * \brief Create a proxy to iterate over a container's nonstandard iterator pairs via c++11 range-for
178 *
179 * This is to be used for containers that provide multiple sets of iterators
180 * such as llvm::Function, which has begin()/end() and arg_begin()/arg_end().
181 * In the latter case, range-for cannot be used without such a proxy.
182 *
183 * \example
184 * for ( auto arg : range( pFunc->arg_begin(), pFunc->arg_end() ) )
185 * {
186 * ...
187 * }
188 */
189 template <typename TIter>
range(const TIter & first,const TIter & last)190 inline RangeWrapper<TIter> range( const TIter& first, const TIter& last )
191 {
192 return RangeWrapper<TIter>(first, last);
193 }
194
195 template <typename TEnum>
196 class EnumIter
197 {
198 public:
operator ++()199 EnumIter operator++(/* prefix */)
200 {
201 increment_me();
202 return *this;
203 }
operator ++(int)204 EnumIter operator++(int /* postfix */)
205 {
206 TEnum old(m_val);
207 increment_me();
208 return old;
209 }
operator *() const210 TEnum operator*() const
211 {
212 return m_val;
213 }
operator !=(EnumIter const & other) const214 bool operator!=(EnumIter const& other) const
215 {
216 return !is_equal(other);
217 }
operator ==(EnumIter const & other) const218 bool operator==(EnumIter const& other) const
219 {
220 return is_equal(other);
221 }
222 private:
EnumIter(TEnum init)223 EnumIter(TEnum init)
224 : m_val(init)
225 {}
is_equal(EnumIter const & other) const226 bool is_equal(EnumIter const& other) const
227 {
228 return m_val == other.m_val;
229 }
increment_me()230 void increment_me()
231 {
232 m_val = static_cast<TEnum>( static_cast<unsigned int>(m_val) + 1 );
233 }
234
235 template <typename T>
236 friend inline RangeWrapper<EnumIter<T>> eRange(T, T);
237
238 TEnum m_val;
239 };
240
241 /**
242 * \brief Create a proxy to iterate over every element of an enumeration
243 *
244 * Assumes that TEnum follows this pattern, with no gaps between the enumerated values:
245 * enum class EFoo {
246 * E1,
247 * E2,
248 * END,
249 * BEGIN = 0
250 * };
251 */
252 template <typename TEnum>
eRange(TEnum begin=TEnum::BEGIN,TEnum end=TEnum::END)253 inline RangeWrapper<EnumIter<TEnum>> eRange( TEnum begin = TEnum::BEGIN, TEnum end = TEnum::END)
254 {
255 return range( EnumIter<TEnum>(begin), EnumIter<TEnum>(end) );
256 }
257
258 /// Template that should be used when a static_cast of a larger integer
259 /// type to a smaller one is required.
260 /// In debug, this will check if the source argument doesn't exceed the target type range.
261 /// In release, it is just static_cast with no check.
262 ///
263 /// Note that the cases where one needs to typecast integer types should be infrequent.
264 /// Could be necessary when dealing with other interfaces, otherwise think twice before
265 /// using it - maybe changing types or modifying the code design would be better.
266 template <typename TDst, typename TSrc>
267 inline typename std::enable_if<
268 std::is_signed<TDst>::value && std::is_signed<TSrc>::value,
int_cast(TSrc value)269 TDst>::type int_cast(TSrc value)
270 {
271 static_assert(std::is_integral<TDst>::value && std::is_integral<TSrc>::value,
272 "int_cast<>() should be used only for conversions between integer types.");
273
274 IGC_ASSERT(std::numeric_limits<TDst>::min() <= value);
275 IGC_ASSERT(value <= std::numeric_limits<TDst>::max());
276
277 return static_cast<TDst>(value);
278 }
279
280 template <typename TDst, typename TSrc>
281 inline typename std::enable_if<
282 std::is_signed<TDst>::value && std::is_unsigned<TSrc>::value,
int_cast(TSrc value)283 TDst>::type int_cast(TSrc value)
284 {
285 static_assert(std::is_integral<TDst>::value && std::is_integral<TSrc>::value,
286 "int_cast<>() should be used only for conversions between integer types.");
287
288 IGC_ASSERT(value <= static_cast<typename std::make_unsigned<TDst>::type>(std::numeric_limits<TDst>::max()));
289
290 return static_cast<TDst>(value);
291 }
292
293 template <typename TDst, typename TSrc>
294 inline typename std::enable_if<
295 std::is_unsigned<TDst>::value && std::is_signed<TSrc>::value,
int_cast(TSrc value)296 TDst>::type int_cast(TSrc value)
297 {
298 static_assert(std::is_integral<TDst>::value && std::is_integral<TSrc>::value,
299 "int_cast<>() should be used only for conversions between integer types.");
300
301 IGC_ASSERT(0 <= value);
302 IGC_ASSERT(static_cast<typename std::make_unsigned<TSrc>::type>(value) <= std::numeric_limits<TDst>::max());
303
304 return static_cast<TDst>(value);
305 }
306
307 template <typename TDst, typename TSrc>
308 inline typename std::enable_if<
309 std::is_unsigned<TDst>::value && std::is_unsigned<TSrc>::value,
int_cast(TSrc value)310 TDst>::type int_cast(TSrc value)
311 {
312 static_assert(std::is_integral<TDst>::value && std::is_integral<TSrc>::value,
313 "int_cast<>() should be used only for conversions between integer types.");
314
315 IGC_ASSERT(value <= std::numeric_limits<TDst>::max());
316 return static_cast<TDst>(value);
317 }
318
319 #if LLVM_VERSION_MAJOR >= 10
320 template <typename TDst>
321 inline typename std::enable_if<
322 std::is_unsigned<TDst>::value,
int_cast(llvm::TypeSize value)323 TDst>::type int_cast(llvm::TypeSize value)
324 {
325 static_assert(std::is_integral<TDst>::value,
326 "int_cast<>() should be used only for conversions between integer types.");
327
328 IGC_ASSERT(value.getFixedSize() <= std::numeric_limits<TDst>::max());
329 return static_cast<TDst>(value.getFixedSize());
330 }
331
332 template <typename TDst>
333 inline typename std::enable_if<
334 std::is_signed<TDst>::value,
int_cast(llvm::TypeSize value)335 TDst>::type int_cast(llvm::TypeSize value)
336 {
337 static_assert(std::is_integral<TDst>::value,
338 "int_cast<>() should be used only for conversions between integer types.");
339
340 IGC_ASSERT(value.getFixedSize() <= static_cast<typename std::make_unsigned<TDst>::type>(std::numeric_limits<TDst>::max()));
341 return static_cast<TDst>(value.getFixedSize());
342 }
343
344 #endif
345
346 #endif //IGC_COMMON_TYPES_H
347