joey created this revision. joey added reviewers: asavonic, Anastasia. Herald added subscribers: cfe-commits, yaxunl, mgorny.
This is the prototype for the approach that was mentioned by Anastasia in http://lists.llvm.org/pipermail/cfe-dev/2018-September/059529.html The tablegen file describes the BIFs and all their overloads, in hopefully a concise manner. There are 3 things generated from the OpenCLBuiltins.td file. 1. OpenCLArgTypes[], this is a table containing all the different types of overloads. This is a separate table so it can be shared by the BIFs. 2. OpenCLBuiltins[], this is a table that contains all the overloads for the BIFs. 3. isOpenCLBuiltin, this is a function that uses a trie-like switch/case to determine if a StringRef is the name of a BIF. Just a quick snippet of the above: OpenCLType OpenCLArgTypes[] = { // 0 { OCLT_float, 0, 0, clang::LangAS::Default, }, // 1 { OCLT_float, 2, 0, clang::LangAS::Default, }, OpenCLBuiltinDecl OpenCLBuiltins[] = { // acos { { OCLT_float, 0, 0, clang::LangAS::Default, }, 1, 0, "", 100, }, { { OCLT_float, 2, 0, clang::LangAS::Default, }, 1, 1, "", 100, }, std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef name) { switch (name.size()) { default: break; case 3: // 1 string to match. if (memcmp(name.data()+0, "foo", 3) != 0) break; return std::make_pair(707, 2); // "foo" While it's a prototype, I have tried to keep it as clean as possible. TODO: 1. Bit-pack the tables to reduce the size. 2. Include the return type in the ArgTypes table to reduce the size. 3. Measure the performance / size impact 4. Auto-generate parts of OCL2Qual, to reduce repeated typing 5. OCL2Qual does not support pointers-to-pointers currently, but I believe no BIFs use that. 6. InsertBuiltinDeclarations builds up an AST function declaration manually, perhaps there is a helper function for this. 7. There is a FIXME in SemaDecl.cpp that needs to be implemented. Repository: rC Clang https://reviews.llvm.org/D53023 Files: Sema/SemaDecl.cpp Sema/SemaExpr.cpp SemaOpenCL/builtin-new.cl TableGen/CMakeLists.txt TableGen/ClangOpenCLBuiltinEmitter.cpp TableGen/TableGen.cpp TableGen/TableGenBackends.h clang/Basic/CMakeLists.txt clang/Basic/OpenCLBuiltins.td
Index: TableGen/TableGenBackends.h =================================================================== --- TableGen/TableGenBackends.h +++ TableGen/TableGenBackends.h @@ -81,6 +81,7 @@ void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records, raw_ostream &OS); +void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS); } // end namespace clang #endif Index: TableGen/TableGen.cpp =================================================================== --- TableGen/TableGen.cpp +++ TableGen/TableGen.cpp @@ -61,7 +61,8 @@ GenDiagDocs, GenOptDocs, GenDataCollectors, - GenTestPragmaAttributeSupportedAttributes + GenTestPragmaAttributeSupportedAttributes, + GenClangOpenCLBuiltins, }; namespace { @@ -161,7 +162,9 @@ clEnumValN(GenTestPragmaAttributeSupportedAttributes, "gen-clang-test-pragma-attribute-supported-attributes", "Generate a list of attributes supported by #pragma clang " - "attribute for testing purposes"))); + "attribute for testing purposes"), + clEnumValN(GenClangOpenCLBuiltins, "gen-clang-opencl-builtins", + "Generate OpenCL builtin handlers"))); cl::opt<std::string> ClangComponent("clang-component", @@ -288,6 +291,9 @@ case GenTestPragmaAttributeSupportedAttributes: EmitTestPragmaAttributeSupportedAttributes(Records, OS); break; + case GenClangOpenCLBuiltins: + EmitClangOpenCLBuiltins(Records, OS); + break; } return false; Index: TableGen/ClangOpenCLBuiltinEmitter.cpp =================================================================== --- /dev/null +++ TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -0,0 +1,195 @@ +//===- ClangOpenCLBuiltinEmitter.cpp - Generate Clang OpenCL Builtin handling +//=-*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits Clang OpenCL Builtin checking code. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" + +#include <set> + +using namespace llvm; + +namespace { +class BuiltinNameEmitter { +public: + BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS) + : Records(Records), OS(OS) {} + + void Emit(); + +private: + RecordKeeper &Records; + raw_ostream &OS; + + void EmitDeclarations(); + void EmitTable(); + void GetOverloads(); + + MapVector<StringRef, std::vector<std::pair<const Record *, unsigned>>> + OverloadInfo; + std::vector<std::pair<std::vector<Record *>, unsigned>> ArgTypesSet; +}; +} // namespace + +void BuiltinNameEmitter::GetOverloads() { + unsigned CumulativeArgIndex = 0; + std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); + for (const auto *B : Builtins) { + StringRef BName = B->getValueAsString("name"); + + if (OverloadInfo.find(BName) == OverloadInfo.end()) { + OverloadInfo.insert(std::make_pair( + BName, std::vector<std::pair<const Record *, unsigned>>{})); + } + + auto Args = B->getValueAsListOfDefs("args"); + auto it = + std::find_if(ArgTypesSet.begin(), ArgTypesSet.end(), + [&](const std::pair<std::vector<Record *>, unsigned> &a) { + return a.first == Args; + }); + unsigned ArgIndex; + if (it == ArgTypesSet.end()) { + ArgTypesSet.push_back(std::make_pair(Args, CumulativeArgIndex)); + ArgIndex = CumulativeArgIndex; + CumulativeArgIndex += Args.size(); + } else { + ArgIndex = it->second; + } + OverloadInfo[BName].push_back(std::make_pair(B, ArgIndex)); + } +} + +void BuiltinNameEmitter::EmitDeclarations() { + OS << "enum OpenCLTypeID {\n"; + std::vector<Record *> Types = Records.getAllDerivedDefinitions("Type"); + StringMap<bool> TypesSeen; + for (const auto *T : Types) { + if (TypesSeen.find(T->getValueAsString("name")) == TypesSeen.end()) + OS << " OCLT_" + T->getValueAsString("name") << ",\n"; + TypesSeen.insert(std::make_pair(T->getValueAsString("name"), true)); + } + OS << "};\n"; + + OS << R"( +struct OpenCLType { + OpenCLTypeID ID; + unsigned VectorWidth; + unsigned isPointer; + clang::LangAS AS; +}; + +struct OpenCLBuiltinDecl { + OpenCLType ReturnType; + unsigned NumArgs; + unsigned ArgTableIndex; + const char* Extension; + unsigned Version; +}; + +)"; +} + +void BuiltinNameEmitter::EmitTable() { + OS << "OpenCLBuiltinDecl OpenCLBuiltins[] = {\n"; + for (auto &i : OverloadInfo) { + StringRef Name = i.first; + OS << "// " << Name << "\n"; + for (auto &Overload : i.second) { + Record *ReturnType = Overload.first->getValueAsDef("returnType"); + OS << " { { OCLT_" << ReturnType->getValueAsString("name") << ", " + << ReturnType->getValueAsInt("vecWidth") << ", " + << ReturnType->getValueAsInt("isPointer") << ", " + << ReturnType->getValueAsString("as") << ", }, " + << Overload.first->getValueAsListOfDefs("args").size() << ", " + << Overload.second << ", " << '"' + << Overload.first->getValueAsString("extension") << "\", " + << Overload.first->getValueAsDef("version")->getValueAsInt("version") + << ", " + << " },\n"; + } + } + OS << "};\n\n"; +} + +void BuiltinNameEmitter::Emit() { + emitSourceFileHeader("OpenCL Builtin handling", OS); + + OS << "#include \"llvm/ADT/StringRef.h\"\n\n"; + + EmitDeclarations(); + + GetOverloads(); + + std::vector<std::vector<Record *>> ArgTypes; + + OS << "OpenCLType OpenCLArgTypes[] = {\n"; + for (auto &P : ArgTypesSet) { + OS << "// " << P.second << "\n"; + for (Record *R : P.first) { + OS << "{ OCLT_" << R->getValueAsString("name") << ", " + << R->getValueAsInt("vecWidth") << ", " + << R->getValueAsInt("isPointer") << ", " << R->getValueAsString("as") + << ", " + << "}, "; + OS << "\n"; + } + } + OS << "};\n\n"; + + EmitTable(); + + // Construct a StringMatcher. + std::vector<StringMatcher::StringPair> validBuiltins; + unsigned CumulativeIndex = 1; + for (auto &i : OverloadInfo) { + auto &Ov = i.second; + std::string RetStmt; + raw_string_ostream SS(RetStmt); + SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size() + << ");"; + SS.flush(); + CumulativeIndex += Ov.size(); + + validBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt)); + } + + OS << "// Return 0 if name is not a recognized OpenCL builtin, or an index\n" + "// into a table of declarations if it is an OpenCL builtin.\n" + "std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef name) " + "{\n"; + + StringMatcher("name", validBuiltins, OS).Emit(0, true); + + OS << " return std::make_pair(0, 0);\n"; + OS << "}\n"; +} + +namespace clang { + +void EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { + BuiltinNameEmitter NameChecker(Records, OS); + NameChecker.Emit(); +} + +} // end namespace clang Index: TableGen/CMakeLists.txt =================================================================== --- TableGen/CMakeLists.txt +++ TableGen/CMakeLists.txt @@ -8,6 +8,7 @@ ClangCommentHTMLTagsEmitter.cpp ClangDataCollectorsEmitter.cpp ClangDiagnosticsEmitter.cpp + ClangOpenCLBuiltinEmitter.cpp ClangOptionDocEmitter.cpp ClangSACheckersEmitter.cpp NeonEmitter.cpp Index: SemaOpenCL/builtin-new.cl =================================================================== --- /dev/null +++ SemaOpenCL/builtin-new.cl @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL1.2 -DCL12 +// RUN: %clang_cc1 %s -triple spir -verify -pedantic -fsyntax-only -cl-std=CL2.0 -DCL20 + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef int int4 __attribute__((ext_vector_type(4))); +typedef int int2 __attribute__((ext_vector_type(2))); +typedef unsigned int uint; + +kernel void test(global float4* buf, global int4* res) +{ + res[0] = convert_int4(buf[0]); +} + +kernel void test2(global int* bar) { + bar[0] = foo(bar); +} + +kernel void test3(constant int* bar, global int* baz) { + baz[0] = foo(bar); +#ifdef CL12 +// expected-error@-2{{passing '__constant int *' to parameter of type '__global int *' changes address space of pointer}} +#endif +} + +kernel void test4(image2d_t img, int2 coord, global float4* out) { + out[0] = read_imagef(img, coord); +} + +#ifdef CL20 +kernel void test5(global uint* out) { + out[0] = get_sub_group_size(); +// expected-error@-1{{use of declaration 'get_sub_group_size' requires cl_khr_subgroups extension to be enabled}} +#pragma OPENCL EXTENSION cl_khr_subgroups : enable + out[1] = get_sub_group_size(); +} +#endif Index: Sema/SemaExpr.cpp =================================================================== --- Sema/SemaExpr.cpp +++ Sema/SemaExpr.cpp @@ -46,6 +46,9 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "llvm/Support/ConvertUTF.h" + +#include "clang/Basic/OpenCLBuiltins.inc" + using namespace clang; using namespace sema; @@ -2029,6 +2032,114 @@ TemplateArgs); } +// Convert an OpenCLType into a QualType +static QualType OCL2Qual(ASTContext &Context, OpenCLType Ty) { + QualType RT = Context.VoidTy; + + switch (Ty.ID) { + case OCLT_size_t: + RT = Context.getSizeType(); + break; + case OCLT_int: + RT = Context.IntTy; + break; + case OCLT_uint: + RT = Context.UnsignedIntTy; + break; + case OCLT_char: + RT = Context.CharTy; + break; + case OCLT_uchar: + RT = Context.UnsignedCharTy; + break; + case OCLT_short: + RT = Context.ShortTy; + break; + case OCLT_ushort: + RT = Context.UnsignedShortTy; + break; + case OCLT_long: + RT = Context.LongTy; + break; + case OCLT_ulong: + RT = Context.UnsignedLongTy; + break; + case OCLT_float: + RT = Context.FloatTy; + break; + case OCLT_double: + RT = Context.DoubleTy; + break; + case OCLT_image2d_ro_t: + RT = Context.OCLImage2dROTy; + break; + default: + assert(0 && "unexpected type!"); + break; + } + + if (Ty.VectorWidth > 0) + RT = Context.getExtVectorType(RT, Ty.VectorWidth); + + if (Ty.isPointer != 0) { + RT = Context.getAddrSpaceQualType(RT, Ty.AS); + RT = Context.getPointerType(RT); + } + return RT; +} + +// Insert the OpenCL builtins into the AST +static void InsertBuiltinDeclarations(Sema &S, LookupResult &R, + DeclarationNameInfo &DNI, Scope *SC, + IdentifierResolver &I, Preprocessor &PP, + unsigned Index, unsigned Len) { + for (unsigned i = 0; i < Len; ++i) { + OpenCLBuiltinDecl &Decl = OpenCLBuiltins[Index - 1 + i]; + ASTContext &Context = S.Context; + + // Ignore this BIF if the the version is incorrect. + if (Context.getLangOpts().OpenCLVersion < Decl.Version) + continue; + + FunctionProtoType::ExtProtoInfo PI; + PI.Variadic = false; + + QualType RT = OCL2Qual(Context, Decl.ReturnType); + + SmallVector<QualType, 5> ArgTypes; + for (unsigned i = 0; i < Decl.NumArgs; i++) { + QualType Ty = OCL2Qual(Context, OpenCLArgTypes[Decl.ArgTableIndex + i]); + ArgTypes.push_back(Ty); + } + + DeclContext *DC = Context.getTranslationUnitDecl(); + + QualType Proto = Context.getFunctionType(RT, ArgTypes, PI); + auto *NewFD = FunctionDecl::Create(Context, DC, SourceLocation(), DNI, + Proto, nullptr, SC_None, false, true); + + NewFD->addAttr(OverloadableAttr::CreateImplicit(Context)); + + if (strlen(Decl.Extension)) + S.setOpenCLExtensionForDecl(NewFD, Decl.Extension); + + llvm::SmallVector<ParmVarDecl *, 16> Params; + for (unsigned i = 0u; i < Decl.NumArgs; ++i) { + std::string arg_name = "a" + std::to_string(i); + Params.push_back( + ParmVarDecl::Create(Context, NewFD, SourceLocation(), + SourceLocation(), PP.getIdentifierInfo(arg_name), + ArgTypes[i], nullptr, SC_None, nullptr)); + } + NewFD->setParams(Params); + + DC->addDecl(NewFD); + SC->AddDecl(NewFD); + I.AddDecl(NewFD); + R.addDecl(NewFD); + } +} + ExprResult Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, UnqualifiedId &Id, @@ -2133,8 +2244,24 @@ // This could be an implicitly declared function reference (legal in C90, // extension in C99, forbidden in C++). if (R.empty() && HasTrailingLParen && II && !getLangOpts().CPlusPlus) { - NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *II, S); - if (D) R.addDecl(D); + if (getLangOpts().OpenCL) { + auto Index = + isOpenCLBuiltin(NameInfo.getName().getAsIdentifierInfo()->getName()); + if (Index.first) { + // This is an OpenCL builtin name. Insert the declarations. + InsertBuiltinDeclarations(*this, R, NameInfo, S, IdResolver, PP, + Index.first, Index.second); + R.resolveKind(); + } else { + NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *II, S); + if (D) + R.addDecl(D); + } + } else { + NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *II, S); + if (D) + R.addDecl(D); + } } // Determine whether this name might be a candidate for Index: Sema/SemaDecl.cpp =================================================================== --- Sema/SemaDecl.cpp +++ Sema/SemaDecl.cpp @@ -896,6 +896,10 @@ if (getLangOpts().CPlusPlus) return BuildDeclarationNameExpr(SS, Result, /*ADL=*/true); + // FIXME: Handle OpenCL builtins here, the same as SemaExpr.cpp. + // This is for cases like write_imagef, that have a void return type. + // Or if you call a function and don't assign the result anywhere. + // C90 6.3.2.2: // If the expression that precedes the parenthesized argument list in a // function call consists solely of an identifier, and if no Index: clang/Basic/OpenCLBuiltins.td =================================================================== --- /dev/null +++ clang/Basic/OpenCLBuiltins.td @@ -0,0 +1,168 @@ +//==--- OpenCLBuiltins.td - OpenCL builtin definitions --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class Type<string Name> { + string name = Name; + int vecWidth = 0; + int isPointer = 0; + string as = "clang::LangAS::Default"; + int has_rounding = 0; + int is_integer = 0; +} + +class VectorType<Type Ty, int VecWidth> : Type<Ty.name> { + int vecWidth = VecWidth; +} + +class AddressSpace<string _as> { + string as = _as; +} + +def Global: AddressSpace<"clang::LangAS::opencl_global">; +def Constant: AddressSpace<"clang::LangAS::opencl_constant">; +def Generic: AddressSpace<"clang::LangAS::opencl_generic">; + +class PointerType<Type Ty, AddressSpace AS = Global> : Type<Ty.name> { + int isPointer = 1; + string as = AS.as; +} + +class Version<int v> { + int version = v; +} + +def CL10: Version<100>; +def CL11: Version<110>; +def CL12: Version<120>; +def CL20: Version<200>; + +def void_t: Type<"void">; + +def size_t_t: Type<"size_t">; + +let is_integer = 1 in { + def char_t : Type<"char">; + def uchar_t : Type<"uchar">; + def short_t : Type<"short">; + def ushort_t : Type<"ushort">; + def int_t : Type<"int">; + def uint_t : Type<"uint">; + def long_t : Type<"long">; + def ulong_t : Type<"ulong">; +} +def int2 : VectorType<int_t, 2>; + +def half_t : Type<"half">; +let has_rounding = 1 in { + def float_t : Type<"float">; + def double_t : Type<"double">; +} + +def float4_t : VectorType<float_t, 4>; + +def image2d_ro_t : Type<"image2d_ro_t">; + +class Builtin<string Name, Type ReturnType, list<Type> Args> { + string name = Name; + string extension; + Type returnType = ReturnType; + list<Type> args = Args; + string extension = ""; + Version version = CL10; +} + +// Creates builtins for one argument BIFs, taking and returning the same type. +multiclass bi_vec<string name, Type ReturnType> { + def: Builtin<name, ReturnType, [ReturnType]>; + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin<name, VectorType<ReturnType, v>, [VectorType<ReturnType, v>]>; + } +} + +// Creates builtins for two argument BIFs, taking and returning the same type. +multiclass bi_vec2<string name, Type ReturnType> { + def: Builtin<name, ReturnType, [ReturnType]>; + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin<name, VectorType<ReturnType, v>, [VectorType<ReturnType, v>, VectorType<ReturnType, v>]>; + } +} + +// Creates builtins for two argument BIFs, taking a vector and a scale and returning the vector type. +multiclass bi_vec3<string name, Type ReturnType> { + foreach v = [2, 3, 4, 8, 16] in { + def : Builtin<name, VectorType<ReturnType, v>, [VectorType<ReturnType, v>, ReturnType]>; + } +} + +// 6.12.2 +foreach name = ["acos", "acosh", "acospi", "asin", "asinh", "asinpi", "atan"] in { + foreach type = [float_t, double_t] in { + defm name#type : bi_vec<name, type>; + } +} + +foreach name = ["atan2"] in { + foreach type = [float_t, double_t] in { + defm name#type : bi_vec2<name, type>; + } +} + +foreach name = ["fmax", "fmin"] in { + foreach type = [float_t, double_t] in { + defm: bi_vec2<name, type>; + defm: bi_vec3<name, type>; + } +} + +// example 'foo', to show using 'version' +def: Builtin<"foo", int_t, [PointerType<int_t, Global>]>; +let version = CL20 in { + def: Builtin<"foo", int_t, [PointerType<int_t, Constant>]>; +} + +// Helper classes for the convert_ BIFs. +class SatModes<Type ty> { + list<string> modes = !if(ty.is_integer, ["", "_sat"], [""]); +} + +class RoundingModes<Type ty, Type ty2> { + list<string> modes = !if(!or(ty.has_rounding, ty2.has_rounding), ["", "_rte", "_rtz", "_rtp", "_rtn"], [""]); +} + +// Generate the convert_ builtins. +foreach type = [float_t, double_t, char_t, uchar_t, short_t, ushort_t, + int_t, uint_t, long_t, ulong_t] in { + foreach type2 = [float_t, double_t, char_t, uchar_t, short_t, ushort_t, + int_t, uint_t, long_t, ulong_t] in { + foreach sat = SatModes<type>.modes in { + foreach rte = RoundingModes<type, type2>.modes in { + def: Builtin<"convert_" # type.name # sat # rte, type, [type2]>; + foreach v = [2, 3, 4, 8, 16] in { + def: Builtin<"convert_" # type.name # v # sat # rte, VectorType<type, v>, [VectorType<type2, v>]>; + } + } + } + } +} + +// Example showing 'extension' +let extension = "cl_khr_subgroups" in { + def : Builtin<"get_sub_group_size", uint_t, []>; +} + +// samplerless read image +def : Builtin<"read_imagef", float4_t, [image2d_ro_t, VectorType<int_t, 2>]>; + +// 6.11.1 +def get_work_dim : Builtin<"get_work_dim", uint_t, []>; +foreach name = ["get_global_size", "get_global_id", "get_local_size", + "get_local_id", "get_num_groups", "get_group_id", + "get_global_offset"] in { + def name : Builtin<name, size_t_t, [uint_t]>; +} Index: clang/Basic/CMakeLists.txt =================================================================== --- clang/Basic/CMakeLists.txt +++ clang/Basic/CMakeLists.txt @@ -41,6 +41,12 @@ TARGET ClangAttrHasAttributeImpl ) +clang_tablegen(OpenCLBuiltins.inc + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ -gen-clang-opencl-builtins + SOURCE OpenCLBuiltins.td + TARGET ClangOpenCLBuiltinsImpl + ) + # ARM NEON clang_tablegen(arm_neon.inc -gen-arm-neon-sema SOURCE arm_neon.td
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits