Skip to content

Commit 012dd42

Browse files
committed
[X86] Support -march=x86-64-v[234]
PR47686. These micro-architecture levels are defined in the x86-64 psABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/77566eb03bc6a326811cb7e9 GCC 11 will support these levels. Note, -mtune=x86-64-v[234] are invalid and __builtin_cpu_is cannot be used on them. Reviewed By: craig.topper, RKSimon Differential Revision: https://reviews.llvm.org/D89197
1 parent e944455 commit 012dd42

File tree

16 files changed

+182
-44
lines changed

16 files changed

+182
-44
lines changed

clang/docs/ReleaseNotes.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ X86 Support in Clang
188188
- The x86 intrinsics ``__rorb``, ``__rorw``, ``__rord``, ``__rorq`, ``_rotr``,
189189
``_rotwr`` and ``_lrotr`` may now be used within constant expressions.
190190

191-
- Support for -march=sapphirerapids was added.
191+
- Support for ``-march=sapphirerapids`` was added.
192+
193+
- Support for ``-march=x86-64-v[234]`` has been added.
194+
See :doc:`UsersManual` for details about these micro-architecture levels.
192195

193196
- The -mtune command line option is no longer ignored for X86. This can be used
194197
to request microarchitectural optimizations independent on -march. -march=<cpu>

clang/docs/UsersManual.rst

+9
Original file line numberDiff line numberDiff line change
@@ -3201,6 +3201,15 @@ and the ABI remains 32-bit but the assembler emits instructions
32013201
appropriate for a CPU running in 16-bit mode, with address-size and
32023202
operand-size prefixes to enable 32-bit addressing and operations.
32033203

3204+
Several micro-architecture levels as specified by the x86-64 psABI are defined.
3205+
They are cumulative in the sense that features from previous levels are
3206+
implicitly included in later levels.
3207+
3208+
- ``-march=x86-64``: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2
3209+
- ``-march=x86-64-v2``: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3
3210+
- ``-march=x86-64-v3``: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
3211+
- ``-march=x86-64-v4``: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
3212+
32043213
ARM
32053214
^^^
32063215

clang/lib/Basic/Targets/X86.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,9 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
506506
case CK_K8:
507507
case CK_K8SSE3:
508508
case CK_x86_64:
509+
case CK_x86_64_v2:
510+
case CK_x86_64_v3:
511+
case CK_x86_64_v4:
509512
defineCPUMacros(Builder, "k8");
510513
break;
511514
case CK_AMDFAM10:
@@ -1312,6 +1315,9 @@ Optional<unsigned> X86TargetInfo::getCPUCacheLineSize() const {
13121315
case CK_ZNVER2:
13131316
// Deprecated
13141317
case CK_x86_64:
1318+
case CK_x86_64_v2:
1319+
case CK_x86_64_v3:
1320+
case CK_x86_64_v4:
13151321
case CK_Yonah:
13161322
case CK_Penryn:
13171323
case CK_Core2:
@@ -1456,7 +1462,7 @@ void X86TargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
14561462
}
14571463

14581464
void X86TargetInfo::fillValidTuneCPUList(SmallVectorImpl<StringRef> &Values) const {
1459-
llvm::X86::fillValidCPUArchList(Values);
1465+
llvm::X86::fillValidTuneCPUList(Values);
14601466
}
14611467

14621468
ArrayRef<const char *> X86TargetInfo::getGCCRegNames() const {

clang/lib/Basic/Targets/X86.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
314314
// Allow 32-bit only CPUs regardless of 64-bit mode unlike isValidCPUName.
315315
// NOTE: gcc rejects 32-bit mtune CPUs in 64-bit mode. But being lenient
316316
// since mtune was ignored by clang for so long.
317-
return llvm::X86::parseArchX86(Name) != llvm::X86::CK_None;
317+
return llvm::X86::parseTuneCPU(Name) != llvm::X86::CK_None;
318318
}
319319

320320
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;

clang/test/CodeGen/attr-target-x86.c

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ int __attribute__((target("arch=lakemont,mmx"))) use_before_def(void) {
3232

3333
int __attribute__((target("tune=sandybridge"))) walrus(int a) { return 4; }
3434

35+
void __attribute__((target("arch=x86-64-v2"))) x86_64_v2() {}
36+
void __attribute__((target("arch=x86-64-v3"))) x86_64_v3() {}
37+
void __attribute__((target("arch=x86-64-v4"))) x86_64_v4() {}
38+
3539
// Check that we emit the additional subtarget and cpu features for foo and not for baz or bar.
3640
// CHECK: baz{{.*}} #0
3741
// CHECK: foo{{.*}} #1
@@ -59,3 +63,10 @@ int __attribute__((target("tune=sandybridge"))) walrus(int a) { return 4; }
5963
// CHECK: #7 = {{.*}}"target-cpu"="lakemont" "target-features"="+cx8,+mmx"
6064
// CHECK-NOT: tune-cpu
6165
// CHECK: #8 = {{.*}}"target-cpu"="i686" "target-features"="+cx8,+x87" "tune-cpu"="sandybridge"
66+
67+
// CHECK: "target-cpu"="x86-64-v2"
68+
// CHECK-SAME: "target-features"="+cx16,+cx8,+fxsr,+mmx,+popcnt,+sahf,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87"
69+
// CHECK: "target-cpu"="x86-64-v3"
70+
// CHECK-SAME: "target-features"="+avx,+avx2,+bmi,+bmi2,+cx16,+cx8,+f16c,+fma,+fxsr,+lzcnt,+mmx,+movbe,+popcnt,+sahf,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave"
71+
// CHECK: "target-cpu"="x86-64-v4"
72+
// CHECK-SAME: "target-features"="+avx,+avx2,+avx512bw,+avx512cd,+avx512dq,+avx512f,+avx512vl,+bmi,+bmi2,+cx16,+cx8,+f16c,+fma,+fxsr,+lzcnt,+mmx,+movbe,+popcnt,+sahf,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave"

clang/test/Driver/x86-march.c

+9
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,12 @@
175175
// RUN: %clang -target x86_64-unknown-unknown -c -### %s -march=znver2 2>&1 \
176176
// RUN: | FileCheck %s -check-prefix=znver2
177177
// znver2: "-target-cpu" "znver2"
178+
179+
// RUN: %clang -target x86_64 -c -### %s -march=x86-64 2>&1 | FileCheck %s --check-prefix=x86-64
180+
// x86-64: "-target-cpu" "x86-64"
181+
// RUN: %clang -target x86_64 -c -### %s -march=x86-64-v2 2>&1 | FileCheck %s --check-prefix=x86-64-v2
182+
// x86-64-v2: "-target-cpu" "x86-64-v2"
183+
// RUN: %clang -target x86_64 -c -### %s -march=x86-64-v3 2>&1 | FileCheck %s --check-prefix=x86-64-v3
184+
// x86-64-v3: "-target-cpu" "x86-64-v3"
185+
// RUN: %clang -target x86_64 -c -### %s -march=x86-64-v4 2>&1 | FileCheck %s --check-prefix=x86-64-v4
186+
// x86-64-v4: "-target-cpu" "x86-64-v4"

clang/test/Driver/x86-mtune.c

+5
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@
4040
// RUN: | FileCheck %s -check-prefix=marchmtune
4141
// marchmtune: "-target-cpu" "core2"
4242
// mmarchmtune: "-tune-cpu" "nehalem"
43+
44+
// RUN: not %clang %s -target x86_64 -E -mtune=x86-64-v2 2>&1 | FileCheck %s --check-prefix=INVALID
45+
// RUN: not %clang %s -target x86_64 -E -mtune=x86-64-v3 2>&1 | FileCheck %s --check-prefix=INVALID
46+
// RUN: not %clang %s -target x86_64 -E -mtune=x86-64-v4 2>&1 | FileCheck %s --check-prefix=INVALID
47+
// INVALID: error: unknown target CPU '{{.*}}'

clang/test/Misc/target-invalid-cpu-note.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
// X86-SAME: athlon, athlon-tbird, athlon-xp, athlon-mp, athlon-4, k8, athlon64,
2626
// X86-SAME: athlon-fx, opteron, k8-sse3, athlon64-sse3, opteron-sse3, amdfam10,
2727
// X86-SAME: barcelona, btver1, btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2,
28-
// X86-SAME: x86-64, geode
28+
// X86-SAME: x86-64, x86-64-v2, x86-64-v3, x86-64-v4, geode{{$}}
2929

3030
// RUN: not %clang_cc1 -triple x86_64--- -target-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix X86_64
3131
// X86_64: error: unknown target CPU 'not-a-cpu'
@@ -35,7 +35,8 @@
3535
// X86_64-SAME: core-avx2, broadwell, skylake, skylake-avx512, skx, cascadelake, cooperlake, cannonlake,
3636
// X86_64-SAME: icelake-client, icelake-server, tigerlake, sapphirerapids, knl, knm, k8, athlon64, athlon-fx, opteron, k8-sse3,
3737
// X86_64-SAME: athlon64-sse3, opteron-sse3, amdfam10, barcelona, btver1,
38-
// X86_64-SAME: btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2, x86-64
38+
// X86_64-SAME: btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2,
39+
// X86_64-SAME: x86-64, x86-64-v2, x86-64-v3, x86-64-v4{{$}}
3940

4041
// RUN: not %clang_cc1 -triple i386--- -tune-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix TUNE_X86
4142
// TUNE_X86: error: unknown target CPU 'not-a-cpu'
@@ -49,7 +50,7 @@
4950
// TUNE_X86-SAME: athlon, athlon-tbird, athlon-xp, athlon-mp, athlon-4, k8, athlon64,
5051
// TUNE_X86-SAME: athlon-fx, opteron, k8-sse3, athlon64-sse3, opteron-sse3, amdfam10,
5152
// TUNE_X86-SAME: barcelona, btver1, btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2,
52-
// TUNE_X86-SAME: x86-64, geode
53+
// TUNE_X86-SAME: x86-64, geode{{$}}
5354

5455
// RUN: not %clang_cc1 -triple x86_64--- -tune-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix TUNE_X86_64
5556
// TUNE_X86_64: error: unknown target CPU 'not-a-cpu'
@@ -63,7 +64,7 @@
6364
// TUNE_X86_64-SAME: athlon, athlon-tbird, athlon-xp, athlon-mp, athlon-4, k8, athlon64,
6465
// TUNE_X86_64-SAME: athlon-fx, opteron, k8-sse3, athlon64-sse3, opteron-sse3, amdfam10,
6566
// TUNE_X86_64-SAME: barcelona, btver1, btver2, bdver1, bdver2, bdver3, bdver4, znver1, znver2,
66-
// TUNE_X86_64-SAME: x86-64, geode
67+
// TUNE_X86_64-SAME: x86-64, geode{{$}}
6768

6869
// RUN: not %clang_cc1 -triple nvptx--- -target-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix NVPTX
6970
// NVPTX: error: unknown target CPU 'not-a-cpu'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang -target x86_64 -march=x86-64 -E -dM %s > %tv1
2+
// RUN: FileCheck %s --check-prefix=X86_64_V1 < %tv1
3+
4+
// X86_64_V1: #define __MMX__ 1
5+
// X86_64_V1: #define __SSE2_MATH__ 1
6+
// X86_64_V1: #define __SSE2__ 1
7+
// X86_64_V1: #define __SSE_MATH__ 1
8+
// X86_64_V1: #define __SSE__ 1
9+
// X86_64_V1: #define __amd64 1
10+
// X86_64_V1: #define __amd64__ 1
11+
// X86_64_V1: #define __k8 1
12+
// X86_64_V1: #define __k8__ 1
13+
// X86_64_V1: #define __x86_64 1
14+
// X86_64_V1: #define __x86_64__ 1
15+
16+
// RUN: %clang -target x86_64 -march=x86-64-v2 -E -dM %s > %tv2
17+
// RUN: diff %tv1 %tv2 > %t.txt || true
18+
// RUN: FileCheck %s --check-prefix=X86_64_V2 < %t.txt
19+
20+
/// v2 is close to Nehalem.
21+
// X86_64_V2: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 1
22+
// X86_64_V2: #define __LAHF_SAHF__ 1
23+
// X86_64_V2: #define __POPCNT__ 1
24+
// X86_64_V2: #define __SSE3__ 1
25+
// X86_64_V2-NEXT: #define __SSE4_1__ 1
26+
// X86_64_V2-NEXT: #define __SSE4_2__ 1
27+
// X86_64_V2: #define __SSSE3__ 1
28+
29+
/// v3 is close to Haswell.
30+
// RUN: %clang -target x86_64 -march=x86-64-v3 -E -dM %s > %tv3
31+
// RUN: diff %tv2 %tv3 > %t.txt || true
32+
// RUN: FileCheck %s --check-prefix=X86_64_V3 < %t.txt
33+
34+
// X86_64_V3: #define __AVX2__ 1
35+
// X86_64_V3-NEXT: #define __AVX__ 1
36+
// X86_64_V3: #define __BMI2__ 1
37+
// X86_64_V3-NEXT: #define __BMI__ 1
38+
// X86_64_V3: #define __F16C__ 1
39+
// X86_64_V3: #define __FMA__ 1
40+
// X86_64_V3: #define __LZCNT__ 1
41+
// X86_64_V3: #define __MOVBE__ 1
42+
// X86_64_V3: #define __XSAVE__ 1
43+
44+
/// v4 is close to the AVX-512 level implemented by Xeon Scalable Processors.
45+
// RUN: %clang -target x86_64 -march=x86-64-v4 -E -dM %s > %tv4
46+
// RUN: diff %tv3 %tv4 > %t.txt || true
47+
// RUN: FileCheck %s --check-prefix=X86_64_V4 < %t.txt
48+
49+
// X86_64_V4: #define __AVX512BW__ 1
50+
// X86_64_V4-NEXT: #define __AVX512CD__ 1
51+
// X86_64_V4-NEXT: #define __AVX512DQ__ 1
52+
// X86_64_V4-NEXT: #define __AVX512F__ 1
53+
// X86_64_V4-NEXT: #define __AVX512VL__ 1
54+
// X86_64_V4-NOT: #define __AVX512{{.*}}

clang/test/Preprocessor/predefined-arch-macros.c

-15
Original file line numberDiff line numberDiff line change
@@ -2263,21 +2263,6 @@
22632263
// CHECK_X86_64_M32: #define __k8__ 1
22642264
// CHECK_X86_64_M32: #define i386 1
22652265

2266-
// RUN: %clang -march=x86-64 -m64 -E -dM %s -o - 2>&1 \
2267-
// RUN: -target i386-unknown-linux \
2268-
// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_X86_64_M64
2269-
// CHECK_X86_64_M64: #define __MMX__ 1
2270-
// CHECK_X86_64_M64: #define __SSE2_MATH__ 1
2271-
// CHECK_X86_64_M64: #define __SSE2__ 1
2272-
// CHECK_X86_64_M64: #define __SSE_MATH__ 1
2273-
// CHECK_X86_64_M64: #define __SSE__ 1
2274-
// CHECK_X86_64_M64: #define __amd64 1
2275-
// CHECK_X86_64_M64: #define __amd64__ 1
2276-
// CHECK_X86_64_M64: #define __k8 1
2277-
// CHECK_X86_64_M64: #define __k8__ 1
2278-
// CHECK_X86_64_M64: #define __x86_64 1
2279-
// CHECK_X86_64_M64: #define __x86_64__ 1
2280-
22812266
// RUN: %clang -march=k8 -m32 -E -dM %s -o - 2>&1 \
22822267
// RUN: -target i386-unknown-linux \
22832268
// RUN: | FileCheck -match-full-lines %s -check-prefix=CHECK_K8_M32

clang/test/Sema/builtin-cpu-supports.c

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ int main() {
1515

1616
if (__builtin_cpu_is("int")) // expected-error {{invalid cpu name for builtin}}
1717
a("intel");
18+
19+
(void)__builtin_cpu_is("x86-64"); // expected-error {{invalid cpu name for builtin}}
20+
(void)__builtin_cpu_is("x86-64-v2"); // expected-error {{invalid cpu name for builtin}}
21+
(void)__builtin_cpu_is("x86-64-v3"); // expected-error {{invalid cpu name for builtin}}
22+
(void)__builtin_cpu_is("x86-64-v4"); // expected-error {{invalid cpu name for builtin}}
1823
#else
1924
if (__builtin_cpu_supports("vsx")) // expected-error {{use of unknown builtin}}
2025
a("vsx");

llvm/docs/ReleaseNotes.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ During this release ...
105105
* The 'mpx' feature was removed from the backend. It had been removed from clang
106106
frontend in 10.0. Mention of the 'mpx' feature in an IR file will print a
107107
message to stderr, but IR should still compile.
108-
* Support for -march=sapphirerapids was added.
108+
* Support for ``-march=sapphirerapids`` and ``-march=x86-64-v[234]`` has been added.
109109
* The assembler now has support for {disp32} and {disp8} pseudo prefixes for
110110
controlling displacement size for memory operands and jump displacements. The
111111
assembler also supports the .d32 and .d8 mnemonic suffixes to do the same.

llvm/include/llvm/Support/X86TargetParser.h

+7
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,24 @@ enum CPUKind {
121121
CK_ZNVER1,
122122
CK_ZNVER2,
123123
CK_x86_64,
124+
CK_x86_64_v2,
125+
CK_x86_64_v3,
126+
CK_x86_64_v4,
124127
CK_Geode,
125128
};
126129

127130
/// Parse \p CPU string into a CPUKind. Will only accept 64-bit capable CPUs if
128131
/// \p Only64Bit is true.
129132
CPUKind parseArchX86(StringRef CPU, bool Only64Bit = false);
133+
CPUKind parseTuneCPU(StringRef CPU, bool Only64Bit = false);
130134

131135
/// Provide a list of valid CPU names. If \p Only64Bit is true, the list will
132136
/// only contain 64-bit capable CPUs.
133137
void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values,
134138
bool Only64Bit = false);
139+
/// Provide a list of valid -mtune names.
140+
void fillValidTuneCPUList(SmallVectorImpl<StringRef> &Values,
141+
bool Only64Bit = false);
135142

136143
/// Get the key feature prioritizing target multiversioning.
137144
ProcessorFeatures getKeyFeature(CPUKind Kind);

llvm/lib/Support/X86TargetParser.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ constexpr FeatureBitset FeaturesNocona =
137137

138138
// Basic 64-bit capable CPU.
139139
constexpr FeatureBitset FeaturesX86_64 = FeaturesPentium4 | Feature64BIT;
140+
constexpr FeatureBitset FeaturesX86_64_V2 = FeaturesX86_64 | FeatureSAHF |
141+
FeaturePOPCNT | FeatureSSE4_2 |
142+
FeatureCMPXCHG16B;
143+
constexpr FeatureBitset FeaturesX86_64_V3 =
144+
FeaturesX86_64_V2 | FeatureAVX2 | FeatureBMI | FeatureBMI2 | FeatureF16C |
145+
FeatureFMA | FeatureLZCNT | FeatureMOVBE | FeatureXSAVE;
146+
constexpr FeatureBitset FeaturesX86_64_V4 = FeaturesX86_64_V3 |
147+
FeatureAVX512BW | FeatureAVX512CD |
148+
FeatureAVX512DQ | FeatureAVX512VL;
140149

141150
// Intel Core CPUs
142151
constexpr FeatureBitset FeaturesCore2 =
@@ -383,10 +392,15 @@ constexpr ProcInfo Processors[] = {
383392
{ {"znver2"}, CK_ZNVER2, FEATURE_AVX2, FeaturesZNVER2 },
384393
// Generic 64-bit processor.
385394
{ {"x86-64"}, CK_x86_64, ~0U, FeaturesX86_64 },
395+
{ {"x86-64-v2"}, CK_x86_64_v2, ~0U, FeaturesX86_64_V2 },
396+
{ {"x86-64-v3"}, CK_x86_64_v3, ~0U, FeaturesX86_64_V3 },
397+
{ {"x86-64-v4"}, CK_x86_64_v4, ~0U, FeaturesX86_64_V4 },
386398
// Geode processors.
387399
{ {"geode"}, CK_Geode, ~0U, FeaturesGeode },
388400
};
389401

402+
constexpr const char *NoTuneList[] = {"x86-64-v2", "x86-64-v3", "x86-64-v4"};
403+
390404
X86::CPUKind llvm::X86::parseArchX86(StringRef CPU, bool Only64Bit) {
391405
for (const auto &P : Processors)
392406
if (P.Name == CPU && (P.Features[FEATURE_64BIT] || !Only64Bit))
@@ -395,13 +409,27 @@ X86::CPUKind llvm::X86::parseArchX86(StringRef CPU, bool Only64Bit) {
395409
return CK_None;
396410
}
397411

412+
X86::CPUKind llvm::X86::parseTuneCPU(StringRef CPU, bool Only64Bit) {
413+
if (llvm::is_contained(NoTuneList, CPU))
414+
return CK_None;
415+
return parseArchX86(CPU, Only64Bit);
416+
}
417+
398418
void llvm::X86::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values,
399419
bool Only64Bit) {
400420
for (const auto &P : Processors)
401421
if (!P.Name.empty() && (P.Features[FEATURE_64BIT] || !Only64Bit))
402422
Values.emplace_back(P.Name);
403423
}
404424

425+
void llvm::X86::fillValidTuneCPUList(SmallVectorImpl<StringRef> &Values,
426+
bool Only64Bit) {
427+
for (const ProcInfo &P : Processors)
428+
if (!P.Name.empty() && (P.Features[FEATURE_64BIT] || !Only64Bit) &&
429+
!llvm::is_contained(NoTuneList, P.Name))
430+
Values.emplace_back(P.Name);
431+
}
432+
405433
ProcessorFeatures llvm::X86::getKeyFeature(X86::CPUKind Kind) {
406434
// FIXME: Can we avoid a linear search here? The table might be sorted by
407435
// CPUKind so we could binary search?

0 commit comments

Comments
 (0)