https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/78071
None >From f073a827ddbe728ced53a82431f517da58753776 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser <nikolasklau...@berlin.de> Date: Mon, 11 Dec 2023 12:51:07 +0100 Subject: [PATCH] [clang] Add add attribute [[clang::builtin]] --- clang/include/clang/Basic/Attr.td | 5 +- clang/include/clang/Basic/DiagnosticGroups.td | 3 + .../clang/Basic/DiagnosticSemaKinds.td | 11 +++ clang/lib/AST/Decl.cpp | 6 ++ clang/lib/AST/ExprConstant.cpp | 15 ++- clang/lib/CodeGen/CGExpr.cpp | 6 +- clang/lib/Headers/mmintrin.h | 9 +- clang/lib/Sema/SemaDecl.cpp | 4 +- clang/lib/Sema/SemaDeclAttr.cpp | 96 +++++++++++++++++++ clang/lib/Sema/SemaExpr.cpp | 3 + clang/test/CodeGenCXX/attr-builtin.cpp | 71 ++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + clang/test/SemaCXX/attr-builtin-debug.cpp | 4 + clang/test/SemaCXX/attr-builtin.cpp | 65 +++++++++++++ 14 files changed, 285 insertions(+), 14 deletions(-) create mode 100644 clang/test/CodeGenCXX/attr-builtin.cpp create mode 100644 clang/test/SemaCXX/attr-builtin-debug.cpp create mode 100644 clang/test/SemaCXX/attr-builtin.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b0a8ef10c500a7..fb435eac80bdd8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4157,11 +4157,10 @@ def DiagnoseAsBuiltin : InheritableAttr { } def Builtin : InheritableAttr { - let Spellings = []; + let Spellings = [Clang<"builtin">]; let Args = [UnsignedArgument<"ID">]; let Subjects = SubjectList<[Function]>; - let SemaHandler = 0; - let Documentation = [InternalOnly]; + let Documentation = [Undocumented]; } def EnforceTCB : InheritableAttr { diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index caee2dc6daadb6..2390a487b0bcb8 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -771,6 +771,8 @@ def NSobjectAttribute : DiagGroup<"NSObject-attribute">; def NSConsumedMismatch : DiagGroup<"nsconsumed-mismatch">; def NSReturnsMismatch : DiagGroup<"nsreturns-mismatch">; +def UnsupportedBuiltin : DiagGroup<"unsupported-builtin">; + def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">; def UnknownAttributes : DiagGroup<"unknown-attributes">; def IgnoredAttributes : DiagGroup<"ignored-attributes">; @@ -1063,6 +1065,7 @@ def Most : DiagGroup<"most", [ PrivateExtern, SelTypeCast, ExternCCompat, + UnsupportedBuiltin, UserDefinedWarnings ]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 28d95ca9b13893..3c096c07bd4708 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5759,6 +5759,17 @@ def warn_unavailable_def : Warning< def warn_deprecated_builtin : Warning< "builtin %0 is deprecated; use %1 instead">, InGroup<DeprecatedBuiltins>; +def err_unknown_builtin : Error<"unknown builtin">; +def warn_unsupported_builtin : Warning<"builtin is not supported">, + InGroup<UnsupportedBuiltin>; +def warn_unsupported_on_member_function : Warning< + "attribute 'builtin' is not supported on member functions">, + InGroup<UnsupportedBuiltin>; +def err_unexpected_param_count : Error< + "expected %0 argument%select{s|}1 but got %2">; +def err_signature_mismatch : Error< + "function signature does not match the signature of the builtin">; +def note_expected_signature : Note<"expected signature is %0">; def err_unavailable : Error<"%0 is unavailable">; def err_property_method_unavailable : Error<"property access is using %0 method which is unavailable">; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index c5c2edf1bfe3ab..63be2447cca308 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3575,6 +3575,12 @@ unsigned FunctionDecl::getBuiltinID(bool ConsiderWrapperFunctions) const { BuiltinID = BAA->getBuiltinName()->getBuiltinID(); } else if (const auto *A = getAttr<BuiltinAttr>()) { BuiltinID = A->getID(); + + // This is an explicit attribute, which means that this has been declared as + // a builtin by the user. In this case we can assume that the function is + // equivalent to the specified builtin. + if (!A->isImplicit()) + return BuiltinID; } if (!BuiltinID) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index f035c1419f4c98..52b45ba0385b88 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7661,8 +7661,19 @@ class ExprEvaluatorBase bool IsConstantEvaluatedBuiltinCall(const CallExpr *E) { unsigned BuiltinOp = E->getBuiltinCallee(); - return BuiltinOp != 0 && - Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp); + + if (BuiltinOp == 0 || !Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp)) + return false; + + if (const auto* CD = E->getCalleeDecl()) { + if (const auto* FD = CD->getAsFunction()) { + if (FD->isConstexpr()) + return true; + if (const auto* BA = FD->getAttr<BuiltinAttr>()) + return BA->isImplicit(); + } + } + return true; } public: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 69cf7f76be9a70..d514bf238a0096 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5183,11 +5183,12 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, // A CXXOperatorCallExpr is created even for explicit object methods, but // these should be treated like static function call. - if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E)) + if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E)) { if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl()); MD && MD->isImplicitObjectMemberFunction()) return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); + } CGCallee callee = EmitCallee(E->getCallee()); @@ -5224,7 +5225,8 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) { const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl()); if (auto builtinID = FD->getBuiltinID()) { - std::string NoBuiltinFD = ("no-builtin-" + FD->getName()).str(); + std::string NoBuiltinFD = + ("no-builtin-" + CGF.getContext().BuiltinInfo.getName(builtinID)).str(); std::string NoBuiltins = "no-builtins"; StringRef Ident = CGF.CGM.getMangledName(GD); diff --git a/clang/lib/Headers/mmintrin.h b/clang/lib/Headers/mmintrin.h index 08849f01071aea..6f0b595a320482 100644 --- a/clang/lib/Headers/mmintrin.h +++ b/clang/lib/Headers/mmintrin.h @@ -33,11 +33,9 @@ typedef char __v8qi __attribute__((__vector_size__(8))); /// /// This intrinsic corresponds to the <c> EMMS </c> instruction. /// -static __inline__ void __attribute__((__always_inline__, __nodebug__, - __target__("mmx,no-evex512"))) -_mm_empty(void) { - __builtin_ia32_emms(); -} +static __inline__ void + __attribute__((__always_inline__, __nodebug__, __target__("mmx,no-evex512"), + __builtin__("__builtin_ia32_emms"))) _mm_empty(void); /// Constructs a 64-bit integer vector, setting the lower 32 bits to the /// value of the 32-bit integer parameter and setting the upper 32 bits to 0. @@ -1561,4 +1559,3 @@ _mm_setr_pi8(char __b0, char __b1, char __b2, char __b3, char __b4, char __b5, #define _m_pcmpgtd _mm_cmpgt_pi32 #endif /* __MMINTRIN_H */ - diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 19d972ed8ab2d8..ace3f664eae872 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15622,7 +15622,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, // Builtin functions cannot be defined. if (unsigned BuiltinID = FD->getBuiltinID()) { if (!Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID) && - !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID)) { + !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID) && + FD->getAttr<BuiltinAttr>() && + FD->getAttr<BuiltinAttr>()->isImplicit()) { Diag(FD->getLocation(), diag::err_builtin_definition) << FD; FD->setInvalidDecl(); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 59e456fd9f7298..3df09d836513b5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5921,6 +5921,98 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident)); } +static void handleBuiltinAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (!AL.checkExactlyNumArgs(S, 1)) + return; + + StringRef BuiltinName; + if (!S.checkStringLiteralArgumentAttr(AL, 0, BuiltinName)) + return; + + bool IsInStdNamespace = BuiltinName.consume_front("std::"); + + unsigned ID = + S.getPreprocessor().getIdentifierTable().get(BuiltinName).getBuiltinID(); + + auto& BuiltinInfo = S.Context.BuiltinInfo; + + if (ID == 0 || BuiltinInfo.isInStdNamespace(ID) != IsInStdNamespace) { + S.Diag(AL.getLoc(), diag::err_unknown_builtin); + return; + } + + if (BuiltinInfo.isUnevaluated(ID) || BuiltinInfo.hasCustomTypechecking(ID)) { + S.Diag(AL.getLoc(), diag::warn_unsupported_builtin); + return; + } + + ASTContext::GetBuiltinTypeError Error; + QualType Signature = S.Context.GetBuiltinType(ID, Error); + if (Error || Signature.isNull()) { + S.Diag(AL.getLoc(), diag::warn_unsupported_builtin); + return; + } + + auto hasCompatibleSignature = [&] { + auto isTypeCompatible = [&](QualType LHS, QualType RHS) { + if (S.Context.hasSameType(LHS, RHS)) + return true; + if (LHS->isPointerType() && RHS->isPointerType()) { + auto LHSPointee = LHS->getPointeeType(); + auto RHSPointee = RHS->getPointeeType(); + if (LHSPointee->isVoidType()) + return RHSPointee.isAtLeastAsQualifiedAs(LHSPointee); + } + return false; + }; + + if (!isTypeCompatible( + cast<FunctionProtoType>(Signature)->getReturnType(), + cast<FunctionProtoType>(D->getFunctionType())->getReturnType())) + return false; + + auto LHSParams = cast<FunctionProtoType>(Signature)->getParamTypes(); + auto RHSParams = + cast<FunctionProtoType>(D->getFunctionType())->getParamTypes(); + + if (LHSParams.size() != RHSParams.size()) + return false; + + return llvm::all_of(llvm::zip(LHSParams, RHSParams), [&](auto tuple) { + return isTypeCompatible(std::get<0>(tuple), std::get<1>(tuple)); + }); + }; + + if (const auto* MD = dyn_cast<CXXMethodDecl>(D)) { + if (!MD->isStatic()) { + S.Diag(AL.getLoc(), diag::warn_unsupported_on_member_function); + return; + } + } + + if (BuiltinInfo.allowTypeMismatch(ID)) { + auto ExpectedParamCount = + cast<FunctionProtoType>(Signature)->getNumParams(); + auto ActualParamCount = + cast<FunctionProtoType>(D->getFunctionType())->getNumParams(); + if (ExpectedParamCount != ActualParamCount) { + S.Diag(AL.getLoc(), diag::err_unexpected_param_count) + << ExpectedParamCount << (ExpectedParamCount == 1) + << ActualParamCount; + return; + } + } else if (QualType FuncSig = cast<FunctionDecl>(D)->getType(); + !S.Context.hasSameFunctionTypeIgnoringExceptionSpec(Signature, + FuncSig) && + !hasCompatibleSignature()) { + S.Diag(AL.getLoc(), diag::err_signature_mismatch); + S.Diag(AL.getLoc(), diag::note_expected_signature) << Signature; + } + + D->addAttr(BuiltinAttr::Create(S.Context, ID)); +} + static void handlePreferredTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.hasParsedType()) { S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; @@ -9717,6 +9809,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, handleBuiltinAliasAttr(S, D, AL); break; + case ParsedAttr::AT_Builtin: + handleBuiltinAttr(S, D, AL); + break; + case ParsedAttr::AT_PreferredType: handlePreferredTypeAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d629be083d8c38..e3ba57cfff6a7d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7147,6 +7147,9 @@ static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S, if (BuiltinID != Builtin::BImove && BuiltinID != Builtin::BIforward) return; + if (!FD->isInStdNamespace()) + return; + S.Diag(DRE->getLocation(), diag::warn_unqualified_call_to_std_cast_function) << FD->getQualifiedNameAsString() << FixItHint::CreateInsertion(DRE->getLocation(), "std::"); diff --git a/clang/test/CodeGenCXX/attr-builtin.cpp b/clang/test/CodeGenCXX/attr-builtin.cpp new file mode 100644 index 00000000000000..4da0354f43d3ff --- /dev/null +++ b/clang/test/CodeGenCXX/attr-builtin.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 %s -S -emit-llvm -triple x86_64-unknown-linux-gnu -o - -Wno-c++23-extensions | FileCheck %s + +[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long); + +void call_memcpy(int i) { + int j; + my_memcpy(&j, &i, sizeof(int)); + + // CHECK: define dso_local void @_Z11call_memcpyi(i32 noundef %i) #0 { + // CHECK-NEXT: entry: + // CHECK-NEXT: %i.addr = alloca i32, align 4 + // CHECK-NEXT: %j = alloca i32, align 4 + // CHECK-NEXT: store i32 %i, ptr %i.addr, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %j, ptr align 4 %i.addr, i64 4, i1 false) + // CHECK-NEXT: ret void + // CHECK-NEXT: } +} + +[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long); + +char* call_typed_memmove(char* dst, const char* src, unsigned long count) { + return typed_memmove(dst, src, count); + + // CHECK: define dso_local noundef ptr @_Z18call_typed_memmovePcPKcm(ptr noundef %dst, ptr noundef %src, i64 noundef %count) #0 { + // CHECK-NEXT: entry: + // CHECK-NEXT: %dst.addr = alloca ptr, align 8 + // CHECK-NEXT: %src.addr = alloca ptr, align 8 + // CHECK-NEXT: %count.addr = alloca i64, align 8 + // CHECK-NEXT: store ptr %dst, ptr %dst.addr, align 8 + // CHECK-NEXT: store ptr %src, ptr %src.addr, align 8 + // CHECK-NEXT: store i64 %count, ptr %count.addr, align 8 + // CHECK-NEXT: %0 = load ptr, ptr %dst.addr, align 8 + // CHECK-NEXT: %1 = load ptr, ptr %src.addr, align 8 + // CHECK-NEXT: %2 = load i64, ptr %count.addr, align 8 + // CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 %0, ptr align 1 %1, i64 %2, i1 false) + // CHECK-NEXT: ret ptr %0 + // CHECK-NEXT: } +} + +template <class T> +[[clang::builtin("std::move")]] __remove_reference_t(T)&& my_move(T&&); + +void call_move() { + int i = my_move(0); + + // CHECK: define dso_local void @_Z9call_movev() #0 { + // CHECK-NEXT: entry: + // CHECK-NEXT: %i = alloca i32, align 4 + // CHECK-NEXT: %ref.tmp = alloca i32, align 4 + // CHECK-NEXT: store i32 0, ptr %ref.tmp, align 4 + // CHECK-NEXT: %0 = load i32, ptr %ref.tmp, align 4 + // CHECK-NEXT: store i32 %0, ptr %i, align 4 + // CHECK-NEXT: ret void + // CHECK-NEXT: } +} + +struct identity { + template <class T> + [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept; +}; + +void call_identity() { + (void)identity()(1); + + // CHECK: define dso_local void @_Z13call_identityv() #0 { + // CHECK-NEXT: entry: + // CHECK-NEXT: %ref.tmp = alloca i32, align 4 + // CHECK-NEXT: store i32 1, ptr %ref.tmp, align 4 + // CHECK-NEXT: ret void + // CHECK-NEXT: } +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index bdfda430eea86c..96fb221e66eba3 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -25,6 +25,7 @@ // CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record) // CHECK-NEXT: BPFPreserveStaticOffset (SubjectMatchRule_record) // CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias) +// CHECK-NEXT: Builtin (SubjectMatchRule_function) // CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function) // CHECK-NEXT: CFAuditedTransfer (SubjectMatchRule_function) // CHECK-NEXT: CFConsumed (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/SemaCXX/attr-builtin-debug.cpp b/clang/test/SemaCXX/attr-builtin-debug.cpp new file mode 100644 index 00000000000000..c81e663137b0db --- /dev/null +++ b/clang/test/SemaCXX/attr-builtin-debug.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions + +[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \ + expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}} diff --git a/clang/test/SemaCXX/attr-builtin.cpp b/clang/test/SemaCXX/attr-builtin.cpp new file mode 100644 index 00000000000000..0fa72600737364 --- /dev/null +++ b/clang/test/SemaCXX/attr-builtin.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-c++23-extensions +// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions + +[[clang::builtin]] void func(); // expected-error {{'builtin' attribute takes one argument}} +[[clang::builtin("unknown_builtin")]] void func(); // expected-warning {{builtin is not supported}} +[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \ + expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}} +[[clang::builtin("move")]] void func(); // expected-warning {{builtin is not supported}} + +// has unevaluated parameters +[[clang::builtin("__builtin_constant_p")]] void constant_p(); // expected-warning {{builtin is not supported}} + +[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long); +[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*, unsigned long); +[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*); // expected-error {{function signature does not match the signature of the builtin}} \ + expected-note {{expected signature is}} + +[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long); // expected-note {{candidate function}} + +void call_memmove(void* ptr) { + typed_memmove(ptr, ptr, 1); // expected-error {{no matching function for call to 'typed_memmove'}} +} + +[[clang::builtin("__builtin_memmove")]] void* non_constexpr_memmove(void*, const void*, unsigned long); + +constexpr void call_non_constexpr_memmove() { // expected-error {{constexpr function never produces a constant expression}} + int i = 0; + int j = 0; + non_constexpr_memmove(&j, &i, sizeof(int)); // expected-note {{subexpression not valid in a constant expression}} +} + +[[clang::builtin("__builtin_memmove")]] constexpr void* constexpr_memmove(void*, const void*, unsigned long); + +constexpr void call_constexpr_memmove() { + int i = 0; + int j = 0; + constexpr_memmove(&j, &i, sizeof(int)); +} + +// allows type mismatches +[[clang::builtin("std::move")]] void my_move(); // expected-error {{expected 1 argument but got 0}} +[[clang::builtin("std::move")]] void my_move(int); + +void call_move() { + my_move(1); // expected-error {{unsupported signature for 'my_move'}} +} + +// has custom type checking +[[clang::builtin("__builtin_operator_new")]] void* my_operator_new(unsigned long); // expected-warning {{builtin is not supported}} + +// canonical types are compared +using size_t = decltype(sizeof(int)); +[[clang::builtin("__builtin_memcmp")]] int my_memcmp(const char*, const char*, size_t); + +struct reject_on_member_functions { + template<class T> + [[clang::builtin("std::forward")]] T&& operator()(T&&) noexcept; // expected-warning {{attribute 'builtin' is not supported on member functions}} + [[clang::builtin("memchr")]] const void* memchr(const void*, int, unsigned long); // expected-warning {{attribute 'builtin' is not supported on member functions}} +}; + +struct accept_on_static_member_functions { + template <class T> + [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept; + [[clang::builtin("memchr")]] static const void* memchr(const void*, int, unsigned long); +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits