openMSX
aligned.hh
Go to the documentation of this file.
1#ifndef ALIGNED_HH
2#define ALIGNED_HH
3
4#include "inline.hh"
5
6#include <bit>
7#include <cstdint>
8#include <cassert>
9#include <cstring>
10
11// Only need to (more strictly) align when SSE is actually enabled.
12#ifdef __SSE2__
13inline constexpr size_t SSE_ALIGNMENT = 16;
14#else
15inline constexpr size_t SSE_ALIGNMENT = 0; // alignas(0) has no effect
16#endif
17
18// 'alignas(0)' has no effect according to the C++ standard, however gcc
19// produces a warning for it. This for example triggers when using
20// alignas(SSE_ALIGNMENT)
21// and the target has no SSE instructions (thus SSE_ALIGNMENT == 0).
22// The following macro works around that.
23#ifdef __SSE2__
24#define ALIGNAS_SSE alignas(SSE_ALIGNMENT)
25#else
26#define ALIGNAS_SSE /*nothing*/
27#endif
28
29// Unaligned loads and stores.
30template<typename T> [[nodiscard]] static ALWAYS_INLINE T unalignedLoad(const void* p)
31{
32 T t;
33 memcpy(&t, p, sizeof(t));
34 return t;
35}
36static ALWAYS_INLINE void unalignedStore(void* p, auto t)
37{
38 memcpy(p, &t, sizeof(t));
39}
40
41[[nodiscard]] ALWAYS_INLINE uint16_t unalignedLoad16(const void* p) {
42 return unalignedLoad<uint16_t>(p);
43}
44[[nodiscard]] ALWAYS_INLINE uint32_t unalignedLoad32(const void* p) {
45 return unalignedLoad<uint32_t>(p);
46}
47[[nodiscard]] ALWAYS_INLINE uint64_t unalignedLoad64(const void* p) {
48 return unalignedLoad<uint64_t>(p);
49}
50ALWAYS_INLINE void unalignedStore16(void* p, uint16_t v) {
51 unalignedStore(p, v);
52}
53ALWAYS_INLINE void unalignedStore32(void* p, uint32_t v) {
54 unalignedStore(p, v);
55}
56ALWAYS_INLINE void unalignedStore64(void* p, uint64_t v) {
57 unalignedStore(p, v);
58}
59
60
61// assume_aligned, assume_SSE_aligned
62//
63// Let the compiler know a pointer is more strictly aligned than guaranteed by
64// the C++ language rules. Sometimes this allows the compiler to generate more
65// efficient code (typically when auto-vectorization is performed).
66//
67// Example:
68// int* p = ...
69// assume_SSE_aligned(p); // compiler can now assume 16-byte alignment for p
70
71// clang offers __has_builtin, fallback for other compilers
72#ifndef __has_builtin
73# define __has_builtin(x) 0
74#endif
75
76// gcc-version check macro
77#if defined(__GNUC__) && defined(__GNUC_MINOR__)
78# define GNUC_PREREQ(maj, min) \
79 (((maj) * 100 + (min)) <= (__GNUC__ * 100 + __GNUC_MINOR__))
80#else
81# define GNUC_PREREQ(maj, min) 0
82#endif
83
84template<size_t A, typename T>
85static ALWAYS_INLINE void assume_aligned(T* __restrict & ptr)
86{
87#ifdef DEBUG // only check in debug build
88 assert((std::bit_cast<uintptr_t>(ptr) % A) == 0);
89#endif
90
91#if __has_builtin(__builtin_assume_aligned) || GNUC_PREREQ(4, 7)
92 ptr = static_cast<T* __restrict>(__builtin_assume_aligned(ptr, A));
93#else
94 (void)ptr; // nothing
95#endif
96}
97
98template<typename T> static ALWAYS_INLINE void assume_SSE_aligned(
99#ifdef __SSE2__
100 T* __restrict & ptr) { assume_aligned<16>(ptr); }
101#else
102 T* __restrict & /*ptr*/) { /* nothing */ }
103#endif
104
105#endif // ALIGNED_HH
TclObject t
ALWAYS_INLINE uint64_t unalignedLoad64(const void *p)
Definition aligned.hh:47
ALWAYS_INLINE void unalignedStore64(void *p, uint64_t v)
Definition aligned.hh:56
ALWAYS_INLINE uint16_t unalignedLoad16(const void *p)
Definition aligned.hh:41
constexpr size_t SSE_ALIGNMENT
Definition aligned.hh:15
ALWAYS_INLINE void unalignedStore16(void *p, uint16_t v)
Definition aligned.hh:50
ALWAYS_INLINE void unalignedStore32(void *p, uint32_t v)
Definition aligned.hh:53
ALWAYS_INLINE uint32_t unalignedLoad32(const void *p)
Definition aligned.hh:44
#define ALWAYS_INLINE
Definition inline.hh:16