Skip to content

Commit f0eb558

Browse files
authored
Remove support for 3DNow!, both intrinsics and builtins. (#96246)
This set of instructions was only supported by AMD chips starting in the K6-2 (introduced 1998), and before the "Bulldozer" family (2011). They were never much used, as they were effectively superseded by the more-widely-implemented SSE (first implemented on the AMD side in Athlon XP in 2001). This is being done as a predecessor towards general removal of MMX register usage. Since there is almost no usage of the 3DNow! intrinsics, and no modern hardware even implements them, simple removal seems like the best option. (Clang half originally uploaded in https://reviews.llvm.org/D94213) Works towards issue #41665 and issue #98272.
1 parent 3706c12 commit f0eb558

29 files changed

+155
-2337
lines changed

clang/docs/ReleaseNotes.rst

+19
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,25 @@ X86 Support
10781078
^^^^^^^^^^^
10791079

10801080
- Remove knl/knm specific ISA supports: AVX512PF, AVX512ER, PREFETCHWT1
1081+
- Support has been removed for the AMD "3DNow!" instruction-set.
1082+
Neither modern AMD CPUs, nor any Intel CPUs implement these
1083+
instructions, and they were never widely used.
1084+
1085+
* The options ``-m3dnow`` and ``-m3dnowa`` are no longer honored, and will emit a warning if used.
1086+
* The macros ``__3dNOW__`` and ``__3dNOW_A__`` are no longer ever set by the compiler.
1087+
* The header ``<mm3dnow.h>`` is deprecated, and emits a warning if included.
1088+
* The 3dNow intrinsic functions have been removed: ``_m_femms``,
1089+
``_m_pavgusb``, ``_m_pf2id``, ``_m_pfacc``, ``_m_pfadd``,
1090+
``_m_pfcmpeq``, ``_m_pfcmpge``, ``_m_pfcmpgt``, ``_m_pfmax``,
1091+
``_m_pfmin``, ``_m_pfmul``, ``_m_pfrcp``, ``_m_pfrcpit1``,
1092+
``_m_pfrcpit2``, ``_m_pfrsqrt``, ``_m_pfrsqrtit1``, ``_m_pfsub``,
1093+
``_m_pfsubr``, ``_m_pi2fd``, ``_m_pmulhrw``, ``_m_pf2iw``,
1094+
``_m_pfnacc``, ``_m_pfpnacc``, ``_m_pi2fw``, ``_m_pswapdsf``,
1095+
``_m_pswapdsi``.
1096+
* The compiler builtins corresponding to each of the above
1097+
intrinsics have also been removed (``__builtin_ia32_femms``, and so on).
1098+
* "3DNow!" instructions remain supported in assembly code, including
1099+
inside inline-assembly.
10811100

10821101
Arm and AArch64 Support
10831102
^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/BuiltinsX86.def

-30
Original file line numberDiff line numberDiff line change
@@ -37,36 +37,6 @@ TARGET_BUILTIN(__builtin_ia32_undef512, "V8d", "ncV:512:", "")
3737
TARGET_BUILTIN(__builtin_ia32_readeflags_u32, "Ui", "n", "")
3838
TARGET_BUILTIN(__builtin_ia32_writeeflags_u32, "vUi", "n", "")
3939

40-
// 3DNow!
41-
//
42-
TARGET_BUILTIN(__builtin_ia32_femms, "v", "n", "3dnow")
43-
TARGET_BUILTIN(__builtin_ia32_pavgusb, "V8cV8cV8c", "ncV:64:", "3dnow")
44-
TARGET_BUILTIN(__builtin_ia32_pf2id, "V2iV2f", "ncV:64:", "3dnow")
45-
TARGET_BUILTIN(__builtin_ia32_pfacc, "V2fV2fV2f", "ncV:64:", "3dnow")
46-
TARGET_BUILTIN(__builtin_ia32_pfadd, "V2fV2fV2f", "ncV:64:", "3dnow")
47-
TARGET_BUILTIN(__builtin_ia32_pfcmpeq, "V2iV2fV2f", "ncV:64:", "3dnow")
48-
TARGET_BUILTIN(__builtin_ia32_pfcmpge, "V2iV2fV2f", "ncV:64:", "3dnow")
49-
TARGET_BUILTIN(__builtin_ia32_pfcmpgt, "V2iV2fV2f", "ncV:64:", "3dnow")
50-
TARGET_BUILTIN(__builtin_ia32_pfmax, "V2fV2fV2f", "ncV:64:", "3dnow")
51-
TARGET_BUILTIN(__builtin_ia32_pfmin, "V2fV2fV2f", "ncV:64:", "3dnow")
52-
TARGET_BUILTIN(__builtin_ia32_pfmul, "V2fV2fV2f", "ncV:64:", "3dnow")
53-
TARGET_BUILTIN(__builtin_ia32_pfrcp, "V2fV2f", "ncV:64:", "3dnow")
54-
TARGET_BUILTIN(__builtin_ia32_pfrcpit1, "V2fV2fV2f", "ncV:64:", "3dnow")
55-
TARGET_BUILTIN(__builtin_ia32_pfrcpit2, "V2fV2fV2f", "ncV:64:", "3dnow")
56-
TARGET_BUILTIN(__builtin_ia32_pfrsqrt, "V2fV2f", "ncV:64:", "3dnow")
57-
TARGET_BUILTIN(__builtin_ia32_pfrsqit1, "V2fV2fV2f", "ncV:64:", "3dnow")
58-
TARGET_BUILTIN(__builtin_ia32_pfsub, "V2fV2fV2f", "ncV:64:", "3dnow")
59-
TARGET_BUILTIN(__builtin_ia32_pfsubr, "V2fV2fV2f", "ncV:64:", "3dnow")
60-
TARGET_BUILTIN(__builtin_ia32_pi2fd, "V2fV2i", "ncV:64:", "3dnow")
61-
TARGET_BUILTIN(__builtin_ia32_pmulhrw, "V4sV4sV4s", "ncV:64:", "3dnow")
62-
// 3DNow! Extensions (3dnowa).
63-
TARGET_BUILTIN(__builtin_ia32_pf2iw, "V2iV2f", "ncV:64:", "3dnowa")
64-
TARGET_BUILTIN(__builtin_ia32_pfnacc, "V2fV2fV2f", "ncV:64:", "3dnowa")
65-
TARGET_BUILTIN(__builtin_ia32_pfpnacc, "V2fV2fV2f", "ncV:64:", "3dnowa")
66-
TARGET_BUILTIN(__builtin_ia32_pi2fw, "V2fV2i", "ncV:64:", "3dnowa")
67-
TARGET_BUILTIN(__builtin_ia32_pswapdsf, "V2fV2f", "ncV:64:", "3dnowa")
68-
TARGET_BUILTIN(__builtin_ia32_pswapdsi, "V2iV2i", "ncV:64:", "3dnowa")
69-
7040
// MMX
7141
//
7242
// All MMX instructions will be generated via builtins. Any MMX vector

clang/include/clang/Driver/Options.td

+6-4
Original file line numberDiff line numberDiff line change
@@ -6135,10 +6135,6 @@ def mno_80387 : Flag<["-"], "mno-80387">, Alias<mno_x87>;
61356135
def mno_fp_ret_in_387 : Flag<["-"], "mno-fp-ret-in-387">, Alias<mno_x87>;
61366136
def mmmx : Flag<["-"], "mmmx">, Group<m_x86_Features_Group>;
61376137
def mno_mmx : Flag<["-"], "mno-mmx">, Group<m_x86_Features_Group>;
6138-
def m3dnow : Flag<["-"], "m3dnow">, Group<m_x86_Features_Group>;
6139-
def mno_3dnow : Flag<["-"], "mno-3dnow">, Group<m_x86_Features_Group>;
6140-
def m3dnowa : Flag<["-"], "m3dnowa">, Group<m_x86_Features_Group>;
6141-
def mno_3dnowa : Flag<["-"], "mno-3dnowa">, Group<m_x86_Features_Group>;
61426138
def mamx_bf16 : Flag<["-"], "mamx-bf16">, Group<m_x86_Features_Group>;
61436139
def mno_amx_bf16 : Flag<["-"], "mno-amx-bf16">, Group<m_x86_Features_Group>;
61446140
def mamx_complex : Flag<["-"], "mamx-complex">, Group<m_x86_Features_Group>;
@@ -6372,6 +6368,12 @@ def mvevpu : Flag<["-"], "mvevpu">, Group<m_ve_Features_Group>,
63726368
def mno_vevpu : Flag<["-"], "mno-vevpu">, Group<m_ve_Features_Group>;
63736369
} // let Flags = [TargetSpecific]
63746370

6371+
// Unsupported X86 feature flags (triggers a warning)
6372+
def m3dnow : Flag<["-"], "m3dnow">;
6373+
def mno_3dnow : Flag<["-"], "mno-3dnow">;
6374+
def m3dnowa : Flag<["-"], "m3dnowa">;
6375+
def mno_3dnowa : Flag<["-"], "mno-3dnowa">;
6376+
63756377
// These are legacy user-facing driver-level option spellings. They are always
63766378
// aliases for options that are spelled using the more common Unix / GNU flag
63776379
// style of double-dash and equals-joined flags.

clang/lib/Basic/Targets/X86.cpp

+5-24
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
258258
if (Feature[0] != '+')
259259
continue;
260260

261-
if (Feature == "+aes") {
261+
if (Feature == "+mmx") {
262+
HasMMX = true;
263+
} else if (Feature == "+aes") {
262264
HasAES = true;
263265
} else if (Feature == "+vaes") {
264266
HasVAES = true;
@@ -487,13 +489,6 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
487489
// for bfloat16 arithmetic operations in the front-end.
488490
HasBFloat16 = SSELevel >= SSE2;
489491

490-
MMX3DNowEnum ThreeDNowLevel = llvm::StringSwitch<MMX3DNowEnum>(Feature)
491-
.Case("+3dnowa", AMD3DNowAthlon)
492-
.Case("+3dnow", AMD3DNow)
493-
.Case("+mmx", MMX)
494-
.Default(NoMMX3DNow);
495-
MMX3DNowLevel = std::max(MMX3DNowLevel, ThreeDNowLevel);
496-
497492
XOPEnum XLevel = llvm::StringSwitch<XOPEnum>(Feature)
498493
.Case("+xop", XOP)
499494
.Case("+fma4", FMA4)
@@ -1031,18 +1026,8 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
10311026
}
10321027

10331028
// Each case falls through to the previous one here.
1034-
switch (MMX3DNowLevel) {
1035-
case AMD3DNowAthlon:
1036-
Builder.defineMacro("__3dNOW_A__");
1037-
[[fallthrough]];
1038-
case AMD3DNow:
1039-
Builder.defineMacro("__3dNOW__");
1040-
[[fallthrough]];
1041-
case MMX:
1029+
if (HasMMX) {
10421030
Builder.defineMacro("__MMX__");
1043-
[[fallthrough]];
1044-
case NoMMX3DNow:
1045-
break;
10461031
}
10471032

10481033
if (CPU >= CK_i486 || CPU == CK_None) {
@@ -1061,8 +1046,6 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
10611046

10621047
bool X86TargetInfo::isValidFeatureName(StringRef Name) const {
10631048
return llvm::StringSwitch<bool>(Name)
1064-
.Case("3dnow", true)
1065-
.Case("3dnowa", true)
10661049
.Case("adx", true)
10671050
.Case("aes", true)
10681051
.Case("amx-bf16", true)
@@ -1232,9 +1215,7 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const {
12321215
.Case("widekl", HasWIDEKL)
12331216
.Case("lwp", HasLWP)
12341217
.Case("lzcnt", HasLZCNT)
1235-
.Case("mm3dnow", MMX3DNowLevel >= AMD3DNow)
1236-
.Case("mm3dnowa", MMX3DNowLevel >= AMD3DNowAthlon)
1237-
.Case("mmx", MMX3DNowLevel >= MMX)
1218+
.Case("mmx", HasMMX)
12381219
.Case("movbe", HasMOVBE)
12391220
.Case("movdiri", HasMOVDIRI)
12401221
.Case("movdir64b", HasMOVDIR64B)

clang/lib/Basic/Targets/X86.h

+2-8
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
6767
AVX2,
6868
AVX512F
6969
} SSELevel = NoSSE;
70-
enum MMX3DNowEnum {
71-
NoMMX3DNow,
72-
MMX,
73-
AMD3DNow,
74-
AMD3DNowAthlon
75-
} MMX3DNowLevel = NoMMX3DNow;
70+
bool HasMMX = false;
7671
enum XOPEnum { NoXOP, SSE4A, FMA4, XOP } XOPLevel = NoXOP;
7772
enum AddrSpace { ptr32_sptr = 270, ptr32_uptr = 271, ptr64 = 272 };
7873

@@ -348,8 +343,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
348343
return "avx512";
349344
if (getTriple().getArch() == llvm::Triple::x86_64 && SSELevel >= AVX)
350345
return "avx";
351-
if (getTriple().getArch() == llvm::Triple::x86 &&
352-
MMX3DNowLevel == NoMMX3DNow)
346+
if (getTriple().getArch() == llvm::Triple::x86 && !HasMMX)
353347
return "no-mmx";
354348
return "";
355349
}

clang/lib/CodeGen/CGBuiltin.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -15969,14 +15969,6 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
1596915969
return Builder.CreateCall(F, {Ops[0]});
1597015970
}
1597115971

15972-
// 3DNow!
15973-
case X86::BI__builtin_ia32_pswapdsf:
15974-
case X86::BI__builtin_ia32_pswapdsi: {
15975-
llvm::Type *MMXTy = llvm::Type::getX86_MMXTy(getLLVMContext());
15976-
Ops[0] = Builder.CreateBitCast(Ops[0], MMXTy, "cast");
15977-
llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_3dnowa_pswapd);
15978-
return Builder.CreateCall(F, Ops, "pswapd");
15979-
}
1598015972
case X86::BI__builtin_ia32_rdrand16_step:
1598115973
case X86::BI__builtin_ia32_rdrand32_step:
1598215974
case X86::BI__builtin_ia32_rdrand64_step:

clang/lib/Driver/ToolChains/Arch/X86.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,17 @@ void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,
310310
Features.push_back("+prefer-no-scatter");
311311
if (Args.hasArg(options::OPT_mapx_inline_asm_use_gpr32))
312312
Features.push_back("+inline-asm-use-gpr32");
313+
314+
// Warn for removed 3dnow support
315+
if (const Arg *A =
316+
Args.getLastArg(options::OPT_m3dnowa, options::OPT_mno_3dnowa,
317+
options::OPT_mno_3dnow)) {
318+
if (A->getOption().matches(options::OPT_m3dnowa))
319+
D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);
320+
}
321+
if (const Arg *A =
322+
Args.getLastArg(options::OPT_m3dnow, options::OPT_mno_3dnow)) {
323+
if (A->getOption().matches(options::OPT_m3dnow))
324+
D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);
325+
}
313326
}

clang/lib/Headers/mm3dnow.h

+6-141
Original file line numberDiff line numberDiff line change
@@ -7,151 +7,16 @@
77
*===-----------------------------------------------------------------------===
88
*/
99

10+
// 3dNow intrinsics are no longer supported.
11+
1012
#ifndef _MM3DNOW_H_INCLUDED
1113
#define _MM3DNOW_H_INCLUDED
1214

15+
#ifndef _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS
16+
#warning "The <mm3dnow.h> header is deprecated, and 3dNow! intrinsics are unsupported. For other intrinsics, include <x86intrin.h>, instead."
17+
#endif
18+
1319
#include <mmintrin.h>
1420
#include <prfchwintrin.h>
1521

16-
typedef float __v2sf __attribute__((__vector_size__(8)));
17-
18-
/* Define the default attributes for the functions in this file. */
19-
#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("3dnow"), __min_vector_width__(64)))
20-
21-
static __inline__ void __attribute__((__always_inline__, __nodebug__, __target__("3dnow")))
22-
_m_femms(void) {
23-
__builtin_ia32_femms();
24-
}
25-
26-
static __inline__ __m64 __DEFAULT_FN_ATTRS
27-
_m_pavgusb(__m64 __m1, __m64 __m2) {
28-
return (__m64)__builtin_ia32_pavgusb((__v8qi)__m1, (__v8qi)__m2);
29-
}
30-
31-
static __inline__ __m64 __DEFAULT_FN_ATTRS
32-
_m_pf2id(__m64 __m) {
33-
return (__m64)__builtin_ia32_pf2id((__v2sf)__m);
34-
}
35-
36-
static __inline__ __m64 __DEFAULT_FN_ATTRS
37-
_m_pfacc(__m64 __m1, __m64 __m2) {
38-
return (__m64)__builtin_ia32_pfacc((__v2sf)__m1, (__v2sf)__m2);
39-
}
40-
41-
static __inline__ __m64 __DEFAULT_FN_ATTRS
42-
_m_pfadd(__m64 __m1, __m64 __m2) {
43-
return (__m64)__builtin_ia32_pfadd((__v2sf)__m1, (__v2sf)__m2);
44-
}
45-
46-
static __inline__ __m64 __DEFAULT_FN_ATTRS
47-
_m_pfcmpeq(__m64 __m1, __m64 __m2) {
48-
return (__m64)__builtin_ia32_pfcmpeq((__v2sf)__m1, (__v2sf)__m2);
49-
}
50-
51-
static __inline__ __m64 __DEFAULT_FN_ATTRS
52-
_m_pfcmpge(__m64 __m1, __m64 __m2) {
53-
return (__m64)__builtin_ia32_pfcmpge((__v2sf)__m1, (__v2sf)__m2);
54-
}
55-
56-
static __inline__ __m64 __DEFAULT_FN_ATTRS
57-
_m_pfcmpgt(__m64 __m1, __m64 __m2) {
58-
return (__m64)__builtin_ia32_pfcmpgt((__v2sf)__m1, (__v2sf)__m2);
59-
}
60-
61-
static __inline__ __m64 __DEFAULT_FN_ATTRS
62-
_m_pfmax(__m64 __m1, __m64 __m2) {
63-
return (__m64)__builtin_ia32_pfmax((__v2sf)__m1, (__v2sf)__m2);
64-
}
65-
66-
static __inline__ __m64 __DEFAULT_FN_ATTRS
67-
_m_pfmin(__m64 __m1, __m64 __m2) {
68-
return (__m64)__builtin_ia32_pfmin((__v2sf)__m1, (__v2sf)__m2);
69-
}
70-
71-
static __inline__ __m64 __DEFAULT_FN_ATTRS
72-
_m_pfmul(__m64 __m1, __m64 __m2) {
73-
return (__m64)__builtin_ia32_pfmul((__v2sf)__m1, (__v2sf)__m2);
74-
}
75-
76-
static __inline__ __m64 __DEFAULT_FN_ATTRS
77-
_m_pfrcp(__m64 __m) {
78-
return (__m64)__builtin_ia32_pfrcp((__v2sf)__m);
79-
}
80-
81-
static __inline__ __m64 __DEFAULT_FN_ATTRS
82-
_m_pfrcpit1(__m64 __m1, __m64 __m2) {
83-
return (__m64)__builtin_ia32_pfrcpit1((__v2sf)__m1, (__v2sf)__m2);
84-
}
85-
86-
static __inline__ __m64 __DEFAULT_FN_ATTRS
87-
_m_pfrcpit2(__m64 __m1, __m64 __m2) {
88-
return (__m64)__builtin_ia32_pfrcpit2((__v2sf)__m1, (__v2sf)__m2);
89-
}
90-
91-
static __inline__ __m64 __DEFAULT_FN_ATTRS
92-
_m_pfrsqrt(__m64 __m) {
93-
return (__m64)__builtin_ia32_pfrsqrt((__v2sf)__m);
94-
}
95-
96-
static __inline__ __m64 __DEFAULT_FN_ATTRS
97-
_m_pfrsqrtit1(__m64 __m1, __m64 __m2) {
98-
return (__m64)__builtin_ia32_pfrsqit1((__v2sf)__m1, (__v2sf)__m2);
99-
}
100-
101-
static __inline__ __m64 __DEFAULT_FN_ATTRS
102-
_m_pfsub(__m64 __m1, __m64 __m2) {
103-
return (__m64)__builtin_ia32_pfsub((__v2sf)__m1, (__v2sf)__m2);
104-
}
105-
106-
static __inline__ __m64 __DEFAULT_FN_ATTRS
107-
_m_pfsubr(__m64 __m1, __m64 __m2) {
108-
return (__m64)__builtin_ia32_pfsubr((__v2sf)__m1, (__v2sf)__m2);
109-
}
110-
111-
static __inline__ __m64 __DEFAULT_FN_ATTRS
112-
_m_pi2fd(__m64 __m) {
113-
return (__m64)__builtin_ia32_pi2fd((__v2si)__m);
114-
}
115-
116-
static __inline__ __m64 __DEFAULT_FN_ATTRS
117-
_m_pmulhrw(__m64 __m1, __m64 __m2) {
118-
return (__m64)__builtin_ia32_pmulhrw((__v4hi)__m1, (__v4hi)__m2);
119-
}
120-
121-
/* Handle the 3dnowa instructions here. */
122-
#undef __DEFAULT_FN_ATTRS
123-
#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("3dnowa"), __min_vector_width__(64)))
124-
125-
static __inline__ __m64 __DEFAULT_FN_ATTRS
126-
_m_pf2iw(__m64 __m) {
127-
return (__m64)__builtin_ia32_pf2iw((__v2sf)__m);
128-
}
129-
130-
static __inline__ __m64 __DEFAULT_FN_ATTRS
131-
_m_pfnacc(__m64 __m1, __m64 __m2) {
132-
return (__m64)__builtin_ia32_pfnacc((__v2sf)__m1, (__v2sf)__m2);
133-
}
134-
135-
static __inline__ __m64 __DEFAULT_FN_ATTRS
136-
_m_pfpnacc(__m64 __m1, __m64 __m2) {
137-
return (__m64)__builtin_ia32_pfpnacc((__v2sf)__m1, (__v2sf)__m2);
138-
}
139-
140-
static __inline__ __m64 __DEFAULT_FN_ATTRS
141-
_m_pi2fw(__m64 __m) {
142-
return (__m64)__builtin_ia32_pi2fw((__v2si)__m);
143-
}
144-
145-
static __inline__ __m64 __DEFAULT_FN_ATTRS
146-
_m_pswapdsf(__m64 __m) {
147-
return (__m64)__builtin_ia32_pswapdsf((__v2sf)__m);
148-
}
149-
150-
static __inline__ __m64 __DEFAULT_FN_ATTRS
151-
_m_pswapdsi(__m64 __m) {
152-
return (__m64)__builtin_ia32_pswapdsi((__v2si)__m);
153-
}
154-
155-
#undef __DEFAULT_FN_ATTRS
156-
15722
#endif

clang/lib/Headers/x86intrin.h

-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414

1515
#include <immintrin.h>
1616

17-
#if !defined(__SCE__) || __has_feature(modules) || defined(__3dNOW__)
18-
#include <mm3dnow.h>
19-
#endif
20-
2117
#if !defined(__SCE__) || __has_feature(modules) || defined(__PRFCHW__)
2218
#include <prfchwintrin.h>
2319
#endif

0 commit comments

Comments
 (0)