Bug 23529: Add support for gcc's attribute abi_tag (needed for compatibility with gcc 5's libstdc++)
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 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; def err_attribute_wrong_decl_type : Error; 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 (i.e. + // parameters and sometimes the return type). + // + // For functions this is basically the list of all tags from the signature + // without the unqualifi
Re: Bug 23529: Add support for gcc's attribute abi_tag (needed for compatibility with gcc 5's libstdc++)
Hi, it seems moderation didn't approve the phabricator mails for D12834. (I have no intention to be subscribed to the list just to get phabricator mails through. For now I am subscribed but disabled mail delivery -.-) So the patch is available at http://reviews.llvm.org/D12834 I also added a test case which shows that substitution is not working correctly right now. regards, Stefan PS: I think contributing to llvm is way to complex. First I attatch patches in the bug tracker, and it takes ages to get a reaction - and them I'm simply told to send to the ML. Then you want me to use phabricator, and then I get no feedback again - because moderation blocked emails from an internal system, and it seems nobody is looking at phabricator directly. This is not something I look forward to go through again; my motiviation to contribute further is rather low. On Sat, 12 Sep 2015 12:36:19 -0700 David Majnemer wrote: > Would you mind sticking abi-tag.patch in phabricator: > http://llvm.org/docs/Phabricator.html ? > Your patch is big enough that it would make it a little easier for me > to review. > > On Sat, Sep 12, 2015 at 7:12 AM, Stefan Bühler via cfe-commits < > cfe-commits@lists.llvm.org> wrote: > > > 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) ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D12834: add gcc abi_tag support
stbuehler added a comment. Just for the record: substitution doesn't work yet (it should add available tags from the substitution, but doesn't). This example shows the difference: my patch mangles `T::F()` as `N::Name(N::__test::Result)::T::F[abi:test](N::__test::Result)`, but actually shouldn't emit the abi tag. namespace N { inline namespace __test __attribute__((abi_tag("test"))) { struct Result { const char *str; }; } inline Result Name(Result p1) { struct T { Result F(Result p2) { struct S { Result F(Result p3) { static Result s3 = p3; return s3; } }; static Result s2 = S().F(p2); return s2; } }; static Result s1 = T().F(p1); return s1; } void F() { // instantiate Name() Name(Result()); } } Repository: rL LLVM http://reviews.llvm.org/D12834 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D12834: add gcc abi_tag support
stbuehler updated this revision to Diff 44972. stbuehler marked an inline comment as done. stbuehler added a comment. - disable dervied abi tags in some recursions to fix infinite recursions - don't emit abi tags for NestedNameSpecifier::Identifier Repository: rL LLVM http://reviews.llvm.org/D12834 Files: docs/ItaniumMangleAbiTags.rst include/clang/Basic/Attr.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AttributeList.h lib/AST/ItaniumMangle.cpp lib/Sema/SemaDeclAttr.cpp Index: lib/Sema/SemaDeclAttr.cpp === --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4446,6 +4446,58 @@ Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleAbiTagAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) +return; + + SmallVector 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(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(); +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. @@ -5360,6 +5412,9 @@ 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: Index: lib/AST/ItaniumMangle.cpp === --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -212,6 +212,8 @@ 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 @@ -261,6 +263,167 @@ } 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 (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 namesp
Re: [PATCH] D12834: add gcc abi_tag support
stbuehler updated this revision to Diff 44973. stbuehler added a comment. integrate patch by Stephan Bergmann : - Fix handling of abi_tag attribute on namespaces to match GCC behavior: - Forbid the attribute on unnamed namespaces. (GCC merely produces a warning instead of an error for unnamed or non-inline namespaces, though.) - When no tags are given for a (named, inline) namespace, use the namespace's name as tag. Repository: rL LLVM http://reviews.llvm.org/D12834 Files: docs/ItaniumMangleAbiTags.rst include/clang/Basic/Attr.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AttributeList.h lib/AST/ItaniumMangle.cpp lib/Sema/SemaDeclAttr.cpp test/SemaCXX/attr-abi-tag-syntax.cpp test/SemaCXX/attr-abi-tag.cpp Index: test/SemaCXX/attr-abi-tag.cpp === --- /dev/null +++ test/SemaCXX/attr-abi-tag.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -x c++ -std=c++11 -triple x86_64-unknown-linux -emit-llvm < %s | FileCheck %s + +// CHECK: @_Z5Func1B6Names1v() +inline namespace Names1 __attribute__((__abi_tag__)) { +class C1 {}; +} +C1 Func1() { return C1(); } + +// CHECK: @_Z5Func2B4Tag1B4Tag2v() +inline namespace Names2 __attribute__((__abi_tag__("Tag1", "Tag2"))) { +class C2 {}; +} +C2 Func2() { return C2(); } Index: test/SemaCXX/attr-abi-tag-syntax.cpp === --- /dev/null +++ test/SemaCXX/attr-abi-tag-syntax.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +namespace N1 { + +namespace __attribute__((__abi_tag__)) {} // \ + // expected-error {{abi_tag attribute only allowed on inline namespaces}} + +namespace N __attribute__((__abi_tag__)) {} // \ + // expected-error {{abi_tag attribute only allowed on inline namespaces}} + +} + +namespace N2 { + +inline namespace __attribute__((__abi_tag__)) {} // \ + // expected-error {{abi_tag attribute only allowed on named namespaces}} + +inline namespace N __attribute__((__abi_tag__)) {} + +} Index: lib/Sema/SemaDeclAttr.cpp === --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4446,6 +4446,66 @@ Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleAbiTagAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + const auto *NS = dyn_cast(D); + + if (!checkAttributeAtLeastNumArgs(S, Attr, NS ? 0 : 1)) +return; + + SmallVector Tags; + + for (unsigned I = 0, E = Attr.getNumArgs(); I != E; ++I) { +StringRef Tag; + +if (!S.checkStringLiteralArgumentAttr(Attr, I, Tag)) + return; + +Tags.push_back(Tag); + } + + if (NS && !NS->isInline()) { +S.Diag(Attr.getLoc(), diag::err_attr_abi_tag_only_on_inline_namespace); +return; + } + if (NS && NS->isAnonymousNamespace()) { +S.Diag(Attr.getLoc(), diag::err_attr_abi_tag_only_on_named_namespace); +return; + } + if (NS && Attr.getNumArgs() == 0) { + Tags.push_back(NS->getName()); + } + + // store tags sorted and without duplicates + std::sort(Tags.begin(), Tags.end()); + Tags.erase(std::unique(Tags.begin(), Tags.end()), Tags.end()); + + 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(); +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. @@ -5360,6 +5420,9 @@ 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: Index: lib/AST/ItaniumMangle.cpp === --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -212,6 +212,8 @@ class CXXNameMangler { ItaniumMangleContextImpl &Context; raw_ostream &Out; + bool NullOut = false; +