Hi all, I've been working on #23529.
The abi tag mangling implemented by gcc is horrible, but I think my patch covers most of the incompatibilities with gcc5. There might be some bugs with substitutions, although I have to come up with a test case for that to see what gcc does... Test cases comparing gcc and patched clang++: http://files.stbuehler.de/test-itanium-mangle.html (generated with http://files.stbuehler.de/test-itanium-mangle.sh, also attached) regards, Stefan
Author: Stefan Bühler <stbueh...@web.de> add gcc abi_tag support - parse abi_tag attribute - emit abi tags in name Itanium Mangling - try to emit the same tags as gcc5 diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4b8a7b7..8f8b9ba 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -344,6 +344,14 @@ class IgnoredAttr : Attr { // Attributes begin here // +def AbiTag : Attr { + let Spellings = [GCC<"abi_tag">]; + let Args = [VariadicStringArgument<"Tags">]; + let Subjects = SubjectList<[Struct, Var, Function, Namespace], ErrorDiag, + "ExpectedStructClassVariableFunctionMethodOrInlineNamespace">; + let Documentation = [Undocumented]; +} + def AddressSpace : TypeAttr { let Spellings = [GNU<"address_space">]; let Args = [IntArgument<"AddressSpace">]; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6ac5748..17f46fa 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2336,7 +2336,8 @@ def warn_attribute_wrong_decl_type : Warning< "Objective-C instance methods|init methods of interface or class extension declarations|" "variables, functions and classes|Objective-C protocols|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" - "interface or protocol declarations|kernel functions}1">, + "interface or protocol declarations|kernel functions|" + "structs, classes, variables, functions, methods and inline namespaces}1">, InGroup<IgnoredAttributes>; def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>; def warn_type_attribute_wrong_type : Warning< @@ -4013,6 +4014,13 @@ def err_definition_of_explicitly_defaulted_member : Error< def err_redefinition_extern_inline : Error< "redefinition of a 'extern inline' function %0 is not supported in " "%select{C99 mode|C++}1">; +def err_attr_abi_tag_only_on_inline_namespace : + Error<"abi_tag attribute only allowed on inline namespaces">; +def err_abi_tag_on_redeclaration : + Error<"cannot add abi_tag attribute in redeclaration">; +def err_new_abi_tag_on_redeclaration : + Error<"abi_tag %0 missing in original declaration">; + def note_deleted_dtor_no_operator_delete : Note< "virtual destructor requires an unambiguous, accessible 'operator delete'">; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index ca456b2..c3f0841 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -852,7 +852,8 @@ enum AttributeDeclKind { ExpectedStructOrUnionOrTypedef, ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, - ExpectedKernelFunction + ExpectedKernelFunction, + ExpectedStructClassVariableFunctionMethodOrInlineNamespace }; } // end namespace clang diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 3f40743..bb16ebe 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -214,6 +214,8 @@ public: class CXXNameMangler { ItaniumMangleContextImpl &Context; raw_ostream &Out; + bool NullOut = false; + bool DisableDerivedAbiTags = false; /// The "structor" is the top-level declaration being mangled, if /// that's not a template specialization; otherwise it's the pattern @@ -263,6 +265,167 @@ class CXXNameMangler { } FunctionTypeDepth; + // abi_tag is a gcc attribute, taking one or more strings called "tags". + // + // the goal is to annotage against which version of a library an object was + // build and to be able to provide backwards compatibility ("dual abi"). + // + // for this the emitted mangled names have to be different, while you don't + // want the user to have to use different names in the source. + // + // the abi_tag can be present on Struct, Var and Function declarations as + // "explicit" tag, and on inline Namespace as "implicit" tag. Explicit tags + // are always emitted after the unqualified name, and (implicit) tags on + // namespace are not. + // + // For functions and variables there is a set of "implicitly available" + // tags. These tags are: all tags from the namespace/structs the name is + // embedded in, all tags from any template arguments of the name, and, for + // functions, alls tags used anywhere in the <bare-function-type> (i.e. + // parameters and sometimes the return type). + // + // For functions this is basically the list of all tags from the signature + // without the unqualified name and usually without the return type of the + // function. In `operator Type()` Type is NOT part of that list, as it is + // part of the unqualified name! + // + // Now all tags from the function return type/variable type which are not + // "implicitly available" must be added to the explicit list of tags, and + // are emitted after the unqualified name. + // + // Example: + // namespace std { + // inline namespace __cxx11 __attribute__((__abi_tag__("cxx11"))) { } + // inline namespace __cxx11 { + // struct string { }; + // } + // } + // + // std::string foo(); // needs abi tag "cxx11" on foo + // std::string foo(std::string); // does NOT need abi tag "cxx11" on foo + // __attribute__((__abi_tag__("cxx11"))) + // std::string foo2(std::string); // emit abi tag "cxx11" on foo anyway + // + // The tags are sorted by name before emitting, and are serialized as + // <abitag> ::= B <"tag" source-name> + + typedef SmallVector<StringRef, 4> AbiTagList; + + // state to gather all implicit and explicit tags used in a mangled name. + // must always have an instance of this while emitting any name to keep + // track. + // + // TODO(abitags): how to handle substituted names? they should add the tags used in + // the substitution to the list of available tags. + class AbiTagState final { + public: + //! all abi tags used implicitly or explicitly + std::set<StringRef> UsedAbiTags; + //! all explicit abi tags (i.e. not from namespace) + std::set<StringRef> EmittedAbiTags; + + AbiTagState* &LinkHead; + AbiTagState *Parent{nullptr}; + + bool LinkActive{false}; + + explicit AbiTagState(AbiTagState* &linkHead) + : LinkHead(linkHead) { + Parent = LinkHead; + LinkHead = this; + LinkActive = true; + } + + // no copy, no move + AbiTagState(AbiTagState const&) = delete; + AbiTagState& operator=(AbiTagState const&) = delete; + + ~AbiTagState() { + pop(); + } + + void pop() { + if (!LinkActive) return; + + assert(LinkHead == this && "abi tag link head must point to us on destruction"); + LinkActive = false; + if (Parent) { + Parent->UsedAbiTags.insert(UsedAbiTags.begin(), UsedAbiTags.end()); + Parent->EmittedAbiTags.insert(EmittedAbiTags.begin(), EmittedAbiTags.end()); + } + LinkHead = Parent; + } + + void write(raw_ostream &Out, const NamedDecl *ND, const AbiTagList *AdditionalAbiTags) { + ND = cast<NamedDecl>(ND->getCanonicalDecl()); + + if (dyn_cast<FunctionDecl>(ND) || dyn_cast<VarDecl>(ND)) { + // assert(AdditionalAbiTags && "function and variables need a list of additional abi tags"); + } else { + assert(!AdditionalAbiTags && "only function and variables need a list of additional abi tags"); + if (const auto* NS = dyn_cast<NamespaceDecl>(ND)) { + if (const auto* AbiTag = NS->getAttr<AbiTagAttr>()) { + for (const auto& Tag: AbiTag->tags()) { + UsedAbiTags.insert(Tag); + } + } + // don't emit abi tags for namespaces + return; + } + } + + AbiTagList TagList; + if (const auto* AbiTag = ND->getAttr<AbiTagAttr>()) { + for (const auto& Tag: AbiTag->tags()) { + UsedAbiTags.insert(Tag); + // AbiTag->tags() is sorted and has no duplicates + TagList.push_back(Tag); + } + } + + if (AdditionalAbiTags) { + for (const auto& Tag: *AdditionalAbiTags) { + UsedAbiTags.insert(Tag); + if (std::find(TagList.begin(), TagList.end(), Tag) == TagList.end()) { + // don't insert duplicates + TagList.push_back(Tag); + } + } + // AbiTag->tags() are already sorted; only add if we had additional tags + std::sort(TagList.begin(), TagList.end()); + } + + writeSortedUniqueAbiTags(Out, TagList); + } + + protected: + template<typename TagList> + void writeSortedUniqueAbiTags(raw_ostream &Out, TagList const& AbiTags) { + for (const auto& Tag: AbiTags) { + EmittedAbiTags.insert(Tag); + Out << "B"; + Out << Tag.size(); + Out << Tag; + } + } + } *AbiTags = nullptr; + AbiTagState AbiTagsRoot{AbiTags}; + + struct TemporaryDisableDerivedAbiTags { + bool& StateRef; + bool OldState; + + TemporaryDisableDerivedAbiTags(bool& State, bool Disable = true) + : StateRef(State) { + OldState = StateRef; + StateRef = Disable; + } + TemporaryDisableDerivedAbiTags(TemporaryDisableDerivedAbiTags const&) = delete; + ~TemporaryDisableDerivedAbiTags() { + StateRef = OldState; + } + }; + llvm::DenseMap<uintptr_t, unsigned> Substitutions; ASTContext &getASTContext() const { return Context.getASTContext(); } @@ -285,6 +448,10 @@ public: : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), SeqID(0) { } + CXXNameMangler(CXXNameMangler &Outer, llvm::raw_null_ostream &Out_) + : Context(Outer.Context), Out(Out_), NullOut(true), Structor(Outer.Structor), StructorType(Outer.StructorType), + SeqID(Outer.SeqID) { } + #if MANGLE_CHECKER ~CXXNameMangler() { if (Out.str()[0] == '\01') @@ -298,18 +465,21 @@ public: #endif raw_ostream &getStream() { return Out; } + void disableDerivedAbiTags() { DisableDerivedAbiTags = true; } + void mangle(const NamedDecl *D); void mangleCallOffset(int64_t NonVirtual, int64_t Virtual); void mangleNumber(const llvm::APSInt &I); void mangleNumber(int64_t Number); void mangleFloat(const llvm::APFloat &F); - void mangleFunctionEncoding(const FunctionDecl *FD); + void mangleFunctionEncoding(const FunctionDecl *FD, bool ExcludeUnqualifiedName = false); void mangleSeqID(unsigned SeqID); - void mangleName(const NamedDecl *ND); + void mangleName(const NamedDecl *ND, bool ExcludeUnqualifiedName = false); void mangleType(QualType T); void mangleNameOrStandardSubstitution(const NamedDecl *ND); private: + void writeAbiTags(const NamedDecl *ND, const AbiTagList *AdditionalAbiTags = nullptr); bool mangleSubstitution(const NamedDecl *ND); bool mangleSubstitution(QualType T); @@ -336,31 +506,49 @@ private: DeclarationName name, unsigned KnownArity = UnknownArity); - void mangleName(const TemplateDecl *TD, + void mangleFunctionEncodingBareType(const FunctionDecl *FD); + + void mangleNameWithAbiTags(const NamedDecl *ND, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName); + void mangleTemplateName(const TemplateDecl *TD, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs); - void mangleUnqualifiedName(const NamedDecl *ND) { - mangleUnqualifiedName(ND, ND->getDeclName(), UnknownArity); + void mangleUnqualifiedName(const NamedDecl *ND, const AbiTagList *AdditionalAbiTags) { + mangleUnqualifiedName(ND, ND->getDeclName(), UnknownArity, AdditionalAbiTags); } void mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name, - unsigned KnownArity); - void mangleUnscopedName(const NamedDecl *ND); - void mangleUnscopedTemplateName(const TemplateDecl *ND); - void mangleUnscopedTemplateName(TemplateName); + unsigned KnownArity, const AbiTagList *AdditionalAbiTags); + void mangleUnscopedName(const NamedDecl *ND, const AbiTagList *AdditionalAbiTags); + void mangleUnscopedTemplateName(const TemplateDecl *ND, + const AbiTagList *AdditionalAbiTags); + void mangleUnscopedTemplateName(TemplateName, + const AbiTagList *AdditionalAbiTags); void mangleSourceName(const IdentifierInfo *II); - void mangleLocalName(const Decl *D); + void mangleLocalName(const Decl *D, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName); void mangleBlockForPrefix(const BlockDecl *Block); void mangleUnqualifiedBlock(const BlockDecl *Block); void mangleLambda(const CXXRecordDecl *Lambda); void mangleNestedName(const NamedDecl *ND, const DeclContext *DC, - bool NoFunction=false); + const AbiTagList *AdditionalAbiTags, + bool NoFunction, + bool ExcludeUnqualifiedName); void mangleNestedName(const TemplateDecl *TD, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs); void manglePrefix(NestedNameSpecifier *qualifier); void manglePrefix(const DeclContext *DC, bool NoFunction=false); void manglePrefix(QualType type); - void mangleTemplatePrefix(const TemplateDecl *ND, bool NoFunction=false); + void mangleTemplatePrefix(const TemplateDecl *ND, + const AbiTagList *AdditionalAbiTags, + bool NoFunction = false, + bool ExcludeUnqualifiedName = false); void mangleTemplatePrefix(TemplateName Template); bool mangleUnresolvedTypeOrSimpleId(QualType DestroyedType, StringRef Prefix = ""); @@ -406,6 +594,10 @@ private: void mangleTemplateParameter(unsigned Index); void mangleFunctionParam(const ParmVarDecl *parm); + + std::set<StringRef> getTagsFromPrefixAndTemplateArguments(const NamedDecl *ND); + AbiTagList makeAdditionalTagsForFunction(const FunctionDecl *FD); + AbiTagList makeAdditionalTagsForVariable(const VarDecl *VD); }; } @@ -456,6 +648,11 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) { return true; } +void CXXNameMangler::writeAbiTags(const NamedDecl *ND, const AbiTagList *AdditionalAbiTags) { + assert(AbiTags && "require AbiTagState"); + if (AbiTags) AbiTags->write(Out, ND, DisableDerivedAbiTags ? nullptr : AdditionalAbiTags); +} + void CXXNameMangler::mangle(const NamedDecl *D) { // <mangled-name> ::= _Z <encoding> // ::= <data name> @@ -471,14 +668,28 @@ void CXXNameMangler::mangle(const NamedDecl *D) { mangleName(cast<FieldDecl>(D)); } -void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { - // <encoding> ::= <function name> <bare-function-type> - mangleName(FD); - +void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD, bool ExcludeUnqualifiedName) { // Don't mangle in the type if this isn't a decl we should typically mangle. - if (!Context.shouldMangleDeclName(FD)) + if (!Context.shouldMangleDeclName(FD)) { + mangleNameWithAbiTags(FD, /* AdditionalAbiTags */ nullptr, ExcludeUnqualifiedName); return; + } + + // <encoding> ::= <function name> <bare-function-type> + if (ExcludeUnqualifiedName) + { + // running makeAdditionalTagsForFunction would loop, don't need it here anyway + mangleNameWithAbiTags(FD, /* AdditionalAbiTags */ nullptr, ExcludeUnqualifiedName); + } else { + AbiTagList AdditionalAbiTags = makeAdditionalTagsForFunction(FD); + mangleNameWithAbiTags(FD, &AdditionalAbiTags, ExcludeUnqualifiedName); + } + + mangleFunctionEncodingBareType(FD); +} + +void CXXNameMangler::mangleFunctionEncodingBareType(const FunctionDecl *FD) { if (FD->hasAttr<EnableIfAttr>()) { FunctionTypeDepthState Saved = FunctionTypeDepth.push(); Out << "Ua9enable_ifI"; @@ -524,7 +735,7 @@ void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { FD = PrimaryTemplate->getTemplatedDecl(); } - mangleBareFunctionType(FD->getType()->getAs<FunctionType>(), + mangleBareFunctionType(FD->getType()->getAs<FunctionType>(), MangleReturnType); } @@ -582,7 +793,21 @@ isTemplate(const NamedDecl *ND, const TemplateArgumentList *&TemplateArgs) { return nullptr; } -void CXXNameMangler::mangleName(const NamedDecl *ND) { +// must not be run from mangleLocalName for the <entity name> as it would loop otherwise. +void CXXNameMangler::mangleName(const NamedDecl *ND, bool ExcludeUnqualifiedName) { + if (!ExcludeUnqualifiedName) { + if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { + AbiTagList VariableAdditionalAbiTags = makeAdditionalTagsForVariable(VD); + mangleNameWithAbiTags(VD, &VariableAdditionalAbiTags, ExcludeUnqualifiedName); + return; + } + } + mangleNameWithAbiTags(ND, nullptr, ExcludeUnqualifiedName); +} + +void CXXNameMangler::mangleNameWithAbiTags(const NamedDecl *ND, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName) { // <name> ::= <nested-name> // ::= <unscoped-name> // ::= <unscoped-template-name> <template-args> @@ -598,7 +823,7 @@ void CXXNameMangler::mangleName(const NamedDecl *ND) { while (!DC->isNamespace() && !DC->isTranslationUnit()) DC = getEffectiveParentContext(DC); else if (GetLocalClassDecl(ND)) { - mangleLocalName(ND); + mangleLocalName(ND, AdditionalAbiTags, ExcludeUnqualifiedName); return; } @@ -608,76 +833,88 @@ void CXXNameMangler::mangleName(const NamedDecl *ND) { // Check if we have a template. const TemplateArgumentList *TemplateArgs = nullptr; if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) { - mangleUnscopedTemplateName(TD); + if (!ExcludeUnqualifiedName) + mangleUnscopedTemplateName(TD, AdditionalAbiTags); mangleTemplateArgs(*TemplateArgs); return; } - mangleUnscopedName(ND); + if (!ExcludeUnqualifiedName) + mangleUnscopedName(ND, AdditionalAbiTags); return; } if (isLocalContainerContext(DC)) { - mangleLocalName(ND); + mangleLocalName(ND, AdditionalAbiTags, ExcludeUnqualifiedName); return; } - mangleNestedName(ND, DC); + mangleNestedName(ND, DC, AdditionalAbiTags, /* NoFunction */ false, ExcludeUnqualifiedName); } -void CXXNameMangler::mangleName(const TemplateDecl *TD, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs) { + +void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName, + const TemplateArgument *TemplateArgs, + unsigned NumTemplateArgs) { const DeclContext *DC = IgnoreLinkageSpecDecls(getEffectiveDeclContext(TD)); if (DC->isTranslationUnit() || isStdNamespace(DC)) { - mangleUnscopedTemplateName(TD); + if (!ExcludeUnqualifiedName) + mangleUnscopedTemplateName(TD, AdditionalAbiTags); mangleTemplateArgs(TemplateArgs, NumTemplateArgs); } else { - mangleNestedName(TD, TemplateArgs, NumTemplateArgs); + mangleNestedName(TD, AdditionalAbiTags, ExcludeUnqualifiedName, TemplateArgs, NumTemplateArgs); } } -void CXXNameMangler::mangleUnscopedName(const NamedDecl *ND) { +void CXXNameMangler::mangleUnscopedName(const NamedDecl *ND, const AbiTagList *AdditionalAbiTags) { // <unscoped-name> ::= <unqualified-name> // ::= St <unqualified-name> # ::std:: if (isStdNamespace(IgnoreLinkageSpecDecls(getEffectiveDeclContext(ND)))) Out << "St"; - mangleUnqualifiedName(ND); + mangleUnqualifiedName(ND, AdditionalAbiTags); } -void CXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *ND) { +void CXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *ND, + const AbiTagList *AdditionalAbiTags) { // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> if (mangleSubstitution(ND)) return; // <template-template-param> ::= <template-param> - if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) + if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) { + assert(!AdditionalAbiTags && "template template param cannot have abi tags"); // TODO(abitags) mangleTemplateParameter(TTP->getIndex()); - else - mangleUnscopedName(ND->getTemplatedDecl()); + } else { + mangleUnscopedName(ND->getTemplatedDecl(), AdditionalAbiTags); + } addSubstitution(ND); } -void CXXNameMangler::mangleUnscopedTemplateName(TemplateName Template) { +void CXXNameMangler::mangleUnscopedTemplateName(TemplateName Template, + const AbiTagList *AdditionalAbiTags) { // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> if (TemplateDecl *TD = Template.getAsTemplateDecl()) - return mangleUnscopedTemplateName(TD); + return mangleUnscopedTemplateName(TD, AdditionalAbiTags); if (mangleSubstitution(Template)) return; + assert(!AdditionalAbiTags && "dependent template name cannot have abi tags"); // TODO(abitags) + DependentTemplateName *Dependent = Template.getAsDependentTemplateName(); assert(Dependent && "Not a dependent template name?"); if (const IdentifierInfo *Id = Dependent->getIdentifier()) mangleSourceName(Id); else mangleOperatorName(Dependent->getOperator(), UnknownArity); - + addSubstitution(Template); } @@ -836,6 +1073,7 @@ void CXXNameMangler::mangleUnresolvedPrefix(NestedNameSpecifier *qualifier, else Out << "sr"; mangleSourceName(qualifier->getAsNamespace()->getIdentifier()); + writeAbiTags(qualifier->getAsNamespace()); break; case NestedNameSpecifier::NamespaceAlias: if (qualifier->getPrefix()) @@ -844,6 +1082,7 @@ void CXXNameMangler::mangleUnresolvedPrefix(NestedNameSpecifier *qualifier, else Out << "sr"; mangleSourceName(qualifier->getAsNamespaceAlias()->getIdentifier()); + writeAbiTags(qualifier->getAsNamespaceAlias()); break; case NestedNameSpecifier::TypeSpec: @@ -878,6 +1117,7 @@ void CXXNameMangler::mangleUnresolvedPrefix(NestedNameSpecifier *qualifier, Out << "sr"; mangleSourceName(qualifier->getAsIdentifier()); + writeAbiTags(qualifier->getAsNamespaceAlias()); break; } @@ -923,7 +1163,8 @@ void CXXNameMangler::mangleUnresolvedName(NestedNameSpecifier *qualifier, void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name, - unsigned KnownArity) { + unsigned KnownArity, + const AbiTagList *AdditionalAbiTags) { unsigned Arity = KnownArity; // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> @@ -942,6 +1183,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, Out << 'L'; mangleSourceName(II); + writeAbiTags(ND, AdditionalAbiTags); break; } @@ -981,6 +1223,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, assert(FD->getIdentifier() && "Data member name isn't an identifier!"); mangleSourceName(FD->getIdentifier()); + // TODO(abitags): not emitting abi tags: internal name anyway break; } @@ -1001,6 +1244,9 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, assert(D->getDeclName().getAsIdentifierInfo() && "Typedef was not named!"); mangleSourceName(D->getDeclName().getAsIdentifierInfo()); + assert(!AdditionalAbiTags && "Type cannot have additional abi tags"); + // explicit abi tags are still possible; take from underlying type, not from typedef. + writeAbiTags(TD, nullptr); break; } @@ -1010,6 +1256,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // <lambda-sig> ::= <parameter-type>+ # Parameter types or 'v' for 'void'. if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) { if (Record->isLambda() && Record->getLambdaManglingNumber()) { + assert(!AdditionalAbiTags && "Lambda type cannot have additional abi tags"); mangleLambda(Record); break; } @@ -1021,6 +1268,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, if (UnnamedMangle > 1) Out << llvm::utostr(UnnamedMangle - 2); Out << '_'; + writeAbiTags(TD, AdditionalAbiTags); break; } @@ -1053,6 +1301,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // Otherwise, use the complete constructor name. This is relevant if a // class with a constructor is declared within a constructor. mangleCXXCtorType(Ctor_Complete); + writeAbiTags(ND, AdditionalAbiTags); break; case DeclarationName::CXXDestructorName: @@ -1064,6 +1313,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // Otherwise, use the complete destructor name. This is relevant if a // class with a destructor is declared within a destructor. mangleCXXDtorType(Dtor_Complete); + writeAbiTags(ND, AdditionalAbiTags); break; case DeclarationName::CXXOperatorName: @@ -1079,6 +1329,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, case DeclarationName::CXXConversionFunctionName: case DeclarationName::CXXLiteralOperatorName: mangleOperatorName(Name, Arity); + writeAbiTags(ND, AdditionalAbiTags); break; case DeclarationName::CXXUsingDirective: @@ -1095,7 +1346,9 @@ void CXXNameMangler::mangleSourceName(const IdentifierInfo *II) { void CXXNameMangler::mangleNestedName(const NamedDecl *ND, const DeclContext *DC, - bool NoFunction) { + const AbiTagList *AdditionalAbiTags, + bool NoFunction, + bool ExcludeUnqualifiedName) { // <nested-name> // ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> @@ -1115,30 +1368,35 @@ void CXXNameMangler::mangleNestedName(const NamedDecl *ND, // Check if we have a template. const TemplateArgumentList *TemplateArgs = nullptr; if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) { - mangleTemplatePrefix(TD, NoFunction); + mangleTemplatePrefix(TD, AdditionalAbiTags, NoFunction, ExcludeUnqualifiedName); mangleTemplateArgs(*TemplateArgs); } else { manglePrefix(DC, NoFunction); - mangleUnqualifiedName(ND); + if (!ExcludeUnqualifiedName) + mangleUnqualifiedName(ND, AdditionalAbiTags); } Out << 'E'; } void CXXNameMangler::mangleNestedName(const TemplateDecl *TD, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs) { // <nested-name> ::= N [<CV-qualifiers>] <template-prefix> <template-args> E Out << 'N'; - mangleTemplatePrefix(TD); + mangleTemplatePrefix(TD, AdditionalAbiTags, ExcludeUnqualifiedName); mangleTemplateArgs(TemplateArgs, NumTemplateArgs); Out << 'E'; } -void CXXNameMangler::mangleLocalName(const Decl *D) { +void CXXNameMangler::mangleLocalName(const Decl *D, + const AbiTagList *AdditionalAbiTags, + bool ExcludeUnqualifiedName) { // <local-name> := Z <function encoding> E <entity name> [<discriminator>] // := Z <function encoding> E s [<discriminator>] // <local-name> := Z <function encoding> E d [ <parameter number> ] @@ -1150,15 +1408,25 @@ void CXXNameMangler::mangleLocalName(const Decl *D) { Out << 'Z'; - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC)) - mangleObjCMethodName(MD); - else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) - mangleBlockForPrefix(BD); - else - mangleFunctionEncoding(cast<FunctionDecl>(DC)); + { + AbiTagState localAbiTags(AbiTags); + + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC)) + mangleObjCMethodName(MD); + else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) + mangleBlockForPrefix(BD); + else + mangleFunctionEncoding(cast<FunctionDecl>(DC)); + + // implicit abi tags (from namespace) are not available in the following + // entity; reset to actually emitted tags, which are available. + localAbiTags.UsedAbiTags = localAbiTags.EmittedAbiTags; + } Out << 'E'; + TemporaryDisableDerivedAbiTags TemporyDisable(DisableDerivedAbiTags, getStructor(dyn_cast<NamedDecl>(D)) != Structor); + if (RD) { // The parameter number is omitted for the last parameter, 0 for the // second-to-last parameter, 1 for the third-to-last parameter, etc. The @@ -1183,13 +1451,17 @@ void CXXNameMangler::mangleLocalName(const Decl *D) { // Mangle the name relative to the closest enclosing function. // equality ok because RD derived from ND above if (D == RD) { - mangleUnqualifiedName(RD); + if (!ExcludeUnqualifiedName) + mangleUnqualifiedName(RD, AdditionalAbiTags); } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { manglePrefix(getEffectiveDeclContext(BD), true /*NoFunction*/); - mangleUnqualifiedBlock(BD); + assert(!AdditionalAbiTags && "Block cannot have additional abi tags"); + if (!ExcludeUnqualifiedName) + mangleUnqualifiedBlock(BD); } else { const NamedDecl *ND = cast<NamedDecl>(D); - mangleNestedName(ND, getEffectiveDeclContext(ND), true /*NoFunction*/); + mangleNestedName(ND, getEffectiveDeclContext(ND), + AdditionalAbiTags, true /*NoFunction*/, ExcludeUnqualifiedName); } } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { // Mangle a block in a default parameter; see above explanation for @@ -1206,30 +1478,35 @@ void CXXNameMangler::mangleLocalName(const Decl *D) { } } - mangleUnqualifiedBlock(BD); + assert(!AdditionalAbiTags && "Block cannot have additional abi tags"); + if (!ExcludeUnqualifiedName) + mangleUnqualifiedBlock(BD); } else { - mangleUnqualifiedName(cast<NamedDecl>(D)); - } - - if (const NamedDecl *ND = dyn_cast<NamedDecl>(RD ? RD : D)) { - unsigned disc; - if (Context.getNextDiscriminator(ND, disc)) { - if (disc < 10) - Out << '_' << disc; - else - Out << "__" << disc << '_'; + if (!ExcludeUnqualifiedName) + mangleUnqualifiedName(cast<NamedDecl>(D), AdditionalAbiTags); + } + + if (!ExcludeUnqualifiedName) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(RD ? RD : D)) { + unsigned disc; + if (Context.getNextDiscriminator(ND, disc)) { + if (disc < 10) + Out << '_' << disc; + else + Out << "__" << disc << '_'; + } } } } void CXXNameMangler::mangleBlockForPrefix(const BlockDecl *Block) { if (GetLocalClassDecl(Block)) { - mangleLocalName(Block); + mangleLocalName(Block, /* AdditionalAbiTags */ nullptr, /* ExcludeUnqualifiedName */ false); return; } const DeclContext *DC = getEffectiveDeclContext(Block); if (isLocalContainerContext(DC)) { - mangleLocalName(Block); + mangleLocalName(Block, /* AdditionalAbiTags */ nullptr, /* ExcludeUnqualifiedName */ false); return; } manglePrefix(getEffectiveDeclContext(Block)); @@ -1240,10 +1517,11 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { if (Decl *Context = Block->getBlockManglingContextDecl()) { if ((isa<VarDecl>(Context) || isa<FieldDecl>(Context)) && Context->getDeclContext()->isRecord()) { - if (const IdentifierInfo *Name - = cast<NamedDecl>(Context)->getIdentifier()) { + const auto *ND = cast<NamedDecl>(Context); + if (const IdentifierInfo *Name = ND->getIdentifier()) { mangleSourceName(Name); - Out << 'M'; + writeAbiTags(ND, /* AdditionalAbiTags */ nullptr); + Out << 'M'; } } } @@ -1276,7 +1554,7 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { if (const IdentifierInfo *Name = cast<NamedDecl>(Context)->getIdentifier()) { mangleSourceName(Name); - Out << 'M'; + Out << 'M'; } } } @@ -1358,11 +1636,11 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) { // Check if we have a template. const TemplateArgumentList *TemplateArgs = nullptr; if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) { - mangleTemplatePrefix(TD); + mangleTemplatePrefix(TD, /* AdditionalAbiTags */ nullptr); mangleTemplateArgs(*TemplateArgs); } else { manglePrefix(getEffectiveDeclContext(ND), NoFunction); - mangleUnqualifiedName(ND); + mangleUnqualifiedName(ND, /* AdditionalAbiTags */ nullptr); } addSubstitution(ND); @@ -1373,27 +1651,30 @@ void CXXNameMangler::mangleTemplatePrefix(TemplateName Template) { // ::= <template-param> // ::= <substitution> if (TemplateDecl *TD = Template.getAsTemplateDecl()) - return mangleTemplatePrefix(TD); + return mangleTemplatePrefix(TD, /* AdditionalAbiTags */ nullptr); if (QualifiedTemplateName *Qualified = Template.getAsQualifiedTemplateName()) manglePrefix(Qualified->getQualifier()); - + if (OverloadedTemplateStorage *Overloaded = Template.getAsOverloadedTemplate()) { mangleUnqualifiedName(nullptr, (*Overloaded->begin())->getDeclName(), - UnknownArity); + UnknownArity, + /* AdditionalAbiTags */ nullptr); return; } - + DependentTemplateName *Dependent = Template.getAsDependentTemplateName(); assert(Dependent && "Unknown template name kind?"); if (NestedNameSpecifier *Qualifier = Dependent->getQualifier()) manglePrefix(Qualifier); - mangleUnscopedTemplateName(Template); + mangleUnscopedTemplateName(Template, /* AdditionalAbiTags */ nullptr); } void CXXNameMangler::mangleTemplatePrefix(const TemplateDecl *ND, - bool NoFunction) { + const AbiTagList *AdditionalAbiTags, + bool NoFunction, + bool ExcludeUnqualifiedName) { // <template-prefix> ::= <prefix> <template unqualified-name> // ::= <template-param> // ::= <substitution> @@ -1405,10 +1686,12 @@ void CXXNameMangler::mangleTemplatePrefix(const TemplateDecl *ND, // <template-template-param> ::= <template-param> if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) { + // TODO(abitags): ??? mangleTemplateParameter(TTP->getIndex()); } else { manglePrefix(getEffectiveDeclContext(ND), NoFunction); - mangleUnqualifiedName(ND->getTemplatedDecl()); + if (!ExcludeUnqualifiedName) + mangleUnqualifiedName(ND->getTemplatedDecl(), AdditionalAbiTags); } addSubstitution(ND); @@ -1452,6 +1735,7 @@ void CXXNameMangler::mangleType(TemplateName TN) { // <name> ::= <nested-name> mangleUnresolvedPrefix(Dependent->getQualifier()); mangleSourceName(Dependent->getIdentifier()); + // writeAbiTags(Dependent); break; } @@ -1543,16 +1827,19 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::Typedef: mangleSourceName(cast<TypedefType>(Ty)->getDecl()->getIdentifier()); + writeAbiTags(cast<TypedefType>(Ty)->getDecl()); break; case Type::UnresolvedUsing: mangleSourceName( cast<UnresolvedUsingType>(Ty)->getDecl()->getIdentifier()); + writeAbiTags(cast<UnresolvedUsingType>(Ty)->getDecl()); break; case Type::Enum: case Type::Record: mangleSourceName(cast<TagType>(Ty)->getDecl()->getIdentifier()); + writeAbiTags(cast<TagType>(Ty)->getDecl()); break; case Type::TemplateSpecialization: { @@ -1571,6 +1858,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, goto unresolvedType; mangleSourceName(TD->getIdentifier()); + writeAbiTags(TD); break; } @@ -1602,16 +1890,19 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::InjectedClassName: mangleSourceName( cast<InjectedClassNameType>(Ty)->getDecl()->getIdentifier()); + writeAbiTags(cast<InjectedClassNameType>(Ty)->getDecl()); break; case Type::DependentName: mangleSourceName(cast<DependentNameType>(Ty)->getIdentifier()); + // writeAbiTags(cast<DependentNameType>(Ty)); break; case Type::DependentTemplateSpecialization: { const DependentTemplateSpecializationType *DTST = cast<DependentTemplateSpecializationType>(Ty); mangleSourceName(DTST->getIdentifier()); + // writeAbiTags(DTST); mangleTemplateArgs(DTST->getArgs(), DTST->getNumArgs()); break; } @@ -2433,7 +2724,11 @@ void CXXNameMangler::mangleType(const InjectedClassNameType *T) { void CXXNameMangler::mangleType(const TemplateSpecializationType *T) { if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl()) { - mangleName(TD, T->getArgs(), T->getNumArgs()); + // types only have explicit abi tags, no addition tags + mangleTemplateName(TD, + /* AdditionalAbiTags */ nullptr, + /* ExcludeUnqualifiedName */ false, + T->getArgs(), T->getNumArgs()); } else { if (mangleSubstitution(QualType(T, 0))) return; @@ -2480,6 +2775,7 @@ void CXXNameMangler::mangleType(const DependentNameType *T) { Out << 'N'; manglePrefix(T->getQualifier()); mangleSourceName(T->getIdentifier()); + // writeAbiTags(T); // TODO(abitags) Out << 'E'; } @@ -3876,6 +4172,74 @@ void CXXNameMangler::addSubstitution(uintptr_t Ptr) { Substitutions[Ptr] = SeqID++; } +std::set<StringRef> CXXNameMangler::getTagsFromPrefixAndTemplateArguments(const NamedDecl *ND) { + llvm::raw_null_ostream NullOutStream; + CXXNameMangler TrackPrefixAndTemplateArguments(*this, NullOutStream); + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + TrackPrefixAndTemplateArguments.mangleFunctionEncoding(FD, /* ExcludeUnqualifiedName */ true); + } else { + TrackPrefixAndTemplateArguments.mangleName(ND, /* ExcludeUnqualifiedName */ true); + } + + return std::move(TrackPrefixAndTemplateArguments.AbiTagsRoot.UsedAbiTags); +} + +CXXNameMangler::AbiTagList CXXNameMangler::makeAdditionalTagsForFunction(const FunctionDecl *FD) { + // when derived abi tags are disabled there is no need to make any list + if (DisableDerivedAbiTags) return AbiTagList(); + + std::set<StringRef> ImplicitlyAvailableTags = getTagsFromPrefixAndTemplateArguments(FD); + std::set<StringRef> ReturnTypeTags; + + { + llvm::raw_null_ostream NullOutStream; + CXXNameMangler TrackReturnTypeTags(*this, NullOutStream); + + const FunctionProtoType *Proto = cast<FunctionProtoType>(FD->getType()->getAs<FunctionType>()); + TrackReturnTypeTags.FunctionTypeDepth.enterResultType(); + TrackReturnTypeTags.mangleType(Proto->getReturnType()); + TrackReturnTypeTags.FunctionTypeDepth.leaveResultType(); + + ReturnTypeTags = std::move(TrackReturnTypeTags.AbiTagsRoot.UsedAbiTags); + } + + AbiTagList AdditionalAbiTags; + + for (const auto& Tag: ReturnTypeTags) { + if (ImplicitlyAvailableTags.count(Tag) == 0) + AdditionalAbiTags.push_back(Tag); + } + + return AdditionalAbiTags; +} + +CXXNameMangler::AbiTagList CXXNameMangler::makeAdditionalTagsForVariable(const VarDecl *VD) { + // when derived abi tags are disabled there is no need to make any list + if (DisableDerivedAbiTags) return AbiTagList(); + + std::set<StringRef> ImplicitlyAvailableTags = getTagsFromPrefixAndTemplateArguments(VD); + std::set<StringRef> VariableTypeTags; + + { + llvm::raw_null_ostream NullOutStream; + CXXNameMangler TrackVariableType(*this, NullOutStream); + + TrackVariableType.mangleType(VD->getType()); + + VariableTypeTags = std::move(TrackVariableType.AbiTagsRoot.UsedAbiTags); + } + + AbiTagList AdditionalAbiTags; + + for (const auto& Tag: VariableTypeTags) { + if (ImplicitlyAvailableTags.count(Tag) == 0) + AdditionalAbiTags.push_back(Tag); + } + + return AdditionalAbiTags; +} + // /// Mangles the name of the declaration D and emits that name to the given @@ -3977,6 +4341,7 @@ void ItaniumMangleContextImpl::mangleStaticGuardVariable(const VarDecl *D, // <special-name> ::= GV <object name> # Guard variable for one-time // # initialization CXXNameMangler Mangler(*this, Out); + Mangler.disableDerivedAbiTags(); // GCC: doesn't emit derived abi tags for guard variables Mangler.getStream() << "_ZGV"; Mangler.mangleName(D); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 31ed9f2..ac85f30 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4167,6 +4167,58 @@ static void handleDeclspecThreadAttr(Sema &S, Decl *D, Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleAbiTagAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + + SmallVector<std::string, 4> Tags; + + for (unsigned I = 0, E = Attr.getNumArgs(); I != E; ++I) { + StringRef Tag; + + if (!S.checkStringLiteralArgumentAttr(Attr, I, Tag)) + return; + + Tags.push_back(Tag); + } + // store tags sorted and without duplicates + std::sort(Tags.begin(), Tags.end()); + Tags.erase(std::unique(Tags.begin(), Tags.end()), Tags.end()); + + if (const auto *NS = dyn_cast<NamespaceDecl>(D)) { + if (!NS->isInline()) { + S.Diag(Attr.getLoc(), diag::err_attr_abi_tag_only_on_inline_namespace); + return; + } + } + + const auto *CD = D->getCanonicalDecl(); + if (CD != D) { + // redeclarations must not add new abi tags, or abi tags in the first place + const auto *OldAbiTagAttr = D->getAttr<AbiTagAttr>(); + if (nullptr == OldAbiTagAttr) { + S.Diag(Attr.getLoc(), diag::err_abi_tag_on_redeclaration); + S.Diag(CD->getLocation(), diag::note_previous_definition); + return; + } + for (const auto& NewTag: Tags) { + if (std::find(OldAbiTagAttr->tags_begin(), + OldAbiTagAttr->tags_end(), + NewTag) == OldAbiTagAttr->tags_end()) { + S.Diag(Attr.getLoc(), diag::err_new_abi_tag_on_redeclaration) << NewTag; + S.Diag(OldAbiTagAttr->getLocation(), diag::note_previous_definition); + return; + } + } + return; + } + + D->addAttr(::new (S.Context) AbiTagAttr(Attr.getRange(), S.Context, + Tags.data(), Tags.size(), + Attr.getAttributeSpellingListIndex())); +} + static void handleARMInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. @@ -4982,6 +5034,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_Thread: handleDeclspecThreadAttr(S, D, Attr); break; + case AttributeList::AT_AbiTag: + handleAbiTagAttr(S, D, Attr); + break; // Thread safety attributes: case AttributeList::AT_AssertExclusiveLock:
======== Abi Tags ======== Introduction ============ This text tries to describe gcc semantic for mangling "abi_tag" attributes described in https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html There is no guarantee the following rules are correct, complete or make sense in any way as they were determined empirically by experiments with gcc5. Declaration =========== Abi tags are declared in an abi_tag attribute and can be applied to a function, variable, class or inline namespace declaration. The attribute takes one or more strings (called tags); the order does not matter. See https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html for details. Tags on an inline namespace are called "implicit tags", all other tags are "explicit tags". Mangling ======== All tags that are "active" on a <unqualified-name> are emitted after the <unqualified-name>, before <template-args> or <discriminator>, and are part of the same <substitution> the <unqualified-name> is. They are mangled as: <abi-tags> ::= <abi-tag>* # sort by name <abi-tag> ::= B <tag source-name> Example: __attribute__((abi_tag("test"))) void Func(); gets mangled as: _Z4FuncB4testv (prettified as `Func[abi:test]()`) Active tags =========== A namespace has never any active tags; for types (class / struct / union / enum) the explicit tags are the active tags. For variables and functions the active tags are the explicit tags plus any "required tags" which are not in the "available tags" set: derived-tags := (required-tags - available-tags) active-tags := explicit-tags + derived-tags Required tags for a function ============================ If a function is used as a local scope for another name, and is part of another function as local scope, it doesn't have any required tags. If a function is used as a local scope for a guard variable name, it doesn't have any required tags. Otherwise the function requires any implicit or explicit tag used in the name for the return type. Required tags for a variable ============================ A variable requires any implicit or explicit tag used in its type. Available tags ============== All tags used in the prefix and in the template arguments for a name are available; for functions also all tags from the <bare-function-type> (which might include the return type for template functions) are available. For <local-name>s all active tags used in the local part (<function- encoding>) are available, but not implicit tags which were not active! Implicit and explicit tags used in the <unqualified-name> for a function (as in the type of a cast operator) are NOT available. Example: a cast operator to std::string (which is std::__cxx11::basic_string<...>) will use 'cxx11' as active tag, as it is required from the return type `std::string` but not available.
test-itanium-mangle.sh
Description: application/shellscript
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits