diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -104,6 +104,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/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -29,6 +29,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); + void 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 : 4; + union { UnionParsedType TypeRep; Decl *DeclRep; @@ -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 GetGNUAsmQual() 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 SetGNUAsmQual(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 @@ -684,13 +684,42 @@ ClobberRefs, Exprs, EndLoc); } +/// ParseGNUAsmQualifierListOpt - Parse a GNU extended asm qualifier list. +/// asm-qualifiers: +/// volatile +/// inline +/// goto +void 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); + return; + } + if (DS.SetGNUAsmQual(AQ, Loc)) + Diag(Loc, diag::err_asm_duplicate_qual) + << DeclSpec::getSpecifierName(AQ); + EndLoc = ConsumeToken(); + } +} + /// 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[opt] '(' asm-argument ')' ';' /// /// [GNU] asm-argument: /// asm-string-literal @@ -713,6 +742,7 @@ } DeclSpec DS(AttrFactory); + ParseGNUAsmQualifierListOpt(DS); SourceLocation Loc = Tok.getLocation(); ParseTypeQualifierListOpt(DS, AR_VendorAttributesParsed); @@ -726,14 +756,9 @@ Diag(Loc, diag::warn_asm_qualifier_ignored) << "_Atomic"; // Remember if this was a volatile asm. - bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; + bool isVolatile = DS.GetGNUAsmQual() & DeclSpec::AQ_volatile; // Remember if this was a goto asm. - bool isGotoAsm = false; - - if (Tok.is(tok::kw_goto)) { - isGotoAsm = true; - ConsumeToken(); - } + bool isGotoAsm = DS.GetGNUAsmQual() & DeclSpec::AQ_goto; if (Tok.isNot(tok::l_paren)) { Diag(Tok, diag::err_expected_lparen_after) << "asm"; 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::SetGNUAsmQual(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/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,47 @@ +// 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 underscores(void) { + __asm__(""); + __asm__ __volatile__(""); + __asm__ __inline__(""); + // Note: goto is not supported with underscore prefix+suffix. + __asm__ goto("" ::::foo); +foo:; +} + +void combinations(void) { + asm volatile inline(""); + asm volatile goto("" ::::foo); + asm inline goto("" ::::foo); + asm volatile inline goto("" ::::foo); +foo:; +} + +void permutations(void) { + asm inline volatile(""); + asm goto volatile(""); + asm goto inline(""); + asm inline goto volatile("" ::::foo); + asm inline volatile goto("" ::::foo); + asm goto inline volatile("" ::::foo); + asm goto volatile inline("" ::::foo); +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:; +}