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
34namespace dnnl {
35namespace 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.
41void 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
48void *malloc(size_t size, int alignment);
49#define MALLOC(size, alignment) malloc(size, alignment)
50#define FREE(ptr) free(ptr)
51#endif
52void free(void *p);
53
54struct 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
69protected:
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
80namespace nstl {
81
82template <typename T>
83constexpr 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.
89template <typename T>
90inline const T modulo(const T &dividend, 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.
99template <typename T>
100inline const T additive_inverse_modulo(const T &dividend, 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
107template <typename T>
108constexpr const T &max(const T &a, const T &b) {
109 return a > b ? a : b;
110}
111
112template <typename T>
113constexpr const T &min(const T &a, const T &b) {
114 return a < b ? a : b;
115}
116
117template <typename T>
118void 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
127template <typename T>
128struct numeric_limits;
129
130template <>
131struct numeric_limits<float> : public std::numeric_limits<float> {};
132
133template <>
134struct numeric_limits<double> : public std::numeric_limits<double> {};
135
136template <>
137struct numeric_limits<int32_t> : public std::numeric_limits<int32_t> {};
138
139template <>
140struct numeric_limits<int16_t> : public std::numeric_limits<int16_t> {};
141
142template <>
143struct numeric_limits<uint16_t> : public std::numeric_limits<uint16_t> {};
144
145template <>
146struct numeric_limits<int8_t> : public std::numeric_limits<int8_t> {};
147
148template <>
149struct numeric_limits<uint8_t> : public std::numeric_limits<uint8_t> {};
150
151template <>
152struct 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
164template <>
165struct 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
177template <typename T>
178struct is_integral {
179 static constexpr bool value = false;
180};
181template <>
182struct is_integral<int32_t> {
183 static constexpr bool value = true;
184};
185template <>
186struct is_integral<int16_t> {
187 static constexpr bool value = true;
188};
189template <>
190struct is_integral<int8_t> {
191 static constexpr bool value = true;
192};
193template <>
194struct is_integral<uint8_t> {
195 static constexpr bool value = true;
196};
197
198template <typename T, typename U>
199struct is_same {
200 static constexpr bool value = false;
201};
202template <typename T>
203struct 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
224enum nstl_status_t { success = 0, out_of_memory };
225
226template <typename T>
227class vector : public c_compatible {
228private:
229 std::vector<T> _impl;
230
231public:
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
260template <typename Key, typename T>
261class map : public c_compatible {
262private:
263 std::map<Key, T> _impl;
264
265public:
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)
285template <size_t... Ints>
286struct index_sequence {};
287
288template <size_t N, size_t... Next>
289struct make_index_sequence_helper
290 : public make_index_sequence_helper<N - 1, N - 1, Next...> {};
291
292template <size_t... Next>
293struct make_index_sequence_helper<0, Next...> {
294 using type = index_sequence<Next...>;
295};
296
297// Generator of compile-time sequence of indices
298template <size_t N>
299using 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