Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
scoped_flush_denormals.hpp
Go to the documentation of this file.
1#pragma once
2
48
49#include <cstdint>
50
51#if defined(__x86_64__) || defined(_M_X64)
52#define PULP_FLUSH_DENORMALS_X86 1
53#if defined(_MSC_VER)
54// MSVC requires the intrinsic declarations header before the SSE headers,
55// including when compiling the x64 slice of an ARM64EC translation unit.
56#include <intrin.h>
57#endif
58#include <pmmintrin.h> // DAZ (SSE3 intrinsics header; also pulls SSE)
59#include <xmmintrin.h> // FTZ + MXCSR access
60#elif (defined(__aarch64__) || defined(_M_ARM64)) && defined(__GNUC__)
61#define PULP_FLUSH_DENORMALS_ARM64 1
62#endif
63
64namespace pulp::signal {
65namespace detail {
66
67#if defined(PULP_FLUSH_DENORMALS_X86)
68// FTZ (bit 15) only — universally supported on SSE and never faults. DAZ
69// (bit 6) is deliberately omitted; see the header doc comment.
70inline constexpr unsigned int kMxcsrFtz = 0x8000u; // flush-to-zero (results)
71#elif defined(PULP_FLUSH_DENORMALS_ARM64)
72inline constexpr std::uint64_t kFpcrFz = std::uint64_t(1) << 24; // FPCR.FZ
73
74inline std::uint64_t read_fpcr() noexcept {
75 std::uint64_t fpcr = 0;
76 __asm__ __volatile__("mrs %0, fpcr" : "=r"(fpcr));
77 return fpcr;
78}
79inline void write_fpcr(std::uint64_t fpcr) noexcept {
80 __asm__ __volatile__("msr fpcr, %0" : : "r"(fpcr));
81}
82#endif
83
84} // namespace detail
85
88#if defined(PULP_FLUSH_DENORMALS_X86) || defined(PULP_FLUSH_DENORMALS_ARM64)
89inline constexpr bool kHardwareFlushSupported = true;
90#else
91inline constexpr bool kHardwareFlushSupported = false;
92#endif
93
96inline bool denormals_are_flushed() noexcept {
97#if defined(PULP_FLUSH_DENORMALS_X86)
98 return (_mm_getcsr() & detail::kMxcsrFtz) != 0;
99#elif defined(PULP_FLUSH_DENORMALS_ARM64)
100 return (detail::read_fpcr() & detail::kFpcrFz) != 0;
101#else
102 return false;
103#endif
104}
105
107 public:
108 // Control-register writes are not free on the audio path, and many hosts
109 // already run their audio threads with flush-to-zero on. Each write is
110 // therefore made only when it changes the register; the observable
111 // contract (FTZ on inside the scope, the caller's exact value restored on
112 // exit) is unchanged.
114#if defined(PULP_FLUSH_DENORMALS_X86)
115 saved_csr_ = _mm_getcsr();
116 if ((saved_csr_ & detail::kMxcsrFtz) == 0)
117 _mm_setcsr(saved_csr_ | detail::kMxcsrFtz);
118#elif defined(PULP_FLUSH_DENORMALS_ARM64)
119 saved_fpcr_ = detail::read_fpcr();
120 if ((saved_fpcr_ & detail::kFpcrFz) == 0)
121 detail::write_fpcr(saved_fpcr_ | detail::kFpcrFz);
122#endif
123 }
124
126#if defined(PULP_FLUSH_DENORMALS_X86)
127 if (_mm_getcsr() != saved_csr_)
128 _mm_setcsr(saved_csr_);
129#elif defined(PULP_FLUSH_DENORMALS_ARM64)
130 if (detail::read_fpcr() != saved_fpcr_)
131 detail::write_fpcr(saved_fpcr_);
132#endif
133 }
134
139
140private:
141#if defined(PULP_FLUSH_DENORMALS_X86)
142 unsigned int saved_csr_ = 0;
143#elif defined(PULP_FLUSH_DENORMALS_ARM64)
144 std::uint64_t saved_fpcr_ = 0;
145#endif
146};
147
148} // namespace pulp::signal
ScopedFlushDenormals & operator=(const ScopedFlushDenormals &)=delete
ScopedFlushDenormals(const ScopedFlushDenormals &)=delete
ScopedFlushDenormals & operator=(ScopedFlushDenormals &&)=delete
~ScopedFlushDenormals() noexcept
Definition scoped_flush_denormals.hpp:125
ScopedFlushDenormals() noexcept
Definition scoped_flush_denormals.hpp:113
ScopedFlushDenormals(ScopedFlushDenormals &&)=delete
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
Definition additive_bank.hpp:218
constexpr bool kHardwareFlushSupported
True when this build sets the hardware flush-to-zero mode.
Definition scoped_flush_denormals.hpp:91
bool denormals_are_flushed() noexcept
Definition scoped_flush_denormals.hpp:96