Skip to content

Commit 838f928

Browse files
committed
use macros to support larger flag ranges
1 parent e6d4583 commit 838f928

6 files changed

Lines changed: 119 additions & 138 deletions

File tree

Include/internal/pycore_cpuinfo_cpuid_features.h

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -39,55 +39,59 @@ extern "C" {
3939
import os, sys
4040
sys.path.insert(0, os.path.realpath(os.path.join(os.getcwd(), "Tools")))
4141
from cpuinfo.cpuid_features_gen import generate_cpuid_features_enum
42-
print(generate_cpuid_features_enum("_Py_cpuid_feature_mask"))
42+
print(generate_cpuid_features_enum())
4343
[python start generated code]*/
44-
// fmt: off
45-
/** Enumeration for CPUID features */
46-
enum _Py_cpuid_feature_mask_e {
47-
/* CPUID (LEAF=1, SUBLEAF=0) [ECX] */
48-
_Py_CPUID_MASK_ECX_L1_SSE3 = 0x00000001, // bit = 0
49-
_Py_CPUID_MASK_ECX_L1_PCLMULQDQ = 0x00000002, // bit = 1
50-
_Py_CPUID_MASK_ECX_L1_SSSE3 = 0x00000200, // bit = 9
51-
_Py_CPUID_MASK_ECX_L1_FMA = 0x00001000, // bit = 12
52-
_Py_CPUID_MASK_ECX_L1_SSE4_1 = 0x00080000, // bit = 19
53-
_Py_CPUID_MASK_ECX_L1_SSE4_2 = 0x00100000, // bit = 20
54-
_Py_CPUID_MASK_ECX_L1_POPCNT = 0x00800000, // bit = 23
55-
_Py_CPUID_MASK_ECX_L1_XSAVE = 0x04000000, // bit = 26
56-
_Py_CPUID_MASK_ECX_L1_OSXSAVE = 0x08000000, // bit = 27
57-
_Py_CPUID_MASK_ECX_L1_AVX = 0x10000000, // bit = 28
58-
/* CPUID (LEAF=1, SUBLEAF=0) [EDX] */
59-
_Py_CPUID_MASK_EDX_L1_CMOV = 0x00008000, // bit = 15
60-
_Py_CPUID_MASK_EDX_L1_SSE = 0x02000000, // bit = 25
61-
_Py_CPUID_MASK_EDX_L1_SSE2 = 0x04000000, // bit = 26
62-
/* CPUID (LEAF=7, SUBLEAF=0) [EBX] */
63-
_Py_CPUID_MASK_EBX_L7_AVX2 = 0x00000020, // bit = 5
64-
_Py_CPUID_MASK_EBX_L7_AVX512_F = 0x00010000, // bit = 16
65-
_Py_CPUID_MASK_EBX_L7_AVX512_DQ = 0x00020000, // bit = 17
66-
_Py_CPUID_MASK_EBX_L7_AVX512_IFMA = 0x00200000, // bit = 21
67-
_Py_CPUID_MASK_EBX_L7_AVX512_PF = 0x04000000, // bit = 26
68-
_Py_CPUID_MASK_EBX_L7_AVX512_ER = 0x08000000, // bit = 27
69-
_Py_CPUID_MASK_EBX_L7_AVX512_CD = 0x10000000, // bit = 28
70-
_Py_CPUID_MASK_EBX_L7_AVX512_BW = 0x40000000, // bit = 30
71-
_Py_CPUID_MASK_EBX_L7_AVX512_VL = 0x80000000, // bit = 31
72-
/* CPUID (LEAF=7, SUBLEAF=0) [ECX] */
73-
_Py_CPUID_MASK_ECX_L7_AVX512_VBMI = 0x00000002, // bit = 1
74-
_Py_CPUID_MASK_ECX_L7_AVX512_VBMI2 = 0x00000040, // bit = 6
75-
_Py_CPUID_MASK_ECX_L7_AVX512_VNNI = 0x00000800, // bit = 11
76-
_Py_CPUID_MASK_ECX_L7_AVX512_BITALG = 0x00001000, // bit = 12
77-
_Py_CPUID_MASK_ECX_L7_AVX512_VPOPCNTDQ = 0x00004000, // bit = 14
78-
/* CPUID (LEAF=7, SUBLEAF=0) [EDX] */
79-
_Py_CPUID_MASK_EDX_L7_AVX512_4VNNIW = 0x00000004, // bit = 2
80-
_Py_CPUID_MASK_EDX_L7_AVX512_4FMAPS = 0x00000008, // bit = 3
81-
_Py_CPUID_MASK_EDX_L7_AVX512_VP2INTERSECT = 0x00000100, // bit = 8
82-
/* CPUID (LEAF=7, SUBLEAF=1) [EAX] */
83-
_Py_CPUID_MASK_EAX_L7S1_AVX_VNNI = 0x00000010, // bit = 4
84-
_Py_CPUID_MASK_EAX_L7S1_AVX_IFMA = 0x00800000, // bit = 23
85-
/* CPUID (LEAF=7, SUBLEAF=1) [EDX] */
86-
_Py_CPUID_MASK_EDX_L7S1_AVX_VNNI_INT8 = 0x00000010, // bit = 4
87-
_Py_CPUID_MASK_EDX_L7S1_AVX_NE_CONVERT = 0x00000020, // bit = 5
88-
_Py_CPUID_MASK_EDX_L7S1_AVX_VNNI_INT16 = 0x00000400, // bit = 10
89-
};
90-
// fmt: on
91-
/*[python end generated code: output=8e58b0997d69bbf8 input=fce00935f64021f9]*/
44+
// clang-format off
45+
/** Constants for CPUID features */
46+
/* CPUID (LEAF=1, SUBLEAF=0) [ECX] */
47+
#define _Py_CPUID_MASK_ECX_L1_SSE3 0x00000001 // bit = 0
48+
#define _Py_CPUID_MASK_ECX_L1_PCLMULQDQ 0x00000002 // bit = 1
49+
#define _Py_CPUID_MASK_ECX_L1_SSSE3 0x00000200 // bit = 9
50+
#define _Py_CPUID_MASK_ECX_L1_FMA 0x00001000 // bit = 12
51+
#define _Py_CPUID_MASK_ECX_L1_SSE4_1 0x00080000 // bit = 19
52+
#define _Py_CPUID_MASK_ECX_L1_SSE4_2 0x00100000 // bit = 20
53+
#define _Py_CPUID_MASK_ECX_L1_POPCNT 0x00800000 // bit = 23
54+
#define _Py_CPUID_MASK_ECX_L1_XSAVE 0x04000000 // bit = 26
55+
#define _Py_CPUID_MASK_ECX_L1_OSXSAVE 0x08000000 // bit = 27
56+
#define _Py_CPUID_MASK_ECX_L1_AVX 0x10000000 // bit = 28
57+
58+
/* CPUID (LEAF=1, SUBLEAF=0) [EDX] */
59+
#define _Py_CPUID_MASK_EDX_L1_CMOV 0x00008000 // bit = 15
60+
#define _Py_CPUID_MASK_EDX_L1_SSE 0x02000000 // bit = 25
61+
#define _Py_CPUID_MASK_EDX_L1_SSE2 0x04000000 // bit = 26
62+
63+
/* CPUID (LEAF=7, SUBLEAF=0) [EBX] */
64+
#define _Py_CPUID_MASK_EBX_L7_AVX2 0x00000020 // bit = 5
65+
#define _Py_CPUID_MASK_EBX_L7_AVX512_F 0x00010000 // bit = 16
66+
#define _Py_CPUID_MASK_EBX_L7_AVX512_DQ 0x00020000 // bit = 17
67+
#define _Py_CPUID_MASK_EBX_L7_AVX512_IFMA 0x00200000 // bit = 21
68+
#define _Py_CPUID_MASK_EBX_L7_AVX512_PF 0x04000000 // bit = 26
69+
#define _Py_CPUID_MASK_EBX_L7_AVX512_ER 0x08000000 // bit = 27
70+
#define _Py_CPUID_MASK_EBX_L7_AVX512_CD 0x10000000 // bit = 28
71+
#define _Py_CPUID_MASK_EBX_L7_AVX512_BW 0x40000000 // bit = 30
72+
#define _Py_CPUID_MASK_EBX_L7_AVX512_VL 0x80000000 // bit = 31
73+
74+
/* CPUID (LEAF=7, SUBLEAF=0) [ECX] */
75+
#define _Py_CPUID_MASK_ECX_L7_AVX512_VBMI 0x00000002 // bit = 1
76+
#define _Py_CPUID_MASK_ECX_L7_AVX512_VBMI2 0x00000040 // bit = 6
77+
#define _Py_CPUID_MASK_ECX_L7_AVX512_VNNI 0x00000800 // bit = 11
78+
#define _Py_CPUID_MASK_ECX_L7_AVX512_BITALG 0x00001000 // bit = 12
79+
#define _Py_CPUID_MASK_ECX_L7_AVX512_VPOPCNTDQ 0x00004000 // bit = 14
80+
81+
/* CPUID (LEAF=7, SUBLEAF=0) [EDX] */
82+
#define _Py_CPUID_MASK_EDX_L7_AVX512_4VNNIW 0x00000004 // bit = 2
83+
#define _Py_CPUID_MASK_EDX_L7_AVX512_4FMAPS 0x00000008 // bit = 3
84+
#define _Py_CPUID_MASK_EDX_L7_AVX512_VP2INTERSECT 0x00000100 // bit = 8
85+
86+
/* CPUID (LEAF=7, SUBLEAF=1) [EAX] */
87+
#define _Py_CPUID_MASK_EAX_L7S1_AVX_VNNI 0x00000010 // bit = 4
88+
#define _Py_CPUID_MASK_EAX_L7S1_AVX_IFMA 0x00800000 // bit = 23
89+
90+
/* CPUID (LEAF=7, SUBLEAF=1) [EDX] */
91+
#define _Py_CPUID_MASK_EDX_L7S1_AVX_VNNI_INT8 0x00000010 // bit = 4
92+
#define _Py_CPUID_MASK_EDX_L7S1_AVX_NE_CONVERT 0x00000020 // bit = 5
93+
#define _Py_CPUID_MASK_EDX_L7S1_AVX_VNNI_INT16 0x00000400 // bit = 10
94+
// clang-format on
95+
/*[python end generated code: output=e9112f064e2effec input=d7df15fec9f3daa2]*/
9296

9397
#endif // !Py_INTERNAL_CPUINFO_CPUID_FEATURES_H

Include/internal/pycore_cpuinfo_xsave_features.h

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,20 @@ extern "C" {
2020

2121
#include "Python.h"
2222

23-
// fmt: off
2423
/*[python input]
2524
import os, sys
2625
sys.path.insert(0, os.path.realpath(os.path.join(os.getcwd(), "Tools")))
2726
from cpuinfo.xsave_features_gen import generate_xsave_features_enum
28-
print(generate_xsave_features_enum("_Py_xsave_feature_mask"))
27+
print(generate_xsave_features_enum())
2928
[python start generated code]*/
30-
// fmt: off
31-
/** Enumeration for XSAVE components */
32-
enum _Py_xsave_feature_mask_e {
33-
_Py_XSAVE_MASK_XCR0_SSE = 0x00000002, // bit = 1
34-
_Py_XSAVE_MASK_XCR0_AVX = 0x00000004, // bit = 2
35-
_Py_XSAVE_MASK_XCR0_AVX512_OPMASK = 0x00000020, // bit = 5
36-
_Py_XSAVE_MASK_XCR0_AVX512_ZMM_HI256 = 0x00000040, // bit = 6
37-
_Py_XSAVE_MASK_XCR0_AVX512_HI16_ZMM = 0x00000080, // bit = 7
38-
};
39-
// fmt: on
40-
/*[python end generated code: output=35ea9a165938f8ef input=336793a305515376]*/
29+
// clang-format off
30+
/** Constants for XSAVE components */
31+
#define _Py_XSAVE_MASK_XCR0_SSE 0x00000002 // bit = 1
32+
#define _Py_XSAVE_MASK_XCR0_AVX 0x00000004 // bit = 2
33+
#define _Py_XSAVE_MASK_XCR0_AVX512_OPMASK 0x00000020 // bit = 5
34+
#define _Py_XSAVE_MASK_XCR0_AVX512_ZMM_HI256 0x00000040 // bit = 6
35+
#define _Py_XSAVE_MASK_XCR0_AVX512_HI16_ZMM 0x00000080 // bit = 7
36+
// clang-format on
37+
/*[python end generated code: output=ac059b802b4317cb input=6323151855b3c9f0]*/
4138

4239
#endif // !Py_INTERNAL_CPUINFO_XSAVE_FEATURES_H

Tools/cpuinfo/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
This package provides functions to generate flags for CPUID and XSAVE.
3+
4+
The constants are macros generated by Argument Clinic as follows:
5+
6+
#define <MACRO_NAME><TAB> 0x<MASK><TAB> // bit = BIT
7+
^ ^
8+
9+
where ^ indicates a column that is a multiple of 4, <MASK> has
10+
exactly 8 characters and <BIT> has at most 2 characters.
11+
12+
A C enumeration is NOT generated as the largest member may not fit
13+
on an 'int', which is forbidden as ISO C restricts enumerator values
14+
to that range.
15+
"""

Tools/cpuinfo/_util.py

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import annotations
22

33
__all__ = [
4-
"next_block", "make_enum_name", "make_enum_member",
4+
"next_block", "make_constant",
55
"Style", "C99_STYLE", "C11_STYLE", "DOXYGEN_STYLE",
6-
"CWriter"
6+
"CWriter",
77
] # fmt: skip
88

99
import contextlib
@@ -13,28 +13,23 @@
1313

1414
if TYPE_CHECKING:
1515
from collections.abc import Iterator
16-
from typing import Any, Final
16+
from typing import Any, Final, Literal
1717

1818

1919
def next_block(w: int) -> int:
2020
"""Compute the smallest multiple of 4 strictly larger than *w*."""
2121
return ((w + 3) & ~0x03) if (w % 4) else (w + 4)
2222

2323

24-
_MASKSIZE: Final[int] = next_block(len("= 0x00000000,"))
24+
_MASKSIZE: Final[int] = next_block(len("0x00000000"))
2525

2626

27-
def make_enum_name(name: str) -> tuple[str, str]:
28-
if name.endswith("_e"):
29-
raise ValueError(f"enumeration must not end by '_e': {name!r}")
30-
return f"{name}_e", name # (enum name, typedef name)
31-
32-
33-
def make_enum_member(key: str, bit: int, name_maxsize: int) -> str:
27+
def make_constant(key: str, bit: int, name_maxsize: int) -> str:
28+
assert bit <= 32, f"{key}: mask does not on an uint32_t"
3429
member_name = key.ljust(name_maxsize)
3530
member_mask = format(1 << bit, "008x")
36-
member_mask = f"= 0x{member_mask},".ljust(_MASKSIZE)
37-
return f"{member_name}{member_mask} // bit = {bit}"
31+
member_mask = f"0x{member_mask}".ljust(_MASKSIZE)
32+
return f"#define {member_name}{member_mask}// bit = {bit}"
3833

3934

4035
class Style(enum.IntEnum):
@@ -43,9 +38,9 @@ class Style(enum.IntEnum):
4338
DOXYGEN = enum.auto()
4439

4540

46-
C99_STYLE = Style.C99
47-
C11_STYLE = Style.C11
48-
DOXYGEN_STYLE = Style.DOXYGEN
41+
C99_STYLE: Final[Literal[Style.C99]] = Style.C99
42+
C11_STYLE: Final[Literal[Style.C11]] = Style.C11
43+
DOXYGEN_STYLE: Final[Literal[Style.DOXYGEN]] = Style.DOXYGEN
4944

5045
_COMMENT_INLINE_STYLE: Final[dict[Style, tuple[str, str, str]]] = {
5146
C99_STYLE: ("// ", "", ""),
@@ -65,17 +60,6 @@ def __init__(self, *, indentsize: int = 4) -> None:
6560
self._stream = StringIO()
6661
self._indent = " " * indentsize
6762
self._prefix = ""
68-
self._disable_external_formatter()
69-
70-
def _disable_external_formatter(self) -> None:
71-
"""Add a directive to suppress external formatters to run."""
72-
with self.prefixed(""):
73-
self.write("// fmt: off")
74-
75-
def _enable_external_formatter(self) -> None:
76-
"""Add a directive to allow external formatters to run."""
77-
with self.prefixed(""):
78-
self.write("// fmt: on")
7963

8064
def comment(
8165
self, text: str, *, level: int = 0, style: Style = C11_STYLE
@@ -111,9 +95,18 @@ def write(
11195
self._write(prefix, sep="", end="")
11296
self._write(*args, sep=sep, end=end)
11397

114-
def _write(self, *args: Any, sep: str, end: str) -> None:
98+
def write_blankline(self) -> None:
99+
self._write()
100+
101+
def _write(self, *args: Any, sep: str = " ", end: str = "\n") -> None:
115102
print(*args, sep=sep, end=end, file=self._stream)
116103

117104
def build(self) -> str:
118-
self._enable_external_formatter()
119-
return self._stream.getvalue().rstrip("\n")
105+
# inject directives to temporarily disable external C formatters
106+
return "\n".join(
107+
(
108+
"// clang-format off",
109+
self._stream.getvalue().rstrip(),
110+
"// clang-format on",
111+
)
112+
)

Tools/cpuinfo/cpuid_features_gen.py

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Generate an enumeration describing masks to apply on CPUID output registers.
33
4-
Member names are _Py_CPUID_MASK_<REGISTER>_L<LEAF>[S<SUBLEAF>]_<FEATURE>,
4+
Constants are _Py_CPUID_MASK_<REGISTER>_L<LEAF>[S<SUBLEAF>]_<FEATURE>,
55
where <> (resp. []) denotes a required (resp. optional) group and:
66
77
- REGISTER is EAX, EBX, ECX or EDX,
@@ -97,7 +97,7 @@
9797
}
9898

9999

100-
def get_member_name(
100+
def get_constant_name(
101101
leaf: Leaf, subleaf: SubLeaf, registry: Registry, name: Feature
102102
) -> str:
103103
node = f"L{leaf}S{subleaf}" if subleaf else f"L{leaf}"
@@ -106,40 +106,26 @@ def get_member_name(
106106

107107
_NAME_MAXSIZE: Final[int] = util.next_block(
108108
max(
109-
len(get_member_name(*family, name))
109+
len(get_constant_name(*family, name))
110110
for family, values in CPUID_FEATURES.items()
111111
for name in values
112112
)
113113
)
114114

115115

116-
def generate_cpuid_features_enum(enum_name: str) -> str:
117-
"""Used by :file:`Include/internal/pycore_cpuinfo_cpuid_features.h`.
118-
119-
The C enumeration is generated by this function and Argument Clinic,
120-
to be eventually rendred as follows:
121-
122-
<INDENT><MEMBER_NAME> <TAB>= 0x<MASK>, <TAB>// bit = BIT
123-
^ ^ ^ ^ ^ ^ ^
124-
125-
where ^ indicates a column that is a multiple of 4, <MASK> has
126-
exactly 8 characters and <BIT> has at most 2 characters.
127-
"""
128-
enum_name, _typedef_enum_name = util.make_enum_name(enum_name)
116+
def generate_cpuid_features_enum() -> str:
117+
"""Used by :file:`Include/internal/pycore_cpuinfo_cpuid_features.h`."""
129118
writer = util.CWriter()
130-
writer.comment("Enumeration for CPUID features", style=DOXYGEN_STYLE)
131-
writer.write(f"enum {enum_name} {{")
119+
writer.comment("Constants for CPUID features", style=DOXYGEN_STYLE)
132120
for family, values in CPUID_FEATURES.items():
133121
leaf, subleaf, registry = family
134-
title = f"CPUID (LEAF={leaf}, SUBLEAF={subleaf}) [{registry}]"
135-
writer.comment(title, level=1)
122+
writer.comment(f"CPUID (LEAF={leaf}, SUBLEAF={subleaf}) [{registry}]")
136123
for feature_name, bit in values.items():
137124
if not feature_name:
138125
raise ValueError(f"invalid entry for {family}")
139126
if not 0 <= bit < 32:
140127
raise ValueError(f"invalid bit value for {feature_name!r}")
141-
key = get_member_name(leaf, subleaf, registry, feature_name)
142-
member_def = util.make_enum_member(key, bit, _NAME_MAXSIZE)
143-
writer.write(member_def, level=1)
144-
writer.write("};")
128+
key = get_constant_name(leaf, subleaf, registry, feature_name)
129+
writer.write(util.make_constant(key, bit, _NAME_MAXSIZE))
130+
writer.write_blankline()
145131
return writer.build()

Tools/cpuinfo/xsave_features_gen.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Generate enumeration for XSAVE state components (XCR0 control register).
2+
Generate constants for XSAVE state components (XCR0 control register).
33
44
See https://en.wikipedia.org/wiki/Control_register#XCR0_and_XSS.
55
@@ -30,36 +30,22 @@
3030
}
3131

3232

33-
def get_member_name(feature: Feature) -> str:
33+
def get_constant_name(feature: Feature) -> str:
3434
return f"_Py_XSAVE_MASK_XCR0_{feature}"
3535

3636

3737
_NAME_MAXSIZE: Final[int] = util.next_block(
38-
max(map(len, map(get_member_name, XSAVE_FEATURES)))
38+
max(map(len, map(get_constant_name, XSAVE_FEATURES)))
3939
)
4040

4141

42-
def generate_xsave_features_enum(enum_name: str) -> str:
43-
"""Used by :file:`Include/internal/pycore_cpuinfo_xsave_features.h`.
44-
45-
The C enumeration is generated by this function and Argument Clinic,
46-
to be eventually rendred as follows:
47-
48-
<INDENT><MEMBER_NAME> <TAB>= 0x<MASK>, <TAB>// bit = BIT
49-
^ ^ ^ ^ ^ ^ ^
50-
51-
where ^ indicates a column that is a multiple of 4, <MASK> has
52-
exactly 8 characters and <BIT> has at most 2 characters.
53-
"""
54-
enum_name, _typedef_enum_name = util.make_enum_name(enum_name)
42+
def generate_xsave_features_enum() -> str:
43+
"""Used by :file:`Include/internal/pycore_cpuinfo_xsave_features.h`."""
5544
writer = util.CWriter()
56-
writer.comment("Enumeration for XSAVE components", style=DOXYGEN_STYLE)
57-
writer.write(f"enum {enum_name} {{")
45+
writer.comment("Constants for XSAVE components", style=DOXYGEN_STYLE)
5846
for feature_name, bit in XSAVE_FEATURES.items():
5947
if not 0 <= bit < 32:
6048
raise ValueError(f"invalid bit value for {feature_name!r}")
61-
key = get_member_name(feature_name)
62-
member_def = util.make_enum_member(key, bit, _NAME_MAXSIZE)
63-
writer.write(member_def, level=1)
64-
writer.write("};")
49+
key = get_constant_name(feature_name)
50+
writer.write(util.make_constant(key, bit, _NAME_MAXSIZE))
6551
return writer.build()

0 commit comments

Comments
 (0)