DUNE PDELab (git)

debugalign.hh
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
4// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
5#ifndef DUNE_DEBUGALIGN_HH
6#define DUNE_DEBUGALIGN_HH
7
8#include <algorithm>
9#include <cassert>
10#include <cmath>
11#include <complex>
12#include <cstddef>
13#include <cstdint>
14#include <cstdlib> // abs
15#include <functional>
16#include <istream>
17#include <ostream>
18#include <type_traits>
19#include <utility>
20
22#include <dune/common/indices.hh>
26
27namespace Dune {
28
31 std::function<void(const char*, std::size_t, const void*)>;
32
34
41
43
52 void violatedAlignment(const char *className, std::size_t expectedAlignment,
53 const void *address);
54
56 inline bool isAligned(const void *p, std::size_t align)
57 {
58 void* aligned_p = (void*)p;
59 std::size_t space = align*2;
60 return p == std::align(align, align, aligned_p, space);
61 }
62
64 template<std::size_t align, class Impl>
65 struct alignas(align) AlignedBase
66 {
75 static void* operator new(std::size_t count, void* ptr) {
76 if(!isAligned(ptr, align))
77 violatedAlignment(className<Impl>().c_str(), align, ptr);
78 return ::operator new(count*sizeof(Impl), ptr);
79 }
80
89 static void* operator new[](std::size_t count, void* ptr) {
90 if(!isAligned(ptr, align))
91 violatedAlignment(className<Impl>().c_str(), align, ptr);
92 return ::operator new[](count*sizeof(Impl), ptr);
93 }
94 };
95
97 static constexpr auto debugAlignment = 2*alignof(std::max_align_t);
98
99 namespace AlignedNumberImpl {
100
101 template<class T, std::size_t align = debugAlignment>
102 class AlignedNumber;
103
104 } // namespace AlignedNumberImpl
105
106 using AlignedNumberImpl::AlignedNumber;
107
108 template<class T, std::size_t align>
109 struct IsNumber<AlignedNumberImpl::AlignedNumber<T,align>>
110 : public std::true_type {};
111
113 template<std::size_t align = debugAlignment, class T>
114 AlignedNumber<T, align> aligned(T value) { return { std::move(value) }; }
115
116 // The purpose of this namespace is to move the `<cmath>` function overloads
117 // out of namespace `Dune`. This avoids problems where people called
118 // e.g. `sqrt(1.0)` inside the `Dune` namespace, without first doing `using
119 // std::sqrt;`. Without any `Dune::sqrt()`, such a use will find
120 // `::sqrt()`, but with `Dune::sqrt()` it will find only `Dune::sqrt()`,
121 // which does not have an overload for `double`.
122 namespace AlignedNumberImpl {
123
125 template<class T, std::size_t align>
127 : public AlignedBase<align, AlignedNumber<T, align> >
128 {
129 T value_;
130
131 public:
132 AlignedNumber() = default;
133 AlignedNumber(T value) : value_(std::move(value)) {}
134 template<class U, std::size_t uAlign,
135 class = std::enable_if_t<(align >= uAlign) &&
136 std::is_convertible<U, T>::value> >
137 AlignedNumber(const AlignedNumber<U, uAlign> &o) : value_(U(o)) {}
138
139 // accessors
140 template<class U,
141 class = std::enable_if_t<std::is_convertible<T, U>::value> >
142 explicit operator U() const { return value_; }
143
144 const T &value() const { return value_; }
145 T &value() { return value_; }
146
147 // I/O
148 template<class charT, class Traits>
149 friend std::basic_istream<charT, Traits>&
150 operator>>(std::basic_istream<charT, Traits>& str, AlignedNumber &u)
151 {
152 return str >> u.value_;
153 }
154
155 template<class charT, class Traits>
156 friend std::basic_ostream<charT, Traits>&
157 operator<<(std::basic_ostream<charT, Traits>& str,
158 const AlignedNumber &u)
159 {
160 return str << u.value_;
161 }
162
163 // The trick with `template<class U = T, class = std::void_t<expr(U)> >` is
164 // needed because at least g++-4.9 seems to evaluates a default argument
165 // in `template<class = std::void_t<expr(T))> >` as soon as possible and will
166 // error out if `expr(T)` is invalid. E.g. for `expr(T)` =
167 // `decltype(--std::declval<T&>())`, instantiating `AlignedNumber<bool>`
168 // will result in an unrecoverable error (`--` cannot be applied to a
169 // `bool`).
170
171 // Increment, decrement
172 template<class U = T, class = std::void_t<decltype(++std::declval<U&>())> >
173 AlignedNumber &operator++() { ++value_; return *this; }
174
175 template<class U = T, class = std::void_t<decltype(--std::declval<U&>())> >
176 AlignedNumber &operator--() { --value_; return *this; }
177
178 template<class U = T, class = std::void_t<decltype(std::declval<U&>()++)> >
179 decltype(auto) operator++(int) { return aligned<align>(value_++); }
180
181 template<class U = T, class = std::void_t<decltype(std::declval<U&>()--)> >
182 decltype(auto) operator--(int) { return aligned<align>(value_--); }
183
184 // unary operators
185 template<class U = T,
186 class = std::void_t<decltype(+std::declval<const U&>())> >
187 decltype(auto) operator+() const { return aligned<align>(+value_); }
188
189 template<class U = T,
190 class = std::void_t<decltype(-std::declval<const U&>())> >
191 decltype(auto) operator-() const { return aligned<align>(-value_); }
192
193 /*
194 * silence warnings from GCC about using `~` on a bool
195 * (when instantiated for T=bool)
196 */
197#if __GNUC__ >= 7
198# pragma GCC diagnostic push
199# pragma GCC diagnostic ignored "-Wbool-operation"
200#endif
201#ifdef __clang__
202# pragma clang diagnostic push
203# pragma clang diagnostic ignored "-Wbool-operation"
204#endif
205 template<class U = T,
206 class = std::void_t<decltype(~std::declval<const U&>())> >
207 decltype(auto) operator~() const { return aligned<align>(~value_); }
208#if __GNUC__ >= 7
209# pragma GCC diagnostic pop
210#endif
211#ifdef __clang__
212# pragma clang diagnostic pop
213#endif
214
215 template<class U = T,
216 class = std::void_t<decltype(!std::declval<const U&>())> >
217 decltype(auto) operator!() const { return aligned<align>(!value_); }
218
219 // assignment operators
220#define DUNE_ASSIGN_OP(OP) \
221 template<class U, std::size_t uAlign, \
222 class = std::enable_if_t< \
223 ( uAlign <= align && \
224 sizeof(std::declval<T&>() OP std::declval<U>()) ) \
225 > > \
226 AlignedNumber &operator OP(const AlignedNumber<U, uAlign> &u) \
227 { \
228 value_ OP U(u); \
229 return *this; \
230 } \
231 \
232 template<class U, \
233 class = std::void_t<decltype(std::declval<T&>() OP \
234 std::declval<U>())> > \
235 AlignedNumber &operator OP(const U &u) \
236 { \
237 value_ OP u; \
238 return *this; \
239 } \
240 \
241 static_assert(true, "Require semicolon to unconfuse editors")
242
243 DUNE_ASSIGN_OP(+=);
244 DUNE_ASSIGN_OP(-=);
245
246 DUNE_ASSIGN_OP(*=);
247 DUNE_ASSIGN_OP(/=);
248 DUNE_ASSIGN_OP(%=);
249
250 DUNE_ASSIGN_OP(^=);
251 DUNE_ASSIGN_OP(&=);
252 DUNE_ASSIGN_OP(|=);
253
254 DUNE_ASSIGN_OP(<<=);
255 DUNE_ASSIGN_OP(>>=);
256
257#undef DUNE_ASSIGN_OP
258 };
259
260 // binary operators
261#define DUNE_BINARY_OP(OP) \
262 template<class T, std::size_t tAlign, class U, std::size_t uAlign, \
263 class = std::void_t<decltype(std::declval<T>() \
264 OP std::declval<U>())> > \
265 decltype(auto) \
266 operator OP(const AlignedNumber<T, tAlign> &t, \
267 const AlignedNumber<U, uAlign> &u) \
268 { \
269 /* can't use std::max(); not constexpr */ \
270 return aligned<(tAlign > uAlign ? tAlign : uAlign)>(T(t) OP U(u)); \
271 } \
272 \
273 template<class T, class U, std::size_t uAlign, \
274 class = std::void_t<decltype(std::declval<T>() \
275 OP std::declval<U>())> > \
276 decltype(auto) \
277 operator OP(const T &t, const AlignedNumber<U, uAlign> &u) \
278 { \
279 return aligned<uAlign>(t OP U(u)); \
280 } \
281 \
282 template<class T, std::size_t tAlign, class U, \
283 class = std::void_t<decltype(std::declval<T>() \
284 OP std::declval<U>())> > \
285 decltype(auto) \
286 operator OP(const AlignedNumber<T, tAlign> &t, const U &u) \
287 { \
288 return aligned<tAlign>(T(t) OP u); \
289 } \
290 \
291 static_assert(true, "Require semicolon to unconfuse editors")
292
293 DUNE_BINARY_OP(+);
294 DUNE_BINARY_OP(-);
295
296 DUNE_BINARY_OP(*);
297 DUNE_BINARY_OP(/);
298 DUNE_BINARY_OP(%);
299
300 DUNE_BINARY_OP(^);
301 DUNE_BINARY_OP(&);
302 DUNE_BINARY_OP(|);
303
304 DUNE_BINARY_OP(<<);
305 DUNE_BINARY_OP(>>);
306
307 DUNE_BINARY_OP(==);
308 DUNE_BINARY_OP(!=);
309 DUNE_BINARY_OP(<);
310 DUNE_BINARY_OP(>);
311 DUNE_BINARY_OP(<=);
312 DUNE_BINARY_OP(>=);
313
314 DUNE_BINARY_OP(&&);
315 DUNE_BINARY_OP(||);
316
317#undef DUNE_BINARY_OP
318
320 //
321 // Overloads for the functions provided by the standard library
322 //
323#define DUNE_UNARY_FUNC(name) \
324 template<class T, std::size_t align> \
325 decltype(auto) name(const AlignedNumber<T, align> &u) \
326 { \
327 using std::name; \
328 return aligned<align>(name(T(u))); \
329 } \
330 static_assert(true, "Require semicolon to unconfuse editors")
331
332 //
333 // <cmath> functions
334 //
335
336 // note: only unary functions are provided at the moment. Getting all the
337 // overloads right for functions with more than one argument is tricky.
338 // All <cmath> functions appear in the list below in the order they are
339 // listed in in the standard, but the unimplemented ones are commented
340 // out.
341
342 // note: abs is provided by both <cstdlib> (for integer) and <cmath> (for
343 // floating point). This overload works for both.
344 DUNE_UNARY_FUNC(abs);
345 DUNE_UNARY_FUNC(acos);
346 DUNE_UNARY_FUNC(acosh);
347 DUNE_UNARY_FUNC(asin);
348 DUNE_UNARY_FUNC(asinh);
349 DUNE_UNARY_FUNC(atan);
350 // atan2
351 DUNE_UNARY_FUNC(atanh);
352 DUNE_UNARY_FUNC(cbrt);
353 DUNE_UNARY_FUNC(ceil);
354 // copysign
355 DUNE_UNARY_FUNC(cos);
356 DUNE_UNARY_FUNC(cosh);
357 DUNE_UNARY_FUNC(erf);
358 DUNE_UNARY_FUNC(erfc);
359 DUNE_UNARY_FUNC(exp);
360 DUNE_UNARY_FUNC(exp2);
361 DUNE_UNARY_FUNC(expm1);
362 DUNE_UNARY_FUNC(fabs);
363 // fdim
364 DUNE_UNARY_FUNC(floor);
365 // fma
366 // fmax
367 // fmin
368 // fmod
369 // frexp
370 // hypos
371 DUNE_UNARY_FUNC(ilogb);
372 // ldexp
373 DUNE_UNARY_FUNC(lgamma);
374 DUNE_UNARY_FUNC(llrint);
375 DUNE_UNARY_FUNC(llround);
376 DUNE_UNARY_FUNC(log);
377 DUNE_UNARY_FUNC(log10);
378 DUNE_UNARY_FUNC(log1p);
379 DUNE_UNARY_FUNC(log2);
380 DUNE_UNARY_FUNC(logb);
381 DUNE_UNARY_FUNC(lrint);
382 DUNE_UNARY_FUNC(lround);
383 // modf
384 DUNE_UNARY_FUNC(nearbyint);
385 // nextafter
386 // nexttoward
387 // pow
388 // remainder
389 // remquo
390 DUNE_UNARY_FUNC(rint);
391 DUNE_UNARY_FUNC(round);
392 // scalbln
393 // scalbn
394 DUNE_UNARY_FUNC(sin);
395 DUNE_UNARY_FUNC(sinh);
396 DUNE_UNARY_FUNC(sqrt);
397 DUNE_UNARY_FUNC(tan);
398 DUNE_UNARY_FUNC(tanh);
399 DUNE_UNARY_FUNC(tgamma);
400 DUNE_UNARY_FUNC(trunc);
401
402 DUNE_UNARY_FUNC(isfinite);
403 DUNE_UNARY_FUNC(isinf);
404 DUNE_UNARY_FUNC(isnan);
405 DUNE_UNARY_FUNC(isnormal);
406 DUNE_UNARY_FUNC(signbit);
407
408 // isgreater
409 // isgreaterequal
410 // isless
411 // islessequal
412 // islessgreater
413 // isunordered
414
415 //
416 // <complex> functions
417 //
418
419 // not all functions are implemented, and unlike for <cmath>, no
420 // comprehensive list is provided
421 DUNE_UNARY_FUNC(real);
422
423#undef DUNE_UNARY_FUNC
424
425 // We need to overload min() and max() since they require types to be
426 // LessThanComparable, which requires `a<b` to be "convertible to bool".
427 // That wording seems to be a leftover from C++03, and today is probably
428 // equivalent to "implicitly convertible". There is also issue 2114
429 // <https://cplusplus.github.io/LWG/issue2114> in the standard (still open
430 // as of 2018-07-06), which strives to require both "implicitly" and
431 // "contextually" convertible -- plus a few other things.
432 //
433 // We do not want our debug type to automatically decay to the underlying
434 // type, so we do not want to make the conversion non-explicit. So the
435 // only option left is to overload min() and max().
436
437 template<class T, std::size_t align>
438 auto max(const AlignedNumber<T, align> &a,
440 {
441 using std::max;
442 return aligned<align>(max(T(a), T(b)));
443 }
444
445 template<class T, std::size_t align>
446 auto max(const T &a, const AlignedNumber<T, align> &b)
447 {
448 using std::max;
449 return aligned<align>(max(a, T(b)));
450 }
451
452 template<class T, std::size_t align>
453 auto max(const AlignedNumber<T, align> &a, const T &b)
454 {
455 using std::max;
456 return aligned<align>(max(T(a), b));
457 }
458
459 template<class T, std::size_t align>
460 auto min(const AlignedNumber<T, align> &a,
461 const AlignedNumber<T, align> &b)
462 {
463 using std::min;
464 return aligned<align>(min(T(a), T(b)));
465 }
466
467 template<class T, std::size_t align>
468 auto min(const T &a, const AlignedNumber<T, align> &b)
469 {
470 using std::min;
471 return aligned<align>(min(a, T(b)));
472 }
473
474 template<class T, std::size_t align>
475 auto min(const AlignedNumber<T, align> &a, const T &b)
476 {
477 using std::min;
478 return aligned<align>(min(T(a), b));
479 }
480
481 } // namespace AlignedNumberImpl
482
483 // SIMD-like functions from "conditional.hh"
484 template<class T, std::size_t align>
485 AlignedNumber<T, align>
486 cond(const AlignedNumber<bool, align> &b,
487 const AlignedNumber<T, align> &v1, const AlignedNumber<T, align> &v2)
488 {
489 return b ? v1 : v2;
490 }
491
492 // SIMD-like functions from "rangeutilities.hh"
493 template<class T, std::size_t align>
494 T max_value(const AlignedNumber<T, align>& val)
495 {
496 return T(val);
497 }
498
499 template<class T, std::size_t align>
500 T min_value(const AlignedNumber<T, align>& val)
501 {
502 return T(val);
503 }
504
505 template<std::size_t align>
506 bool any_true(const AlignedNumber<bool, align>& val)
507 {
508 return bool(val);
509 }
510
511 template<std::size_t align>
512 bool all_true(const AlignedNumber<bool, align>& val)
513 {
514 return bool(val);
515 }
516
517 // SIMD-like functionality from "simd/interface.hh"
518 namespace Simd {
519 namespace Overloads {
520
521 template<class T, std::size_t align>
522 struct ScalarType<AlignedNumber<T, align> > { using type = T; };
523
524 template<class U, class T, std::size_t align>
525 struct RebindType<U, AlignedNumber<T, align> > {
526 using type = AlignedNumber<U, align>;
527 };
528
529 template<class T, std::size_t align>
530 struct LaneCount<AlignedNumber<T, align> > : index_constant<1> {};
531
532 template<class T, std::size_t align>
533 T& lane(ADLTag<5>, std::size_t l, AlignedNumber<T, align> &v)
534 {
535 assert(l == 0);
536 return v.value();
537 }
538
539 template<class T, std::size_t align>
540 T lane(ADLTag<5>, std::size_t l, const AlignedNumber<T, align> &v)
541 {
542 assert(l == 0);
543 return v.value();
544 }
545
546 template<class T, std::size_t align>
549 const AlignedNumber<T, align> &ifTrue,
550 const AlignedNumber<T, align> &ifFalse)
551 {
552 return mask ? ifTrue : ifFalse;
553 }
554
555 template<std::size_t align>
557 {
558 return bool(mask);
559 }
560
561 } // namespace Overloads
562
563 } // namespace Simd
564
565} // namespace Dune
566
567#endif // DUNE_DEBUGALIGN_HH
Basic definitions for SIMD Implementations.
aligned wrappers for arithmetic types
Definition: debugalign.hh:128
A free function to provide the demangled class name of a given object or type as a string.
Default implementations for SIMD Implementations.
Traits for type conversions and type information.
std::istream & operator>>(std::istream &stream, std::tuple< Ts... > &t)
Read a std::tuple.
Definition: streamoperators.hh:43
std::integral_constant< std::size_t, i > index_constant
An index constant with value i.
Definition: indices.hh:29
typename Impl::voider< Types... >::type void_t
Is void for all valid input types. The workhorse for C++11 SFINAE-techniques.
Definition: typetraits.hh:40
I round(const T &val, typename EpsilonType< T >::Type epsilon)
round using epsilon
Definition: float_cmp.cc:311
I trunc(const T &val, typename EpsilonType< T >::Type epsilon)
truncate using epsilon
Definition: float_cmp.cc:407
constexpr auto max
Function object that returns the greater of the given values.
Definition: hybridutilities.hh:484
constexpr auto min
Function object that returns the smaller of the given values.
Definition: hybridutilities.hh:506
Mask< V > mask(ADLTag< 0, std::is_same< V, Mask< V > >::value >, const V &v)
implements Simd::mask()
Definition: defaults.hh:153
bool anyTrue(ADLTag< 0 >, const Mask &mask)=delete
implements Simd::anyTrue()
Dune namespace.
Definition: alignedallocator.hh:13
void violatedAlignment(const char *className, std::size_t expectedAlignment, const void *address)
called when an alignment violation is detected
Definition: debugalign.cc:36
std::string className()
Provide the demangled class name of a type T as a string.
Definition: classname.hh:47
static constexpr auto debugAlignment
an alignment large enough to trigger alignment errors
Definition: debugalign.hh:97
T lane(std::size_t l, const T &v)
access a lane of a simd vector (scalar version)
Definition: simd.hh:366
const T1 cond(bool b, const T1 &v1, const T2 &v2)
conditional evaluate
Definition: conditional.hh:28
AlignedNumber< T, align > aligned(T value)
align a value to a certain alignment
Definition: debugalign.hh:114
ViolatedAlignmentHandler & violatedAlignmentHandler()
access the handler called by violatedAlignment()
Definition: debugalign.cc:30
bool isAligned(const void *p, std::size_t align)
check whether an address conforms to the given alignment
Definition: debugalign.hh:56
std::function< void(const char *, std::size_t, const void *)> ViolatedAlignmentHandler
type of the handler called by violatedAlignment()
Definition: debugalign.hh:31
CRTP base mixin class to check alignment.
Definition: debugalign.hh:66
Tag used to force late-binding lookup in Dune::Simd::Overloads.
Definition: base.hh:182
should be derived from a Dune::index_constant
Definition: standard.hh:74
should have a member type type
Definition: standard.hh:67
should have a member type type
Definition: standard.hh:60
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Nov 23, 23:29, 2024)