native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.x86.wait.ccm
1// SPDX-FileCopyrightText: 2024 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3module;
4#include "native/isa_import.h"
5#include <cstdint>
6#include <x86intrin.h>
7#include "native/attributes.h"
8#include "native/targets.h"
9
10export module native.x86.wait;
11export import native.x86.features;
12
13export namespace native {
18
23
27
30 template <typename T>
31 concept waiter = requires (volatile void * p, std::uint32_t t) {
32 bool(T::supported);
33 T::monitor(p);
34 T::mwait(t);
35 };
36
46 template <waiter W> native_flatten
47 void wait_until(volatile auto * p, auto f)
48 noexcept(noexcept(!f(p)) && noexcept(W::monitor(p)) && noexcept(W::mwait(0))) native_blocking {
49 [[assume(W::supported)]];
50 while (!f(p)) {
51 W::monitor(p);
52 if (!f(p))
53 W::mwait(0);
54 }
55 }
56
58 struct mwaitx {
59 using timer_t = std::uint64_t;
62 [[gnu::target("mwaitx")]] static void monitor(volatile void * p) noexcept;
65 [[gnu::target("mwaitx")]] static void mwait(std::uint32_t timer = 0) noexcept native_blocking;
67 static const bool supported;
68 };
69
70
72 struct umwait {
73 using timer_t = std::uint64_t;
76 [[gnu::target("waitpkg")]] static void monitor(volatile void * p) noexcept;
79 [[gnu::target("waitpkg")]] static std::uint8_t mwait(std::uint64_t timer = 0) noexcept native_blocking;
81 static const bool supported;
82 };
83
88 template<isa<x86> Arch = NATIVE_BASELINE, unsigned Control = 1>
89 requires(Arch.has(x86_feature::waitpkg) && Control < 2)
91 std::uint8_t tpause(std::uint64_t deadline) noexcept native_blocking {
92 return __builtin_ia32_tpause(Control, std::uint32_t(deadline >> 32),
93 std::uint32_t(deadline));
94 }
95
97 struct spin {
98 using timer_t = std::uint64_t;
100 native_inline native_artificial static void monitor(volatile void *) noexcept {}
102 native_inline native_artificial static void mwait(std::uint32_t = 0) noexcept native_blocking { _mm_pause(); }
104 inline static constinit bool supported = true;
105 };
106
115 auto with_waiter(auto k)
116 noexcept(noexcept(k.template operator()<mwaitx>()) &&
117 noexcept(k.template operator()<umwait>()) && noexcept(k.template operator()<spin>())) {
118 if (mwaitx::supported) return k.template operator()<mwaitx>();
119 else if (umwait::supported) return k.template operator()<umwait>();
120 else return k.template operator()<spin>();
121 }
122
124}
125
126namespace native {
127 // Out-of-line module-owned leaves avoid exposing the intrinsic headers'
128 // TU-local inline functions through a BMI, and preserve baseline callers.
129 [[gnu::target("mwaitx")]] void mwaitx::monitor(volatile void * p) noexcept {
130 _mm_monitorx(const_cast<void *>(p), 0, 0);
131 }
132 [[gnu::target("mwaitx")]] void mwaitx::mwait(std::uint32_t timer) noexcept native_blocking {
133 _mm_mwaitx(0, 0, timer);
134 }
135 [[gnu::target("waitpkg")]] void umwait::monitor(volatile void * p) noexcept {
136 _umonitor(const_cast<void *>(p));
137 }
138 [[gnu::target("waitpkg")]] std::uint8_t umwait::mwait(std::uint64_t timer) noexcept native_blocking {
139 return _umwait(1, timer);
140 }
141
142 namespace {
143 // Share the common observer and admission rules, including leaf validity.
144 // Waiting needs no vector state or unrelated SIMD instruction features.
145 const auto wait_capabilities = observe_x86_capabilities();
146 }
147 const bool mwaitx::supported = classify_isa(wait_capabilities, x86_feature::mwaitx).admitted();
148 const bool umwait::supported = classify_isa(wait_capabilities, x86_feature::waitpkg).admitted();
149}
Compiler attributes for host code, with shader-safe shared modifiers.
some way to wait for a value to change
#define native_artificial
[[artificial]].
Definition attributes.h:245
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_flatten
portable [[flatten]]
Definition attributes.h:228
#define native_blocking
Declares a function potentially blocks. Prevents inference of native_nonblocking.
#define native_target(x)
this indicates a required feature set for the current multiversioned function.
Definition attributes.h:476
void wait_until(volatile auto *p, auto f) noexcept(noexcept(!f(p)) &&noexcept(W::monitor(p)) &&noexcept(W::mwait(0))) native_blocking
Wait until a predicate holds about a given memory location.
std::uint8_t tpause(std::uint64_t deadline) noexcept native_blocking
auto with_waiter(auto k) noexcept(noexcept(k.template operator()<mwaitx >()) &&noexcept(k.template operator()<umwait >()) &&noexcept(k.template operator()<spin >()))
finds an appropriate waiter for the current CPU Prefer MWAITX, then WAITPKG, otherwise PAUSE....
static void mwait(std::uint32_t timer=0) noexcept native_blocking
static void monitor(volatile void *p) noexcept
static void monitor(volatile void *p) noexcept
static std::uint8_t mwait(std::uint64_t timer=0) noexcept native_blocking
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
x86_capabilities observe_x86_capabilities() noexcept
Capture CPUID and, only with XSAVE and OSXSAVE, XCR0. No ISA profile required.
Standard-library adaptations documented here for SIMD value types.
waiter using the MONITORX/MWAITX instruction feature
A universally supported x86 spin waiter using PAUSE; monitor is a no-op.
static void mwait(std::uint32_t=0) noexcept native_blocking
Issue one PAUSE, ignoring the timer; no sleep duration or memory ordering is promised.
static void monitor(volatile void *) noexcept
Ignore the address; PAUSE waiting does not arm a hardware monitor.
waiter using the WAITPKG instruction feature