native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.x86.features.ccm
1// SPDX-FileCopyrightText: 2024 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3module;
4#include <array>
5#include <cstdint>
6#include <string_view>
7#include "native/attributes.h"
8#if defined(_MSC_VER)
9#include <intrin.h>
10#else
11#include <cpuid.h>
12#endif
13
14export module native.x86.features;
15export import native.isa;
16
17export namespace native {
23
27 std::int32_t eax, ebx, ecx, edx;
28 };
29
35 cpuid_t cpuid(std::int32_t eax, std::int32_t ecx) noexcept;
36
40 enum class native_nodiscard cpu_vendor : std::uint8_t {
41 intel = 0,
42 amd = 1,
44 };
45
46
55 std::uint64_t xcr0 = 0;
57 bool xcr0_observed = false;
60 std::uint32_t max_basic_leaf = 0;
61 std::uint32_t leaf1_ecx = 0, leaf1_edx = 0, leaf7_ebx = 0;
64 std::uint32_t max_leaf7_subleaf = 0;
67 std::uint32_t leaf7_1_eax = 0;
69 std::uint32_t leaf7_edx = 0;
73 std::uint32_t leaf7_ecx = 0;
76 std::uint32_t max_extended_leaf = 0, extended1_ecx = 0;
79 std::uint32_t leaf7_1_edx = 0;
80 };
81
84 std::array<char,13> vendor_id{};
87 };
88
91
93 extern const enum cpu_vendor cpu_vendor;
94}
95
96namespace native {
97 namespace detail {
98 constexpr std::array<char,13> vendor_identifier(cpuid_t leaf) noexcept {
99 std::array<char,13> result{};
100 std::uint32_t words[]{std::uint32_t(leaf.ebx),std::uint32_t(leaf.edx),std::uint32_t(leaf.ecx)};
101 for(unsigned word=0;word!=3;++word)
102 for(unsigned byte=0;byte!=4;++byte)
103 result[4*word+byte]=char((words[word]>>(8*byte))&0xff);
104 return result;
105 }
106 constexpr enum cpu_vendor identify_vendor(std::array<char,13> const & id) noexcept {
107 std::string_view name{id.data(),12};
108 if(name=="GenuineIntel") return cpu_vendor::intel;
109 if(name=="AuthenticAMD") return cpu_vendor::amd;
110 return cpu_vendor::unknown;
111 }
112 static_assert(identify_vendor(vendor_identifier({0,0x756e6547,0x6c65746e,0x49656e69}))==cpu_vendor::intel);
113 static_assert(identify_vendor(vendor_identifier({0,0x68747541,0x444d4163,0x69746e65}))==cpu_vendor::amd);
114 static_assert(identify_vendor(vendor_identifier({}))==cpu_vendor::unknown);
115 }
116
117 cpuid_t cpuid(std::int32_t eax, std::int32_t ecx) noexcept {
118#if defined(_MSC_VER)
119 int registers[4];
120 __cpuidex(registers, eax, ecx);
121 return {registers[0], registers[1], registers[2], registers[3]};
122#else
123 cpuid_t result;
124 __cpuid_count(eax, ecx, result.eax, result.ebx, result.ecx, result.edx);
125 return result;
126#endif
127 }
128
129
131 x86_capabilities result;
132 auto identity=cpuid(0,0);
133 result.raw.max_basic_leaf = std::uint32_t(identity.eax);
134 result.vendor_id=detail::vendor_identifier(identity);
135 result.vendor=detail::identify_vendor(result.vendor_id);
136 if (result.raw.max_basic_leaf >= 1) {
137 auto leaf = cpuid(1, 0);
138 result.raw.leaf1_ecx = std::uint32_t(leaf.ecx);
139 result.raw.leaf1_edx = std::uint32_t(leaf.edx);
140 constexpr auto xsave = (1u << 26) | (1u << 27);
141 if ((result.raw.leaf1_ecx & xsave) == xsave) {
142#if defined(_MSC_VER)
143 result.xcr0 = _xgetbv(0);
144#else
145 std::uint32_t low, high;
146 __asm__("xgetbv" : "=a"(low), "=d"(high) : "c"(0));
147 result.xcr0 = std::uint64_t(low) | (std::uint64_t(high) << 32);
148#endif
149 result.xcr0_observed = true;
150 }
151 }
152 if (result.raw.max_basic_leaf >= 7) {
153 auto leaf = cpuid(7, 0);
154 result.raw.leaf7_ebx = std::uint32_t(leaf.ebx);
155 result.raw.leaf7_ecx = std::uint32_t(leaf.ecx);
156 result.raw.leaf7_edx = std::uint32_t(leaf.edx);
157 result.raw.max_leaf7_subleaf = std::uint32_t(leaf.eax);
158 if (result.raw.max_leaf7_subleaf >= 1) {
159 auto subleaf = cpuid(7, 1);
160 result.raw.leaf7_1_eax = std::uint32_t(subleaf.eax);
161 result.raw.leaf7_1_edx = std::uint32_t(subleaf.edx);
162 }
163 }
164 result.raw.max_extended_leaf=std::uint32_t(cpuid(std::int32_t(0x80000000u),0).eax);
165 if(result.raw.max_extended_leaf>=0x80000001u)
166 result.raw.extended1_ecx=std::uint32_t(cpuid(std::int32_t(0x80000001u),0).ecx);
167 auto features=detail::decode_x86_features(result.raw);
168 result.present=features.present;
169 result.observed=features.observed;
170 return result;
171 }
172
173 const enum cpu_vendor cpu_vendor = [] static noexcept {
174 return detail::identify_vendor(detail::vendor_identifier(cpuid(0,0)));
175 }();
176}
Compiler attributes for host code, with shader-safe shared modifiers.
cpuid_t cpuid(std::int32_t eax, std::int32_t ecx) noexcept
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
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.
cpu_vendor
Recognized CPUID vendor strings; vendor identity alone does not imply features.
@ intel
CPUID reports GenuineIntel.
@ amd
CPUID reports AuthenticAMD.
@ unknown
Any other vendor string.
A structural set of one family's features, without prerequisite closure.
Definition isa.h:58
CPUID query inputs retained for diagnostics, not a second admission source.
std::uint32_t leaf7_edx
CPUID.7.0 EDX; bit 23 reports AVX512_FP16. Zero without leaf 7.
enum cpu_vendor vendor
Classification of vendor_id; it does not establish instruction support.
std::array< char, 13 > vendor_id
Exact CPUID vendor identifier, followed by a zero terminator.
std::uint64_t xcr0
OS-enabled vector state. Used only when xcr0_observed is true.
isa< x86 > present
Features whose queries succeeded and reported support; no compiler closure.
bool xcr0_observed
True after XSAVE/OSXSAVE permit XGETBV and its result was captured.
raw_observations raw
Diagnostic snapshot; editing it does not update present/observed or XCR0.
isa< x86 > observed
Features whose CPUID leaves were available, whether present or absent.