nickdesaulniers created this revision.
nickdesaulniers added a reviewer: aaron.ballman.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
nickdesaulniers added a comment.

I still plan to add CodeGen support for `asm inline` before the next release, 
which should be as simple as emitting an additional `inlinehint`, but I'd like 
to save that for a follow up patch on top of this.


The parsing of GNU C extended asm statements was a little brittle and
had a few issues:

- It was using Parse::ParseTypeQualifierListOpt to parse the `volatile` 
qualifier.  That parser is really meant for TypeQualifiers; an asm statement 
doesn't really have a type qualifier. This is still maybe nice to have, but not 
necessary. We now can check for the `volatile` token by properly expanding the 
grammer, rather than abusing Parse::ParseTypeQualifierListOpt.
- The parsing of `goto` was position dependent, so `asm goto volatile` wouldn't 
parse. The qualifiers should be position independent to one another. Now they 
are.
- We would warn on duplicate `volatile`, but the parse error for duplicate 
`goto` was a generic parse error and wasn't clear.
- We need to add support for the recent GNU C extension `asm inline`. Adding 
support to the parser with the above issues highlighted the need for this 
refactoring.

Link: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D75563

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Parse/Parser.h
  clang/include/clang/Sema/DeclSpec.h
  clang/lib/Parse/ParseStmtAsm.cpp
  clang/lib/Sema/DeclSpec.cpp
  clang/test/Parser/asm-qualifiers.c

Index: clang/test/Parser/asm-qualifiers.c
===================================================================
--- /dev/null
+++ 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:;
+}
Index: clang/lib/Sema/DeclSpec.cpp
===================================================================
--- clang/lib/Sema/DeclSpec.cpp
+++ 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,16 @@
   llvm_unreachable("Unknown type qualifier!");
 }
 
+bool DeclSpec::SetGNUAsmQual(AQ A, SourceLocation Loc) {
+  if (!(A == AQ_unspecified || A == AQ_volatile || A == AQ_inline ||
+        A == AQ_goto) ||
+      GNUAsmQualifiers & A)
+    return true;
+
+  GNUAsmQualifiers |= A;
+  return false;
+}
+
 bool DeclSpec::setFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec,
                                      unsigned &DiagID) {
   // 'inline inline' is ok.  However, since this is likely not what the user
Index: clang/lib/Parse/ParseStmtAsm.cpp
===================================================================
--- clang/lib/Parse/ParseStmtAsm.cpp
+++ clang/lib/Parse/ParseStmtAsm.cpp
@@ -684,13 +684,43 @@
                                 ClobberRefs, Exprs, EndLoc);
 }
 
+/// ParseGNUAsmQualifierList - 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 (AQ != DeclSpec::AQ_unspecified)
+      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 +743,7 @@
   }
 
   DeclSpec DS(AttrFactory);
+  ParseGNUAsmQualifierListOpt(DS);
   SourceLocation Loc = Tok.getLocation();
   ParseTypeQualifierListOpt(DS, AR_VendorAttributesParsed);
 
@@ -726,14 +757,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";
Index: clang/include/clang/Sema/DeclSpec.h
===================================================================
--- clang/include/clang/Sema/DeclSpec.h
+++ 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,
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -2762,6 +2762,7 @@
       DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
       bool AtomicAllowed = true, bool IdentifierRequired = false,
       Optional<llvm::function_ref<void()>> CodeCompletionHandler = None);
+  void ParseGNUAsmQualifierListOpt(DeclSpec &DS);
   void ParseDirectDeclarator(Declarator &D);
   void ParseDecompositionDeclarator(Declarator &D);
   void ParseParenDeclarator(Declarator &D);
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ 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 {
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to