From bf45304d48bf15dbf3ea8064caf1966dc4a72bc3 Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Thu, 9 Oct 2025 13:01:44 -0300 Subject: [PATCH] Always register `asm` attribute first in a Decl. Previously, clang's SemaDecl processed `asm` attributes after every other attribute in the Decl, unlike gcc and clang's own parser which requires `asm` attributes to be the first one in the declaration (see #162556). Therefore, move the processing of `asm` attributes to be the first one in the attributes list. Signed-off-by: Giuliano Belinassi --- clang/docs/ReleaseNotes.rst | 11 +++ clang/include/clang/Sema/Sema.h | 7 ++ clang/lib/Sema/SemaDecl.cpp | 136 ++++++++++++++++++-------------- clang/test/Sema/attr-print.c | 3 + 4 files changed, 98 insertions(+), 59 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5e9a71e1e74d6..4d7b2b071b6f8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -128,6 +128,17 @@ AST Dumping Potentially Breaking Changes - Default arguments of template template parameters are pretty-printed now. +- Pretty-printing of ``asm`` attributes are now always the first attribute + on the right side of the declaration. Before we had, e.g.: + + ``__attribute__(("visibility")) asm("string")`` + + Now we have: + + ``asm("string") __attribute__(("visibility"))`` + + Which is accepted by both clang and gcc parsers. + Clang Frontend Potentially Breaking Changes ------------------------------------------- - Members of anonymous unions/structs are now injected as ``IndirectFieldDecl`` diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 265462a86e405..0ae17022b54b3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3961,6 +3961,13 @@ class Sema final : public SemaBase { bool &AddToScope, ArrayRef Bindings = {}); +private: + // Perform a check on an AsmLabel to verify its consistency and emit + // diagnostics in case of an error. + void CheckAsmLabel(Scope *S, Expr *AsmLabelExpr, StorageClass SC, + TypeSourceInfo *TInfo, VarDecl *); + +public: /// Perform semantic checking on a newly-created variable /// declaration. /// diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6eaf7b9435491..ac70158c391c8 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7640,6 +7640,58 @@ static bool isMainVar(DeclarationName Name, VarDecl *VD) { VD->isExternC()); } +void Sema::CheckAsmLabel(Scope *S, Expr *E, StorageClass SC, + TypeSourceInfo *TInfo, VarDecl *NewVD) { + + // Quickly return if the function does not have an `asm` attribute. + if (E == nullptr) + return; + + // The parser guarantees this is a string. + StringLiteral *SE = cast(E); + StringRef Label = SE->getString(); + QualType R = TInfo->getType(); + if (S->getFnParent() != nullptr) { + switch (SC) { + case SC_None: + case SC_Auto: + Diag(E->getExprLoc(), diag::warn_asm_label_on_auto_decl) << Label; + break; + case SC_Register: + // Local Named register + if (!Context.getTargetInfo().isValidGCCRegisterName(Label) && + DeclAttrsMatchCUDAMode(getLangOpts(), getCurFunctionDecl())) + Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label; + break; + case SC_Static: + case SC_Extern: + case SC_PrivateExtern: + break; + } + } else if (SC == SC_Register) { + // Global Named register + if (DeclAttrsMatchCUDAMode(getLangOpts(), NewVD)) { + const auto &TI = Context.getTargetInfo(); + bool HasSizeMismatch; + + if (!TI.isValidGCCRegisterName(Label)) + Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label; + else if (!TI.validateGlobalRegisterVariable(Label, Context.getTypeSize(R), + HasSizeMismatch)) + Diag(E->getExprLoc(), diag::err_asm_invalid_global_var_reg) << Label; + else if (HasSizeMismatch) + Diag(E->getExprLoc(), diag::err_asm_register_size_mismatch) << Label; + } + + if (!R->isIntegralType(Context) && !R->isPointerType()) { + Diag(TInfo->getTypeLoc().getBeginLoc(), + diag::err_asm_unsupported_register_type) + << TInfo->getTypeLoc().getSourceRange(); + NewVD->setInvalidDecl(true); + } + } +} + NamedDecl *Sema::ActOnVariableDeclarator( Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, @@ -8124,6 +8176,26 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } + if (Expr *E = D.getAsmLabel()) { + // The parser guarantees this is a string. + StringLiteral *SE = cast(E); + StringRef Label = SE->getString(); + + // Insert the asm attribute. + NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0))); + } else if (!ExtnameUndeclaredIdentifiers.empty()) { + llvm::DenseMap::iterator I = + ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier()); + if (I != ExtnameUndeclaredIdentifiers.end()) { + if (isDeclExternC(NewVD)) { + NewVD->addAttr(I->second); + ExtnameUndeclaredIdentifiers.erase(I); + } else + Diag(NewVD->getLocation(), diag::warn_redefine_extname_not_applied) + << /*Variable*/ 1 << NewVD; + } + } + // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); @@ -8174,65 +8246,11 @@ NamedDecl *Sema::ActOnVariableDeclarator( if (getLangOpts().ObjCAutoRefCount && ObjC().inferObjCARCLifetime(NewVD)) NewVD->setInvalidDecl(); - // Handle GNU asm-label extension (encoded as an attribute). - if (Expr *E = D.getAsmLabel()) { - // The parser guarantees this is a string. - StringLiteral *SE = cast(E); - StringRef Label = SE->getString(); - if (S->getFnParent() != nullptr) { - switch (SC) { - case SC_None: - case SC_Auto: - Diag(E->getExprLoc(), diag::warn_asm_label_on_auto_decl) << Label; - break; - case SC_Register: - // Local Named register - if (!Context.getTargetInfo().isValidGCCRegisterName(Label) && - DeclAttrsMatchCUDAMode(getLangOpts(), getCurFunctionDecl())) - Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label; - break; - case SC_Static: - case SC_Extern: - case SC_PrivateExtern: - break; - } - } else if (SC == SC_Register) { - // Global Named register - if (DeclAttrsMatchCUDAMode(getLangOpts(), NewVD)) { - const auto &TI = Context.getTargetInfo(); - bool HasSizeMismatch; - - if (!TI.isValidGCCRegisterName(Label)) - Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label; - else if (!TI.validateGlobalRegisterVariable(Label, - Context.getTypeSize(R), - HasSizeMismatch)) - Diag(E->getExprLoc(), diag::err_asm_invalid_global_var_reg) << Label; - else if (HasSizeMismatch) - Diag(E->getExprLoc(), diag::err_asm_register_size_mismatch) << Label; - } - - if (!R->isIntegralType(Context) && !R->isPointerType()) { - Diag(TInfo->getTypeLoc().getBeginLoc(), - diag::err_asm_unsupported_register_type) - << TInfo->getTypeLoc().getSourceRange(); - NewVD->setInvalidDecl(true); - } - } - - NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0))); - } else if (!ExtnameUndeclaredIdentifiers.empty()) { - llvm::DenseMap::iterator I = - ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier()); - if (I != ExtnameUndeclaredIdentifiers.end()) { - if (isDeclExternC(NewVD)) { - NewVD->addAttr(I->second); - ExtnameUndeclaredIdentifiers.erase(I); - } else - Diag(NewVD->getLocation(), diag::warn_redefine_extname_not_applied) - << /*Variable*/1 << NewVD; - } - } + // Check the ASM label here, as we need to know all other attributes of the + // Decl first. Otherwise, we can't know if the asm label refers to the + // host or device in a CUDA context. The device has other registers than + // host and we must know where the function will be placed. + CheckAsmLabel(S, D.getAsmLabel(), SC, TInfo, NewVD); // Find the shadowed declaration before filtering for scope. NamedDecl *ShadowedDecl = D.getCXXScopeSpec().isEmpty() diff --git a/clang/test/Sema/attr-print.c b/clang/test/Sema/attr-print.c index 8492356e5d2e5..211e61a937f63 100644 --- a/clang/test/Sema/attr-print.c +++ b/clang/test/Sema/attr-print.c @@ -35,3 +35,6 @@ int * __sptr * __ptr32 ppsp32; // CHECK: __attribute__((availability(macos, strict, introduced=10.6))); void f6(int) __attribute__((availability(macosx,strict,introduced=10.6))); + +// CHECK: _libc_intl_domainname asm("__gi__libc_intl_domainname") __attribute__((visibility("hidden"))); +extern const char _libc_intl_domainname[]; extern typeof (_libc_intl_domainname) _libc_intl_domainname asm("__gi__libc_intl_domainname") __attribute__((visibility("hidden")));