From d150f82faeafb0dc866eb460999d1f92927f630f Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Wed, 25 Sep 2019 18:00:31 +0000 Subject: [PATCH] [Mangle] Add flag to asm labels to disable '\01' prefixing LLDB synthesizes decls using asm labels. These decls cannot have a mangle different than the one specified in the label name. I.e., the '\01' prefix should not be added. Fixes an expression evaluation failure in lldb's TestVirtual.py on iOS. rdar://45827323 Differential Revision: https://reviews.llvm.org/D67774 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@372903 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 20 ++++++++++++-- include/clang/Basic/AttrDocs.td | 24 ++++++++++++++++ lib/AST/Mangle.cpp | 12 ++++++-- lib/Sema/SemaDecl.cpp | 15 +++++----- unittests/AST/DeclTest.cpp | 49 +++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 12 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index dfc85908bb..bca51f2f7d 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -722,9 +722,25 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr { def AsmLabel : InheritableAttr { let Spellings = [Keyword<"asm">, Keyword<"__asm__">]; - let Args = [StringArgument<"Label">]; + let Args = [ + // Label specifies the mangled name for the decl. + StringArgument<"Label">, + + // IsLiteralLabel specifies whether the label is literal (i.e. suppresses + // the global C symbol prefix) or not. If not, the mangle-suppression prefix + // ('\01') is omitted from the decl name at the LLVM IR level. + // + // Non-literal labels are used by some external AST sources like LLDB. + BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1> + ]; let SemaHandler = 0; - let Documentation = [Undocumented]; + let Documentation = [AsmLabelDocs]; + let AdditionalMembers = +[{ +bool isEquivalent(AsmLabelAttr *Other) const { + return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel(); +} +}]; } def Availability : InheritableAttr { diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 2e9ec57be7..114a9856c5 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -2558,6 +2558,30 @@ manipulating bits of the enumerator when issuing warnings. }]; } +def AsmLabelDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute can be used on a function or variable to specify its symbol name. + +On some targets, all C symbols are prefixed by default with a single character, typically ``_``. This was done historically to distinguish them from symbols used by other languages. (This prefix is also added to the standard Itanium C++ ABI prefix on "mangled" symbol names, so that e.g. on such targets the true symbol name for a C++ variable declared as ``int cppvar;`` would be ``__Z6cppvar``; note the two underscores.) This prefix is *not* added to the symbol names specified by the ``asm`` attribute; programmers wishing to match a C symbol name must compensate for this. + +For example, consider the following C code: + +.. code-block:: c + + int var1 asm("altvar") = 1; // "altvar" in symbol table. + int var2 = 1; // "_var2" in symbol table. + + void func1(void) asm("altfunc"); + void func1(void) {} // "altfunc" in symbol table. + void func2(void) {} // "_func2" in symbol table. + +Clang's implementation of this attribute is compatible with GCC's, `documented here `_. + +While it is possible to use this attribute to name a special symbol used internally by the compiler, such as an LLVM intrinsic, this is neither recommended nor supported and may cause the compiler to crash or miscompile. Users who wish to gain access to intrinsic behavior are strongly encouraged to request new builtin functions. + }]; +} + def EnumExtensibilityDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 28de87fde9..b158fe85a4 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -122,15 +122,21 @@ void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) { if (const AsmLabelAttr *ALA = D->getAttr()) { // If we have an asm name, then we use it as the mangling. + // If the label isn't literal, or if this is an alias for an LLVM intrinsic, + // do not add a "\01" prefix. + if (!ALA->getIsLiteralLabel() || ALA->getLabel().startswith("llvm.")) { + Out << ALA->getLabel(); + return; + } + // Adding the prefix can cause problems when one file has a "foo" and // another has a "\01foo". That is known to happen on ELF with the // tricks normally used for producing aliases (PR9177). Fortunately the // llvm mangler on ELF is a nop, so we can just avoid adding the \01 - // marker. We also avoid adding the marker if this is an alias for an - // LLVM intrinsic. + // marker. char GlobalPrefix = getASTContext().getTargetInfo().getDataLayout().getGlobalPrefix(); - if (GlobalPrefix && !ALA->getLabel().startswith("llvm.")) + if (GlobalPrefix) Out << '\01'; // LLVM IR Marker for __asm("foo") Out << ALA->getLabel(); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 42ce5f183a..7274fc96c5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2766,7 +2766,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (AsmLabelAttr *NewA = New->getAttr()) { if (AsmLabelAttr *OldA = Old->getAttr()) { - if (OldA->getLabel() != NewA->getLabel()) { + if (!OldA->isEquivalent(NewA)) { // This redeclaration changes __asm__ label. Diag(New->getLocation(), diag::err_different_asm_label); Diag(OldA->getLocation(), diag::note_previous_declaration); @@ -6983,8 +6983,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } - NewVD->addAttr(::new (Context) - AsmLabelAttr(Context, SE->getStrTokenLoc(0), Label)); + NewVD->addAttr(::new (Context) AsmLabelAttr( + Context, SE->getStrTokenLoc(0), Label, /*IsLiteralLabel=*/true)); } else if (!ExtnameUndeclaredIdentifiers.empty()) { llvm::DenseMap::iterator I = ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier()); @@ -8882,8 +8882,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (Expr *E = (Expr*) D.getAsmLabel()) { // The parser guarantees this is a string. StringLiteral *SE = cast(E); - NewFD->addAttr(::new (Context) AsmLabelAttr(Context, SE->getStrTokenLoc(0), - SE->getString())); + NewFD->addAttr(::new (Context) + AsmLabelAttr(Context, SE->getStrTokenLoc(0), + SE->getString(), /*IsLiteralLabel=*/true)); } else if (!ExtnameUndeclaredIdentifiers.empty()) { llvm::DenseMap::iterator I = ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier()); @@ -17555,8 +17556,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, LookupOrdinaryName); AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc), AttributeCommonInfo::AS_Pragma); - AsmLabelAttr *Attr = - AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info); + AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit( + Context, AliasName->getName(), /*LiteralLabel=*/true, Info); // If a declaration that: // 1) declares a function or a variable diff --git a/unittests/AST/DeclTest.cpp b/unittests/AST/DeclTest.cpp index 6691952b2f..b3bc064cd1 100644 --- a/unittests/AST/DeclTest.cpp +++ b/unittests/AST/DeclTest.cpp @@ -10,12 +10,16 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/LLVM.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" using namespace clang::ast_matchers; using namespace clang::tooling; +using namespace clang; TEST(Decl, CleansUpAPValues) { MatchFinder Finder; @@ -56,3 +60,48 @@ TEST(Decl, CleansUpAPValues) { "constexpr _Complex __uint128_t c = 0xffffffffffffffff;", Args)); } + +TEST(Decl, AsmLabelAttr) { + // Create two method decls: `f` and `g`. + StringRef Code = R"( + struct S { + void f() {} + void g() {} + }; + )"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); + ASTContext &Ctx = AST->getASTContext(); + assert(Ctx.getTargetInfo().getDataLayout().getGlobalPrefix() && + "Expected target to have a global prefix"); + DiagnosticsEngine &Diags = AST->getDiagnostics(); + SourceManager &SM = AST->getSourceManager(); + FileID MainFileID = SM.getMainFileID(); + + // Find the method decls within the AST. + SmallVector Decls; + AST->findFileRegionDecls(MainFileID, Code.find('{'), 0, Decls); + ASSERT_TRUE(Decls.size() == 1); + CXXRecordDecl *DeclS = cast(Decls[0]); + NamedDecl *DeclF = *DeclS->method_begin(); + NamedDecl *DeclG = *(++DeclS->method_begin()); + + // Attach asm labels to the decls: one literal, and one not. + DeclF->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "foo", + /*LiteralLabel=*/true)); + DeclG->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "goo", + /*LiteralLabel=*/false)); + + // Mangle the decl names. + std::string MangleF, MangleG; + MangleContext *MC = ItaniumMangleContext::create(Ctx, Diags); + { + llvm::raw_string_ostream OS_F(MangleF); + llvm::raw_string_ostream OS_G(MangleG); + MC->mangleName(DeclF, OS_F); + MC->mangleName(DeclG, OS_G); + } + + ASSERT_TRUE(0 == MangleF.compare("\x01" "foo")); + ASSERT_TRUE(0 == MangleG.compare("goo")); +} -- 2.40.0