OpenJPH
Open-source implementation of JPEG2000 Part-15
Loading...
Searching...
No Matches
ojph_arch.h
Go to the documentation of this file.
1//***************************************************************************/
2// This software is released under the 2-Clause BSD license, included
3// below.
4//
5// Copyright (c) 2019, Aous Naman
6// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
7// Copyright (c) 2019, The University of New South Wales, Australia
8// Copyright (c) 2026, Osamu Watanabe
9//
10// Redistribution and use in source and binary forms, with or without
11// modification, are permitted provided that the following conditions are
12// met:
13//
14// 1. Redistributions of source code must retain the above copyright
15// notice, this list of conditions and the following disclaimer.
16//
17// 2. Redistributions in binary form must reproduce the above copyright
18// notice, this list of conditions and the following disclaimer in the
19// documentation and/or other materials provided with the distribution.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//***************************************************************************/
33// This file is part of the OpenJPH software implementation.
34// File: ojph_arch.h
35// Author: Aous Naman
36// Date: 28 August 2019
37//***************************************************************************/
38
39
40#ifndef OJPH_ARCH_H
41#define OJPH_ARCH_H
42
43#include <cstring>
44#include <cstdio>
45#include <cstdint>
46#include <cmath>
47
48#include "ojph_defs.h"
49
50
52// preprocessor directives for compiler
54#ifdef _MSC_VER
55#define OJPH_COMPILER_MSVC
56#elif (defined __GNUC__)
57#define OJPH_COMPILER_GNUC
58#endif
59
60#ifdef __EMSCRIPTEN__
61#define OJPH_EMSCRIPTEN
62#endif
63
64#ifdef OJPH_COMPILER_MSVC
65#include <intrin.h>
66#endif
67
69 // portable force-inline / no-inline function qualifiers
71#ifdef OJPH_COMPILER_MSVC
72 #define OJPH_FORCE_INLINE static __forceinline
73 #define OJPH_NO_INLINE static __declspec(noinline)
74#else
75 #define OJPH_FORCE_INLINE static inline __attribute__((always_inline))
76 #define OJPH_NO_INLINE static __attribute__((noinline))
77#endif
78
80// preprocessor directives for architecture
82#if defined(__arm__) || defined(__TARGET_ARCH_ARM) \
83 || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
84 #define OJPH_ARCH_ARM
85#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
86 #define OJPH_ARCH_I386
87#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) \
88 || defined(_M_X64)
89 #define OJPH_ARCH_X86_64
90#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
91 #define OJPH_ARCH_IA64
92#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
93 || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
94 || defined(_M_MPPC) || defined(_M_PPC)
95 #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
96 #define OJPH_ARCH_PPC64
97 #else
98 #define OJPH_ARCH_PPC
99 #endif
100#else
101 #define OJPH_ARCH_UNKNOWN
102#endif
103
104// Only little-endian POWER (ppc64le) is supported for SIMD
105#if defined(OJPH_ARCH_PPC64) && \
106 (defined(__LITTLE_ENDIAN__) || \
107 (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
108 #define OJPH_ARCH_PPC64LE
109#endif
110
111namespace ojph {
113 // disable SIMD for unknown architecture
115#if !defined(OJPH_ARCH_X86_64) && !defined(OJPH_ARCH_I386) && \
116 !defined(OJPH_ARCH_ARM) && !defined(OJPH_ARCH_PPC64LE) && \
117 !defined(OJPH_DISABLE_SIMD)
118#define OJPH_DISABLE_SIMD
119#endif // !OJPH_ARCH_UNKNOWN
120
122 // OS detection definitions
124#if (defined WIN32) || (defined _WIN32) || (defined _WIN64)
125#define OJPH_OS_WINDOWS
126#elif (defined __APPLE__)
127#define OJPH_OS_APPLE
128#elif (defined __ANDROID__)
129#define OJPH_OS_ANDROID
130#elif (defined __linux)
131#define OJPH_OS_LINUX
132#elif (defined __FreeBSD__)
133#define OJPH_OS_FREEBSD
134#elif (defined __OpenBSD__)
135#define OJPH_OS_OPENBSD
136#endif
137
139 // defines for dll
141#if defined(OJPH_OS_WINDOWS) && defined(OJPH_BUILD_SHARED_LIBRARY)
142#define OJPH_EXPORT __declspec(dllexport)
143#else
144#define OJPH_EXPORT
145#endif
146
148 // cpu features
151 int get_cpu_ext_level();
152
153 enum : int {
166 };
167
168 enum : int {
174 };
175
176 // POWER8 (ISA 2.07) is the minimum supported SIMD level; older CPUs
177 // (POWER7 and earlier) use the generic code paths
178 enum : int {
180 PPC_CPU_EXT_LEVEL_ARCH_2_07 = 1, // ISA 2.07 (POWER8)
181 PPC_CPU_EXT_LEVEL_ARCH_3_00 = 2, // ISA 3.0 (POWER9)
182 PPC_CPU_EXT_LEVEL_ARCH_3_1 = 3, // ISA 3.1 (POWER10)
183 };
184
186 static inline ui32 population_count(ui32 val)
187 {
188 #if defined(OJPH_COMPILER_MSVC) \
189 && (defined(OJPH_ARCH_X86_64) || defined(OJPH_ARCH_I386))
190 return (ui32)__popcnt(val);
191 #elif (defined OJPH_COMPILER_GNUC)
192 return (ui32)__builtin_popcount(val);
193 #else
194 val -= ((val >> 1) & 0x55555555);
195 val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
196 val = (((val >> 4) + val) & 0x0f0f0f0f);
197 val += (val >> 8);
198 val += (val >> 16);
199 return (int)(val & 0x0000003f);
200 #endif
201 }
202
204#ifdef OJPH_COMPILER_MSVC
205 #pragma intrinsic(_BitScanReverse)
206#endif
207 static inline ui32 count_leading_zeros(ui32 val)
208 {
209 #ifdef OJPH_COMPILER_MSVC
210 unsigned long result = 0;
211 _BitScanReverse(&result, val);
212 return 31 ^ (ui32)result;
213 #elif (defined OJPH_COMPILER_GNUC)
214 return (ui32)__builtin_clz(val);
215 #else
216 val |= (val >> 1);
217 val |= (val >> 2);
218 val |= (val >> 4);
219 val |= (val >> 8);
220 val |= (val >> 16);
221 return 32 - population_count(val);
222 #endif
223 }
224
226#ifdef OJPH_COMPILER_MSVC
227 #if (defined OJPH_ARCH_X86_64 || defined OJPH_ARCH_ARM)
228 #pragma intrinsic(_BitScanReverse64)
229 #elif (defined OJPH_ARCH_I386)
230 #pragma intrinsic(_BitScanReverse)
231 #else
232 #error Error unsupport MSVC version
233 #endif
234#endif
235 static inline ui32 count_leading_zeros(ui64 val)
236 {
237 #ifdef OJPH_COMPILER_MSVC
238 unsigned long result = 0;
239 #if (defined OJPH_ARCH_X86_64) || (defined OJPH_ARCH_ARM)
240 _BitScanReverse64(&result, val);
241 #elif (defined OJPH_ARCH_I386)
242 ui32 msb = (ui32)(val >> 32), lsb = (ui32)val;
243 if (msb == 0)
244 _BitScanReverse(&result, lsb);
245 else {
246 _BitScanReverse(&result, msb);
247 result += 32;
248 }
249 #else
250 #error Error unsupport MSVC version
251 #endif
252 return 63 ^ (ui32)result;
253 #elif (defined OJPH_COMPILER_GNUC)
254 return (ui32)__builtin_clzll(val);
255 #else
256 val |= (val >> 1);
257 val |= (val >> 2);
258 val |= (val >> 4);
259 val |= (val >> 8);
260 val |= (val >> 16);
261 val |= (val >> 32);
262 return 64 - population_count64(val);
263 #endif
264 }
265
267#ifdef OJPH_COMPILER_MSVC
268 #pragma intrinsic(_BitScanForward)
269#endif
270 static inline ui32 count_trailing_zeros(ui32 val)
271 {
272 #ifdef OJPH_COMPILER_MSVC
273 unsigned long result = 0;
274 _BitScanForward(&result, val);
275 return (ui32)result;
276 #elif (defined OJPH_COMPILER_GNUC)
277 return (ui32)__builtin_ctz(val);
278 #else
279 val |= (val << 1);
280 val |= (val << 2);
281 val |= (val << 4);
282 val |= (val << 8);
283 val |= (val << 16);
284 return 32 - population_count(val);
285 #endif
286 }
287
289#ifdef OJPH_COMPILER_MSVC
290 #pragma intrinsic(_BitScanForward64)
291#endif
292 static inline ui32 count_trailing_zeros(ui64 val)
293 {
294 #ifdef OJPH_COMPILER_MSVC
295 unsigned long result = 0;
296 #if (defined OJPH_ARCH_X86_64) || (defined OJPH_ARCH_ARM)
297 _BitScanForward64(&result, val);
298 #elif (defined OJPH_ARCH_I386)
299 ui32 lsb = (ui32)val, msb = (ui32)(val >> 32);
300 if (lsb != 0)
301 _BitScanForward(&result, lsb);
302 else {
303 _BitScanForward(&result, msb);
304 result += 32;
305 }
306 #endif
307 return (ui32)result;
308 #elif (defined OJPH_COMPILER_GNUC)
309 return (ui32)__builtin_ctzll(val);
310 #else
311 if ((ui32)val != 0)
312 return count_trailing_zeros((ui32)val);
313 return 32 + count_trailing_zeros((ui32)(val >> 32));
314 #endif
315 }
316
318 static inline si32 ojph_round(float val)
319 {
320 #ifdef OJPH_COMPILER_MSVC
321 return (si32)(val + (val >= 0.0f ? 0.5f : -0.5f));
322 #elif (defined OJPH_COMPILER_GNUC)
323 return (si32)(val + (val >= 0.0f ? 0.5f : -0.5f));
324 #else
325 return (si32)round(val);
326 #endif
327 }
328
330 static inline si32 ojph_trunc(float val)
331 {
332 #ifdef OJPH_COMPILER_MSVC
333 return (si32)(val);
334 #elif (defined OJPH_COMPILER_GNUC)
335 return (si32)(val);
336 #else
337 return (si32)trunc(val);
338 #endif
339 }
340
342 // constants
344 #ifndef OJPH_EMSCRIPTEN
345 const ui32 byte_alignment = 64; // 64 bytes == 512 bits
348 #else
349 const ui32 byte_alignment = 16; // 16 bytes == 128 bits
351 const ui32 object_alignment = 8;
352 #endif
353
355 // templates for alignment
357
359 // finds the size such that it is a multiple of byte_alignment
360 template <typename T, ui32 N>
361 size_t calc_aligned_size(size_t size) {
362 size = size * sizeof(T) + N - 1;
363 size &= ~((1ULL << (31 - count_leading_zeros(N))) - 1);
364 size >>= (63 - count_leading_zeros((ui64)sizeof(T)));
365 return size;
366 }
367
369 // moves the pointer to first address that is a multiple of byte_alignment
370 template <typename T, ui32 N>
371 inline T *align_ptr(T *ptr) {
372 intptr_t p = reinterpret_cast<intptr_t>(ptr);
373 p += N - 1;
374 p &= ~((1ULL << (31 - count_leading_zeros(N))) - 1);
375 return reinterpret_cast<T *>(p);
376 }
377
379 // Determine the byte order of the target at compile time when possible,
380 // so that the compiler can remove the branches for the other byte order.
381 // __BYTE_ORDER__ is a predefined macro that describes the target
382 // architecture, not the machine running the compiler, so it is also
383 // correct when cross-compiling.
384 // All MSVC targets (x86, x64, ARM64 Windows) are little endian.
385#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
386 constexpr bool is_machine_little_endian = false;
387#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
388 constexpr bool is_machine_little_endian = true;
389#elif defined(OJPH_COMPILER_MSVC)
390 constexpr bool is_machine_little_endian = true;
391#else
392 // fallback in case macro __BYTE_ORDER__ is not defined
393 // If the first byte in memory is 0x01, the machine is Little Endian.
394 // If the first byte in memory is 0x00, the machine is Big Endian.
396 {
397 const uint16_t n = 0x0001;
398 bool is_machine_little_endian = (*((uint8_t *)&n) == 0x01);
400 }
402#endif
403
405 // swap bytes 1 2 --> 2 1 on big-endian machines
406 static inline ui16 swap_bytes_if_be(ui16 t)
407 {
409 return t;
410 else
411 return (ui16)((t << 8) | (t >> 8));
412 }
413
414 // swap bytes 1 2 --> 2 1 on little-endian machines
415 static inline ui16 swap_bytes_if_le(ui16 t)
416 {
418 return (ui16)((t << 8) | (t >> 8));
419 else
420 return t;
421 }
422
423 // swap bytes 1 2 3 4 --> 4 3 2 1 on big-endian machines
424 static inline ui32 swap_bytes_if_be(ui32 t)
425 {
427 return t;
428 else
429 {
430 ui32 u = swap_bytes_if_be((ui16)(t & 0xFFFFu));
431 u <<= 16;
432 u |= swap_bytes_if_be((ui16)(t >> 16));
433 return u;
434 }
435 }
436
437 // swap bytes 1 2 3 4 --> 4 3 2 1 on little-endian machines
438 static inline ui32 swap_bytes_if_le(ui32 t)
439 {
441 {
442 ui32 u = swap_bytes_if_le((ui16)(t & 0xFFFFu));
443 u <<= 16;
444 u |= swap_bytes_if_le((ui16)(t >> 16));
445 return u;
446 }
447 else
448 return t;
449 }
450
451 // swap bytes 1 2 3 4 5 6 7 8 --> 8 7 6 5 4 3 2 1 on little-endian machines
452 static inline ui64 swap_bytes_if_le(ui64 t)
453 {
455 {
456 ui64 u =
457 swap_bytes_if_le((ui32)(t & 0xFFFFFFFFu));
458 u <<= 32;
459 u |= swap_bytes_if_le((ui32)(t >> 32));
460 return u;
461 }
462 else
463 return t;
464 }
465
467 // loads 4 bytes from p as a little-endian 32-bit integer; that is, the
468 // byte at the lowest address goes into the least-significant byte of the
469 // result, irrespective of the machine's endianness
470 static inline ui32 load_le_ui32(const ui8 *p)
471 {
473 ui32 val;
474 std::memcpy(&val, p, sizeof(val));
475 return val;
476 }
477 else
478 return (ui32)p[0] | ((ui32)p[1] << 8)
479 | ((ui32)p[2] << 16) | ((ui32)p[3] << 24);
480 }
481
483 // loads two consecutive ui16 values from p, placing the one at the lower
484 // address in the least-significant 16 bits of the result, irrespective
485 // of the machine's endianness
486 static inline ui32 load_le_ui16x2(const ui16 *p)
487 {
489 ui32 val;
490 std::memcpy(&val, p, sizeof(val));
491 return val;
492 }
493 else
494 return (ui32)p[0] | ((ui32)p[1] << 16);
495 }
496}
497
498#endif // !OJPH_ARCH_H
const ui32 object_alignment
Definition ojph_arch.h:347
static ui16 swap_bytes_if_le(ui16 t)
Definition ojph_arch.h:415
@ ARM_CPU_EXT_LEVEL_SVE
Definition ojph_arch.h:172
@ ARM_CPU_EXT_LEVEL_SVE2
Definition ojph_arch.h:173
@ ARM_CPU_EXT_LEVEL_NEON
Definition ojph_arch.h:170
@ ARM_CPU_EXT_LEVEL_GENERIC
Definition ojph_arch.h:169
@ ARM_CPU_EXT_LEVEL_ASIMD
Definition ojph_arch.h:171
const ui32 byte_alignment
Definition ojph_arch.h:345
const bool is_machine_little_endian
Definition ojph_arch.h:401
uint64_t ui64
Definition ojph_defs.h:56
static si32 ojph_round(float val)
Definition ojph_arch.h:318
static bool check_if_machine_is_little_endian()
Definition ojph_arch.h:395
static ui32 load_le_ui16x2(const ui16 *p)
Definition ojph_arch.h:486
static ui32 load_le_ui32(const ui8 *p)
Definition ojph_arch.h:470
size_t calc_aligned_size(size_t size)
Definition ojph_arch.h:361
uint16_t ui16
Definition ojph_defs.h:52
T * align_ptr(T *ptr)
Definition ojph_arch.h:371
@ PPC_CPU_EXT_LEVEL_ARCH_3_1
Definition ojph_arch.h:182
@ PPC_CPU_EXT_LEVEL_GENERIC
Definition ojph_arch.h:179
@ PPC_CPU_EXT_LEVEL_ARCH_2_07
Definition ojph_arch.h:180
@ PPC_CPU_EXT_LEVEL_ARCH_3_00
Definition ojph_arch.h:181
static ui32 population_count(ui32 val)
Definition ojph_arch.h:186
static ui16 swap_bytes_if_be(ui16 t)
Definition ojph_arch.h:406
OJPH_EXPORT int get_cpu_ext_level()
static si32 ojph_trunc(float val)
Definition ojph_arch.h:330
static ui32 count_trailing_zeros(ui32 val)
Definition ojph_arch.h:270
static ui32 count_leading_zeros(ui32 val)
Definition ojph_arch.h:207
int32_t si32
Definition ojph_defs.h:55
const ui32 log_byte_alignment
Definition ojph_arch.h:346
uint32_t ui32
Definition ojph_defs.h:54
uint8_t ui8
Definition ojph_defs.h:50
@ X86_CPU_EXT_LEVEL_AVX2
Definition ojph_arch.h:163
@ X86_CPU_EXT_LEVEL_AVX
Definition ojph_arch.h:162
@ X86_CPU_EXT_LEVEL_AVX512
Definition ojph_arch.h:165
@ X86_CPU_EXT_LEVEL_GENERIC
Definition ojph_arch.h:154
@ X86_CPU_EXT_LEVEL_SSE2
Definition ojph_arch.h:157
@ X86_CPU_EXT_LEVEL_SSE41
Definition ojph_arch.h:160
@ X86_CPU_EXT_LEVEL_SSE
Definition ojph_arch.h:156
@ X86_CPU_EXT_LEVEL_MMX
Definition ojph_arch.h:155
@ X86_CPU_EXT_LEVEL_SSE42
Definition ojph_arch.h:161
@ X86_CPU_EXT_LEVEL_SSSE3
Definition ojph_arch.h:159
@ X86_CPU_EXT_LEVEL_SSE3
Definition ojph_arch.h:158
@ X86_CPU_EXT_LEVEL_AVX2FMA
Definition ojph_arch.h:164
#define OJPH_EXPORT
Definition ojph_arch.h:144