diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -84,6 +84,11 @@ Modified Compiler Flags ----------------------- +- -Wasm-ignored-qualifier (ex. `asm const ("")`) has been removed and replaced + with an error (this matches a recent change in GCC-9). +- -Wasm-file-asm-volatile (ex. `asm volatile ("")` at global scope) has been + removed and replaced with an error (this matches GCC's behavior). + New Pragmas in Clang -------------------- @@ -104,6 +109,9 @@ - The default C language standard used when `-std=` is not specified has been upgraded from gnu11 to gnu17. +- Clang now supports the GNU C extension `asm inline`; it won't do anything + *yet*, but it will be parsed. + - ... C++ Language Changes in Clang diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1084,9 +1084,8 @@ // Inline ASM warnings. def ASMOperandWidths : DiagGroup<"asm-operand-widths">; -def ASMIgnoredQualifier : DiagGroup<"asm-ignored-qualifier">; def ASM : DiagGroup<"asm", [ - ASMOperandWidths, ASMIgnoredQualifier + ASMOperandWidths ]>; // OpenMP warnings. diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -12,11 +12,10 @@ let Component = "Parse" in { -def warn_asm_qualifier_ignored : Warning< - "ignored %0 qualifier on asm">, CatInlineAsm, InGroup; -def warn_file_asm_volatile : Warning< - "meaningless 'volatile' on asm outside function">, CatInlineAsm, - InGroup; +def err_file_asm_volatile : Error< + "meaningless '%0' on asm outside function">, CatInlineAsm; +def err_asm_qualifier_ignored : Error< + "expected 'volatile', 'inline', 'goto', or '('">, CatInlineAsm; let CategoryName = "Inline Assembly Issue" in { def err_asm_empty : Error<"__asm used with no assembly instructions">; @@ -29,6 +28,7 @@ "GNU-style inline assembly is disabled">; def err_asm_goto_cannot_have_output : Error< "'asm goto' cannot have output constraints">; +def err_asm_duplicate_qual : Error<"duplicate asm qualifier '%0'">; } let CategoryName = "Parse Issue" in { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2762,6 +2762,7 @@ DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed, bool AtomicAllowed = true, bool IdentifierRequired = false, Optional> CodeCompletionHandler = None); + bool ParseGNUAsmQualifierListOpt(DeclSpec &DS); void ParseDirectDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -321,6 +321,14 @@ TQ_atomic = 16 }; + // GNU asm qualifiers + enum AQ { + AQ_unspecified = 0, + AQ_volatile = 1, + AQ_inline = 2, + AQ_goto = 4, + }; + /// ParsedSpecifiers - Flags to query which specifiers were applied. This is /// returned by getParsedSpecifiers. enum ParsedSpecifiers { @@ -366,6 +374,9 @@ // constexpr-specifier unsigned ConstexprSpecifier : 2; + // GNU asm specifier + unsigned GNUAsmQualifiers : 3; // Bitwise OR of AQ. + union { UnionParsedType TypeRep; Decl *DeclRep; @@ -436,10 +447,10 @@ TypeSpecType(TST_unspecified), TypeAltiVecVector(false), TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false), TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), - TypeQualifiers(TQ_unspecified), - FS_inline_specified(false), FS_forceinline_specified(false), - FS_virtual_specified(false), FS_noreturn_specified(false), - Friend_specified(false), ConstexprSpecifier(CSK_unspecified), + TypeQualifiers(TQ_unspecified), FS_inline_specified(false), + FS_forceinline_specified(false), FS_virtual_specified(false), + FS_noreturn_specified(false), Friend_specified(false), + ConstexprSpecifier(CSK_unspecified), GNUAsmQualifiers(AQ_unspecified), FS_explicit_specifier(), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} @@ -543,6 +554,7 @@ static const char *getSpecifierName(DeclSpec::SCS S); static const char *getSpecifierName(DeclSpec::TSCS S); static const char *getSpecifierName(ConstexprSpecKind C); + static const char *getSpecifierName(DeclSpec::AQ A); // type-qualifiers @@ -566,6 +578,9 @@ TQ_pipeLoc = SourceLocation(); } + /// getGNUAsmQualifiers - Return a set of AQs. + unsigned getGNUAsmQualifiers() const { return GNUAsmQualifiers; } + // function-specifier bool isInlineSpecified() const { return FS_inline_specified | FS_forceinline_specified; @@ -718,6 +733,8 @@ bool SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, const LangOptions &Lang); + bool setGNUAsmQualifier(AQ A, SourceLocation Loc); + bool setFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); bool setFunctionSpecForceInline(SourceLocation Loc, const char *&PrevSpec, diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -349,31 +349,10 @@ return false; } -/// isTypeQualifier - Return true if the current token could be the -/// start of a type-qualifier-list. -static bool isTypeQualifier(const Token &Tok) { - switch (Tok.getKind()) { - default: return false; - // type-qualifier - case tok::kw_const: - case tok::kw_volatile: - case tok::kw_restrict: - case tok::kw___private: - case tok::kw___local: - case tok::kw___global: - case tok::kw___constant: - case tok::kw___generic: - case tok::kw___read_only: - case tok::kw___read_write: - case tok::kw___write_only: - return true; - } -} - // Determine if this is a GCC-style asm statement. static bool isGCCAsmStatement(const Token &TokAfterAsm) { - return TokAfterAsm.is(tok::l_paren) || TokAfterAsm.is(tok::kw_goto) || - isTypeQualifier(TokAfterAsm); + return TokAfterAsm.is(tok::l_paren) || TokAfterAsm.is(tok::kw_volatile) || + TokAfterAsm.is(tok::kw_inline) || TokAfterAsm.is(tok::kw_goto); } /// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled, @@ -684,13 +663,51 @@ ClobberRefs, Exprs, EndLoc); } +/// ParseGNUAsmQualifierListOpt - Parse a GNU extended asm qualifier list. +/// asm-qualifier: +/// volatile +/// inline +/// goto +/// +/// asm-qualifier-list: +/// asm-qualifier +/// asm-qualifier-list asm-qualifier +bool Parser::ParseGNUAsmQualifierListOpt(DeclSpec &DS) { + SourceLocation EndLoc; + while (1) { + SourceLocation Loc = Tok.getLocation(); + DeclSpec::AQ AQ = DeclSpec::AQ_unspecified; + + if (Tok.getKind() == tok::kw_volatile) + AQ = DeclSpec::AQ_volatile; + else if (Tok.getKind() == tok::kw_inline) + AQ = DeclSpec::AQ_inline; + else if (Tok.getKind() == tok::kw_goto) + AQ = DeclSpec::AQ_goto; + else { + if (EndLoc.isValid()) + DS.SetRangeEnd(EndLoc); + if (Tok.isNot(tok::l_paren)) { + SkipUntil(tok::r_paren, StopAtSemi); + Diag(Loc, diag::err_asm_qualifier_ignored); + return true; + } + return false; + } + if (DS.setGNUAsmQualifier(AQ, Loc)) + Diag(Loc, diag::err_asm_duplicate_qual) << DeclSpec::getSpecifierName(AQ); + EndLoc = ConsumeToken(); + } + return false; +} + /// ParseAsmStatement - Parse a GNU extended asm statement. /// asm-statement: /// gnu-asm-statement /// ms-asm-statement /// /// [GNU] gnu-asm-statement: -/// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' +/// 'asm' asm-qualifier-list[opt] '(' asm-argument ')' ';' /// /// [GNU] asm-argument: /// asm-string-literal @@ -713,33 +730,9 @@ } DeclSpec DS(AttrFactory); - SourceLocation Loc = Tok.getLocation(); - ParseTypeQualifierListOpt(DS, AR_VendorAttributesParsed); - - // GNU asms accept, but warn, about type-qualifiers other than volatile. - if (DS.getTypeQualifiers() & DeclSpec::TQ_const) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "const"; - if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "restrict"; - // FIXME: Once GCC supports _Atomic, check whether it permits it here. - if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "_Atomic"; - - // Remember if this was a volatile asm. - bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; - // Remember if this was a goto asm. - bool isGotoAsm = false; - - if (Tok.is(tok::kw_goto)) { - isGotoAsm = true; - ConsumeToken(); - } - - if (Tok.isNot(tok::l_paren)) { - Diag(Tok, diag::err_expected_lparen_after) << "asm"; - SkipUntil(tok::r_paren, StopAtSemi); + if(ParseGNUAsmQualifierListOpt(DS)) return StmtError(); - } + BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -750,7 +743,7 @@ if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) { const auto *SL = cast(AsmString.get()); if (!SL->getString().trim().empty()) - Diag(Loc, diag::err_gnu_inline_asm_disabled); + Diag(Tok.getLocation(), diag::err_gnu_inline_asm_disabled); } if (AsmString.isInvalid()) { @@ -764,6 +757,7 @@ ExprVector Exprs; ExprVector Clobbers; + bool isVolatile = DS.getGNUAsmQualifiers() & DeclSpec::AQ_volatile; if (Tok.is(tok::r_paren)) { // We have a simple asm expression like 'asm("foo")'. T.consumeClose(); @@ -829,6 +823,7 @@ } } } + bool isGotoAsm = DS.getGNUAsmQualifiers() & DeclSpec::AQ_goto; if (!isGotoAsm && (Tok.isNot(tok::r_paren) || AteExtraColon)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1528,12 +1528,19 @@ assert(Tok.is(tok::kw_asm) && "Not an asm!"); SourceLocation Loc = ConsumeToken(); - if (Tok.is(tok::kw_volatile)) { - // Remove from the end of 'asm' to the end of 'volatile'. + if (Tok.is(tok::kw_volatile) || Tok.is(tok::kw_goto) || Tok.is(tok::kw_inline)) { + // Remove from the end of 'asm' to the end of the asm qualifier. SourceRange RemovalRange(PP.getLocForEndOfToken(Loc), PP.getLocForEndOfToken(Tok.getLocation())); - Diag(Tok, diag::warn_file_asm_volatile) + DeclSpec::AQ AQ = DeclSpec::AQ_unspecified; + if (Tok.is(tok::kw_volatile)) + AQ = DeclSpec::AQ_volatile; + else if (Tok.is(tok::kw_goto)) + AQ = DeclSpec::AQ_goto; + else if (Tok.is(tok::kw_inline)) + AQ = DeclSpec::AQ_inline; + Diag(Tok, diag::err_file_asm_volatile) << DeclSpec::getSpecifierName(AQ) << FixItHint::CreateRemoval(RemovalRange); ConsumeToken(); } diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -589,6 +589,16 @@ llvm_unreachable("Unknown typespec!"); } +const char *DeclSpec::getSpecifierName(AQ A) { + switch (A) { + case DeclSpec::AQ_unspecified: return "unspecified"; + case DeclSpec::AQ_volatile: return "volatile"; + case DeclSpec::AQ_inline: return "inline"; + case DeclSpec::AQ_goto: return "goto"; + } + llvm_unreachable("Unknown GNUAsmQualifier!"); +} + bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, @@ -938,6 +948,12 @@ llvm_unreachable("Unknown type qualifier!"); } +bool DeclSpec::setGNUAsmQualifier(AQ A, SourceLocation Loc) { + bool IsInvalid = GNUAsmQualifiers & A; + GNUAsmQualifiers |= A; + return IsInvalid; +} + bool DeclSpec::setFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { // 'inline inline' is ok. However, since this is likely not what the user diff --git a/clang/test/CodeGen/inline-asm-mixed-style.c b/clang/test/CodeGen/inline-asm-mixed-style.c --- a/clang/test/CodeGen/inline-asm-mixed-style.c +++ b/clang/test/CodeGen/inline-asm-mixed-style.c @@ -14,11 +14,6 @@ // CHECK: movl %ebx, %eax // CHECK: movl %ecx, %edx - __asm mov eax, ebx - __asm const ("movl %ecx, %edx"); // expected-warning {{ignored const qualifier on asm}} - // CHECK: movl %ebx, %eax - // CHECK: movl %ecx, %edx - __asm volatile goto ("movl %ecx, %edx"); // CHECK: movl %ecx, %edx diff --git a/clang/test/Parser/asm-qualifiers.c b/clang/test/Parser/asm-qualifiers.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/asm-qualifiers.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify %s + +void qualifiers(void) { + asm(""); + asm volatile(""); + asm inline(""); + asm goto("" ::::foo); +foo:; +} + +void unknown_qualifiers(void) { + asm noodle(""); // expected-error {{expected 'volatile', 'inline', 'goto', or '('}} + asm goto noodle("" ::::foo); // expected-error {{expected 'volatile', 'inline', 'goto', or '('}} + asm volatile noodle inline(""); // expected-error {{expected 'volatile', 'inline', 'goto', or '('}} +foo:; +} + +void underscores(void) { + __asm__(""); + __asm__ __volatile__(""); + __asm__ __inline__(""); + // Note: goto is not supported with underscore prefix+suffix. + __asm__ goto("" ::::foo); +foo:; +} + +void permutations(void) { + asm goto inline volatile("" ::::foo); + asm goto inline(""); + asm goto volatile inline("" ::::foo); + asm goto volatile(""); + asm inline goto volatile("" ::::foo); + asm inline goto("" ::::foo); + asm inline volatile goto("" ::::foo); + asm inline volatile(""); + asm volatile goto("" ::::foo); + asm volatile inline goto("" ::::foo); + asm volatile inline(""); +foo:; +} + +void duplicates(void) { + asm volatile volatile(""); // expected-error {{duplicate asm qualifier 'volatile'}} + __asm__ __volatile__ __volatile__(""); // expected-error {{duplicate asm qualifier 'volatile'}} + asm inline inline(""); // expected-error {{duplicate asm qualifier 'inline'}} + __asm__ __inline__ __inline__(""); // expected-error {{duplicate asm qualifier 'inline'}} + asm goto goto("" ::::foo); // expected-error {{duplicate asm qualifier 'goto'}} + __asm__ goto goto("" ::::foo); // expected-error {{duplicate asm qualifier 'goto'}} +foo:; +} + +// globals +asm (""); +// +asm volatile (""); // expected-error {{meaningless 'volatile' on asm outside function}} +asm inline (""); // expected-error {{meaningless 'inline' on asm outside function}} +asm goto (""::::noodle); // expected-error {{meaningless 'goto' on asm outside function}} +// expected-error@-1 {{expected ')'}} +// expected-note@-2 {{to match this '('}} diff --git a/clang/test/Parser/asm.c b/clang/test/Parser/asm.c --- a/clang/test/Parser/asm.c +++ b/clang/test/Parser/asm.c @@ -12,12 +12,6 @@ void f2() { asm("foo" : "=r" (a)); // expected-error {{use of undeclared identifier 'a'}} asm("foo" : : "r" (b)); // expected-error {{use of undeclared identifier 'b'}} - - asm const (""); // expected-warning {{ignored const qualifier on asm}} - asm volatile (""); - asm restrict (""); // expected-warning {{ignored restrict qualifier on asm}} - // FIXME: Once GCC supports _Atomic, check whether it allows this. - asm _Atomic (""); // expected-warning {{ignored _Atomic qualifier on asm}} } void a() __asm__(""); // expected-error {{cannot use an empty string literal in 'asm'}} diff --git a/clang/test/Sema/asm.c b/clang/test/Sema/asm.c --- a/clang/test/Sema/asm.c +++ b/clang/test/Sema/asm.c @@ -91,9 +91,6 @@ return a; } -// -asm volatile (""); // expected-warning {{meaningless 'volatile' on asm outside function}} - // PR3904 void test8(int i) { // A number in an input constraint can't point to a read-write constraint.