1 | /******************************************************************************* |
2 | * Copyright 2016-2022 Intel Corporation |
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 COMMON_NSTL_HPP |
18 | #define COMMON_NSTL_HPP |
19 | |
20 | #include <float.h> |
21 | #include <limits.h> |
22 | #include <stdint.h> |
23 | |
24 | #include <cassert> |
25 | #include <cstdlib> |
26 | #include <map> |
27 | #include <vector> |
28 | |
29 | #include "bfloat16.hpp" |
30 | #include "float16.hpp" |
31 | #include "internal_defs.hpp" |
32 | #include "z_magic.hpp" |
33 | |
34 | namespace dnnl { |
35 | namespace impl { |
36 | |
37 | #ifdef DNNL_ENABLE_MEM_DEBUG |
38 | // Export the malloc symbol (for SHARED build) or define it as weak (for STATIC |
39 | // build) in order to replace it with a custom one from an object file at link |
40 | // time. |
41 | void DNNL_WEAK *malloc(size_t size, int alignment); |
42 | // Use glibc malloc (and, consequently, free) when DNNL_ENABLE_MEM_DEBUG is |
43 | // enabled, such that the operator new doesn't fail during out_of_memory |
44 | // testing. |
45 | #define MALLOC(size, alignment) ::malloc(size) |
46 | #define FREE(ptr) ::free(ptr) |
47 | #else |
48 | void *malloc(size_t size, int alignment); |
49 | #define MALLOC(size, alignment) malloc(size, alignment) |
50 | #define FREE(ptr) free(ptr) |
51 | #endif |
52 | void free(void *p); |
53 | |
54 | struct c_compatible { |
55 | enum { default_alignment = 64 }; |
56 | static void *operator new(size_t sz) { |
57 | return MALLOC(sz, default_alignment); |
58 | } |
59 | static void *operator new(size_t sz, void *p) { |
60 | UNUSED(sz); |
61 | return p; |
62 | } |
63 | static void *operator new[](size_t sz) { |
64 | return MALLOC(sz, default_alignment); |
65 | } |
66 | static void operator delete(void *p) { FREE(p); } |
67 | static void operator delete[](void *p) { FREE(p); } |
68 | |
69 | protected: |
70 | // This member is intended as a check for whether all necessary members in |
71 | // derived classes have been allocated. Consequently status::out_of_memory |
72 | // should be returned if a fail occurs. This member should be modified only |
73 | // in a constructor. |
74 | bool is_initialized_ = true; |
75 | }; |
76 | |
77 | #undef MALLOC |
78 | #undef FREE |
79 | |
80 | namespace nstl { |
81 | |
82 | template <typename T> |
83 | constexpr const T abs(const T &a) { |
84 | return a >= 0 ? a : -a; |
85 | } |
86 | |
87 | // Computes the modulus and returns the result as the least positive residue |
88 | // when the divisor > 0. |
89 | template <typename T> |
90 | inline const T modulo(const T ÷nd, const T &divisor) { |
91 | static_assert(std::is_integral<T>::value, "T must be an integer type." ); |
92 | assert(divisor > 0); |
93 | T result = dividend % divisor; |
94 | return result < 0 ? result + divisor : result; |
95 | } |
96 | |
97 | // Computes the additive inverse modulus and returns the result as the least |
98 | // positive residue when the divisor > 0. |
99 | template <typename T> |
100 | inline const T additive_inverse_modulo(const T ÷nd, const T &divisor) { |
101 | static_assert(std::is_integral<T>::value, "T must be an integer type." ); |
102 | assert(divisor > 0); |
103 | T result = modulo(dividend, divisor); |
104 | return result > 0 ? divisor - result : 0; |
105 | } |
106 | |
107 | template <typename T> |
108 | constexpr const T &max(const T &a, const T &b) { |
109 | return a > b ? a : b; |
110 | } |
111 | |
112 | template <typename T> |
113 | constexpr const T &min(const T &a, const T &b) { |
114 | return a < b ? a : b; |
115 | } |
116 | |
117 | template <typename T> |
118 | void swap(T &t1, T &t2) { |
119 | T tmp(t1); |
120 | t1 = t2; |
121 | t2 = tmp; |
122 | } |
123 | |
124 | // Rationale: oneDNN needs numeric limits implementation that does not |
125 | // generate dependencies on C++ run-time libraries. |
126 | |
127 | template <typename T> |
128 | struct numeric_limits; |
129 | |
130 | template <> |
131 | struct numeric_limits<float> : public std::numeric_limits<float> {}; |
132 | |
133 | template <> |
134 | struct numeric_limits<double> : public std::numeric_limits<double> {}; |
135 | |
136 | template <> |
137 | struct numeric_limits<int32_t> : public std::numeric_limits<int32_t> {}; |
138 | |
139 | template <> |
140 | struct numeric_limits<int16_t> : public std::numeric_limits<int16_t> {}; |
141 | |
142 | template <> |
143 | struct numeric_limits<uint16_t> : public std::numeric_limits<uint16_t> {}; |
144 | |
145 | template <> |
146 | struct numeric_limits<int8_t> : public std::numeric_limits<int8_t> {}; |
147 | |
148 | template <> |
149 | struct numeric_limits<uint8_t> : public std::numeric_limits<uint8_t> {}; |
150 | |
151 | template <> |
152 | struct numeric_limits<bfloat16_t> { |
153 | static constexpr bfloat16_t lowest() { return bfloat16_t(0xff7f, true); } |
154 | |
155 | static constexpr bfloat16_t max() { return bfloat16_t(0x7f7f, true); } |
156 | |
157 | static constexpr int digits = 8; |
158 | |
159 | static constexpr bfloat16_t epsilon() { |
160 | return bfloat16_t(((0x7f - (digits - 1)) << (digits - 1)), true); |
161 | } |
162 | }; |
163 | |
164 | template <> |
165 | struct numeric_limits<float16_t> { |
166 | static constexpr float16_t lowest() { return float16_t(0xfbff, true); } |
167 | |
168 | static constexpr float16_t max() { return float16_t(0x7bff, true); } |
169 | |
170 | static constexpr int digits = 11; |
171 | |
172 | static constexpr float16_t epsilon() { |
173 | return float16_t(((0x0f - (digits - 1)) << (digits - 1)), true); |
174 | } |
175 | }; |
176 | |
177 | template <typename T> |
178 | struct is_integral { |
179 | static constexpr bool value = false; |
180 | }; |
181 | template <> |
182 | struct is_integral<int32_t> { |
183 | static constexpr bool value = true; |
184 | }; |
185 | template <> |
186 | struct is_integral<int16_t> { |
187 | static constexpr bool value = true; |
188 | }; |
189 | template <> |
190 | struct is_integral<int8_t> { |
191 | static constexpr bool value = true; |
192 | }; |
193 | template <> |
194 | struct is_integral<uint8_t> { |
195 | static constexpr bool value = true; |
196 | }; |
197 | |
198 | template <typename T, typename U> |
199 | struct is_same { |
200 | static constexpr bool value = false; |
201 | }; |
202 | template <typename T> |
203 | struct is_same<T, T> { |
204 | static constexpr bool value = true; |
205 | }; |
206 | |
207 | // Rationale: oneDNN needs container implementations that do not generate |
208 | // dependencies on C++ run-time libraries. |
209 | // |
210 | // Implementation philosophy: caller is responsible to check if the operation |
211 | // is valid. The only functions that have to return status are those that |
212 | // depend on memory allocation or similar operations. |
213 | // |
214 | // This means that e.g. an operator [] does not have to check for boundaries. |
215 | // The caller should have checked the boundaries. If it did not we crash and |
216 | // burn: this is a bug in oneDNN and throwing an exception would not have been |
217 | // recoverable. |
218 | // |
219 | // On the other hand, insert() or resize() or a similar operation needs to |
220 | // return a status because the outcome depends on factors external to the |
221 | // caller. The situation is probably also not recoverable also, but oneDNN |
222 | // needs to be nice and report "out of memory" to the users. |
223 | |
224 | enum nstl_status_t { success = 0, out_of_memory }; |
225 | |
226 | template <typename T> |
227 | class vector : public c_compatible { |
228 | private: |
229 | std::vector<T> _impl; |
230 | |
231 | public: |
232 | typedef typename std::vector<T>::iterator iterator; |
233 | typedef typename std::vector<T>::const_iterator const_iterator; |
234 | typedef typename std::vector<T>::size_type size_type; |
235 | vector() {} |
236 | vector(size_type n) : _impl(n) {} |
237 | vector(size_type n, const T &value) : _impl(n, value) {} |
238 | template <typename input_iterator> |
239 | vector(input_iterator first, input_iterator last) : _impl(first, last) {} |
240 | ~vector() {} |
241 | size_type size() const { return _impl.size(); } |
242 | T &operator[](size_type i) { return _impl[i]; } |
243 | const T &operator[](size_type i) const { return _impl[i]; } |
244 | iterator begin() { return _impl.begin(); } |
245 | const_iterator begin() const { return _impl.begin(); } |
246 | iterator end() { return _impl.end(); } |
247 | const_iterator end() const { return _impl.end(); } |
248 | template <typename input_iterator> |
249 | nstl_status_t insert( |
250 | iterator pos, input_iterator begin, input_iterator end) { |
251 | _impl.insert(pos, begin, end); |
252 | return success; |
253 | } |
254 | void clear() { _impl.clear(); } |
255 | void push_back(const T &t) { _impl.push_back(t); } |
256 | void resize(size_type count) { _impl.resize(count); } |
257 | void reserve(size_type count) { _impl.reserve(count); } |
258 | }; |
259 | |
260 | template <typename Key, typename T> |
261 | class map : public c_compatible { |
262 | private: |
263 | std::map<Key, T> _impl; |
264 | |
265 | public: |
266 | typedef typename std::map<Key, T>::iterator iterator; |
267 | typedef typename std::map<Key, T>::const_iterator const_iterator; |
268 | typedef typename std::map<Key, T>::size_type size_type; |
269 | map() {} |
270 | ~map() {} |
271 | size_type size() const { return _impl.size(); } |
272 | T &operator[](const Key &k) { return _impl[k]; } |
273 | const T &operator[](const Key &k) const { return _impl[k]; } |
274 | iterator begin() { return _impl.begin(); } |
275 | const_iterator begin() const { return _impl.begin(); } |
276 | iterator end() { return _impl.end(); } |
277 | const_iterator end() const { return _impl.end(); } |
278 | template <typename input_iterator> |
279 | void clear() { |
280 | _impl.clear(); |
281 | } |
282 | }; |
283 | |
284 | // Compile-time sequence of indices (part of C++14) |
285 | template <size_t... Ints> |
286 | struct index_sequence {}; |
287 | |
288 | template <size_t N, size_t... Next> |
289 | struct make_index_sequence_helper |
290 | : public make_index_sequence_helper<N - 1, N - 1, Next...> {}; |
291 | |
292 | template <size_t... Next> |
293 | struct make_index_sequence_helper<0, Next...> { |
294 | using type = index_sequence<Next...>; |
295 | }; |
296 | |
297 | // Generator of compile-time sequence of indices |
298 | template <size_t N> |
299 | using make_index_sequence = typename make_index_sequence_helper<N>::type; |
300 | } // namespace nstl |
301 | } // namespace impl |
302 | } // namespace dnnl |
303 | |
304 | #endif |
305 | |
306 | // vim: et ts=4 sw=4 cindent cino+=l0,\:4,N-s |
307 | |