sstwcw created this revision.
sstwcw added reviewers: HazardyKnusperkeks, MyDeveloperDay, curdeius, owenpan, 
rymiel.
Herald added a project: All.
sstwcw requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

New:

  module mh1
      (input var int in1,
       input var in2, in3,
       output tagged_st out);
  endmodule

Old:

  module mh1
      (input var int in1, input var in2, in3, output tagged_st out);
  endmodule

`getNextNonComment` was modified to return a non-const pointer because
we needed to use it that way in `verilogGroupDecl`.

The comment on line 2620 was a typo.  We corrected it while modifying
the function.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D143825

Files:
  clang/lib/Format/FormatToken.h
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTestVerilog.cpp

Index: clang/unittests/Format/FormatTestVerilog.cpp
===================================================================
--- clang/unittests/Format/FormatTestVerilog.cpp
+++ clang/unittests/Format/FormatTestVerilog.cpp
@@ -275,6 +275,76 @@
                                 "x = x;"));
 }
 
+TEST_F(FormatTestVerilog, Headers) {
+  // Test headers with multiple ports.
+  verifyFormat("module mh1\n"
+               "    (input var int in1,\n"
+               "     input var shortreal in2,\n"
+               "     output tagged_st out);\n"
+               "endmodule");
+  // Ports should be grouped by types.
+  verifyFormat("module test\n"
+               "    (input [7 : 0] a,\n"
+               "     input signed [7 : 0] b, c, d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input [7 : 0] a,\n"
+               "     (* x = x *) input signed [7 : 0] b, c, d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input [7 : 0] a = 0,\n"
+               "     input signed [7 : 0] b = 0, c = 0, d = 0);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    #(parameter x)\n"
+               "    (input [7 : 0] a,\n"
+               "     input signed [7 : 0] b, c, d);\n"
+               "endmodule");
+  // When a line needs to be broken, ports of the same type should be aligned to
+  // the same column.
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b, c, //\n"
+               "                          d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    ((* x = x *) input signed [7 : 0] b, c, //\n"
+               "                                      d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b = 0, c, //\n"
+               "                          d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b, c = 0, //\n"
+               "                          d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b, c, //\n"
+               "                          d = 0);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input wire logic signed [7 : 0][0 : 1] b, c, //\n"
+               "                                            d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b, //\n"
+               "                          c, //\n"
+               "                          d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input [7 : 0] a,\n"
+               "     input signed [7 : 0] b, //\n"
+               "                          c, //\n"
+               "                          d);\n"
+               "endmodule");
+  verifyFormat("module test\n"
+               "    (input signed [7 : 0] b, //\n"
+               "                          c, //\n"
+               "                          d,\n"
+               "     output signed [7 : 0] h);\n"
+               "endmodule");
+}
+
 TEST_F(FormatTestVerilog, Hierarchy) {
   verifyFormat("module x;\n"
                "endmodule");
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -2590,8 +2590,21 @@
     FormatToken *Start = Current;
     FormatToken *LatestOperator = nullptr;
     unsigned OperatorIndex = 0;
+    // The first name of the current type in a port list.
+    FormatToken *VerilogFirstOfType = nullptr;
 
     while (Current) {
+      // In Verilog ports in a module header that don't have a type take the
+      // type of the previous one.  For example,
+      //   module a(output b,
+      //                   c,
+      //            output d);
+      // In this case there need to be fake parentheses around b and c.
+      if (Style.isVerilog() && Precedence == prec::Comma) {
+        VerilogFirstOfType =
+            verilogGroupDecl(VerilogFirstOfType, LatestOperator);
+      }
+
       // Consume operators with higher precedence.
       parse(Precedence + 1);
 
@@ -2604,7 +2617,7 @@
         Start = Current;
       }
 
-      // At the end of the line or when an operator with higher precedence is
+      // At the end of the line or when an operator with lower precedence is
       // found, insert fake parenthesis and return.
       if (!Current ||
           (Current->closesScope() &&
@@ -2641,6 +2654,12 @@
       }
     }
 
+    // Group variables of the same type.
+    if (Style.isVerilog() && Precedence == prec::Comma &&
+        VerilogFirstOfType != nullptr) {
+      addFakeParenthesis(VerilogFirstOfType, prec::Comma);
+    }
+
     if (LatestOperator && (Current || Precedence > 0)) {
       // The requires clauses do not neccessarily end in a semicolon or a brace,
       // but just go over to struct/class or a function declaration, we need to
@@ -2776,6 +2795,144 @@
     }
   }
 
+  // Add fake parenthesis around declarations of the same type for example in a
+  // module prototype. Return the first port / variable of the current type.
+  FormatToken *verilogGroupDecl(FormatToken *FirstOfType,
+                                FormatToken *PreviousComma) {
+    if (Current == nullptr)
+      return nullptr;
+
+    FormatToken *Start = Current;
+
+    // Skip attributes.
+    while (Start->startsSequence(tok::l_paren, tok::star)) {
+      if ((Start = Start->MatchingParen) == nullptr ||
+          (Start = Start->getNextNonComment()) == nullptr) {
+        return nullptr;
+      }
+    }
+
+    FormatToken *Tok = Start;
+
+    if (Tok->is(Keywords.kw_assign))
+      Tok = Tok->getNextNonComment();
+
+    // Skip any type qualifiers to find the first identifier. It may be either a
+    // new type name or a variable name. There can be several type qualifiers
+    // preceding a variable name, and we can not tell them apart by looking at
+    // the word alone since a macro can be defined as either a type qualifier or
+    // a variable name. Thus we use the last word before the dimensions instead
+    // of the first word as the candidate for the variable or type name.
+    FormatToken *First = nullptr;
+    while (Tok != nullptr) {
+      FormatToken *Next = Tok->getNextNonComment();
+
+      if (Tok->is(tok::hash)) {
+        // Start of a macro expansion.
+        First = Tok;
+        Tok = Next;
+        if (Tok != nullptr)
+          Tok = Tok->getNextNonComment();
+      } else if (Tok->is(tok::hashhash)) {
+        // Concatenation. Skip.
+        Tok = Next;
+        if (Tok != nullptr)
+          Tok = Tok->getNextNonComment();
+      } else if ((Keywords.isVerilogQualifier(*Tok) ||
+                  Keywords.isVerilogIdentifier(*Tok))) {
+        First = Tok;
+        Tok = Next;
+        // The name may have dots like `interface_foo.modport_foo`.
+        while (Tok != nullptr && Tok->isOneOf(tok::period, tok::coloncolon) &&
+               (Tok = Tok->getNextNonComment())) {
+          if (Keywords.isVerilogIdentifier(*Tok))
+            Tok = Tok->getNextNonComment();
+        }
+      } else if (Next == nullptr) {
+        Tok = nullptr;
+      } else if (Tok->is(tok::l_paren)) {
+        // Make sure the parenthesized list is a drive strength. Otherwise the
+        // statement may be a module instantiation in which case we have already
+        // found the instance name.
+        if (Next->isOneOf(
+                Keywords.kw_highz0, Keywords.kw_highz1, Keywords.kw_large,
+                Keywords.kw_medium, Keywords.kw_pull0, Keywords.kw_pull1,
+                Keywords.kw_small, Keywords.kw_strong0, Keywords.kw_strong1,
+                Keywords.kw_supply0, Keywords.kw_supply1, Keywords.kw_weak0,
+                Keywords.kw_weak1)) {
+          Tok->setType(TT_VerilogStrength);
+          Tok = Tok->MatchingParen;
+          if (Tok != nullptr) {
+            Tok->setType(TT_VerilogStrength);
+            Tok = Tok->getNextNonComment();
+          }
+        } else {
+          break;
+        }
+      } else if (Tok->is(tok::hash)) {
+        if (Next->is(tok::l_paren))
+          Next = Next->MatchingParen;
+        if (Next != nullptr)
+          Tok = Next->getNextNonComment();
+      } else {
+        break;
+      }
+    }
+
+    // Find the second identifier. If it exists it will be the name.
+    FormatToken *Second = nullptr;
+    // Dimensions.
+    while (Tok != nullptr && Tok->is(tok::l_square) &&
+           (Tok = Tok->MatchingParen) != nullptr) {
+      Tok = Tok->getNextNonComment();
+    }
+    if (Tok != nullptr &&
+        (Tok->is(tok::hash) || Keywords.isVerilogIdentifier(*Tok))) {
+      Second = Tok;
+    }
+
+    // If the second identifier doesn't exist and there are qualifiers, the type
+    // is implied.
+    FormatToken *TypedName = nullptr;
+    if (Second != nullptr) {
+      TypedName = Second;
+      if (First != nullptr && First->is(TT_Unknown))
+        First->setType(TT_VerilogDimensionedTypeName);
+    } else if (First != Start) {
+      // If 'First' is null, then this isn't a declaration, 'TypedName' gets set
+      // to null as intended.
+      TypedName = First;
+    }
+
+    if (TypedName != nullptr) {
+      // This is a declaration with a new type.
+      if (TypedName->is(TT_Unknown))
+        TypedName->setType(TT_StartOfName);
+      // Group variables of the previous type.
+      if (FirstOfType != nullptr && PreviousComma != nullptr) {
+        PreviousComma->setType(TT_VerilogTypeComma);
+        addFakeParenthesis(FirstOfType, prec::Comma, PreviousComma->Previous);
+      }
+
+      FirstOfType = TypedName;
+
+      // Don't let higher precedence handle the qualifiers. For example if we
+      // have:
+      //    parameter x = 0
+      // We skip `parameter` here. This way the fake parentheses for the
+      // assignment will be around `x = 0`.
+      while (Current != nullptr && Current != FirstOfType) {
+        if (Current->opensScope()) {
+          next();
+          parse();
+        }
+        next();
+      }
+    }
+
+    return FirstOfType;
+  }
+
   const FormatStyle &Style;
   const AdditionalKeywords &Keywords;
   const AnnotatedLine &Line;
@@ -4203,6 +4360,9 @@
     // Add space in attribute like `(* ASYNC_REG = "TRUE" *)`.
     if (Left.endsSequence(tok::star, tok::l_paren) && Right.is(tok::identifier))
       return true;
+    // Add space before drive strength like in `wire (strong1, pull0)`.
+    if (Right.is(tok::l_paren) && Right.is(TT_VerilogStrength))
+      return true;
   }
   if (Left.is(TT_ImplicitStringLiteral))
     return Right.hasWhitespaceBefore();
@@ -4514,6 +4674,9 @@
       return true;
     }
   } else if (Style.isVerilog()) {
+    // Break between ports of different types.
+    if (Left.is(TT_VerilogTypeComma))
+      return true;
     // Break after labels. In Verilog labels don't have the 'case' keyword, so
     // it is hard to identify them in UnwrappedLineParser.
     if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left))
Index: clang/lib/Format/FormatToken.h
===================================================================
--- clang/lib/Format/FormatToken.h
+++ clang/lib/Format/FormatToken.h
@@ -152,8 +152,12 @@
   TYPE(VerilogDimensionedTypeName)                                             \
   /* for the base in a number literal, not including the quote */              \
   TYPE(VerilogNumberBase)                                                      \
+  /* like `(strong1, pull0)` */                                                \
+  TYPE(VerilogStrength)                                                        \
   /* Things inside the table in user-defined primitives. */                    \
   TYPE(VerilogTableItem)                                                       \
+  /* those that separate ports of different types */                           \
+  TYPE(VerilogTypeComma)                                                       \
   TYPE(Unknown)
 
 /// Determines the semantic type of a syntactic token, e.g. whether "<" is a
@@ -743,8 +747,8 @@
   }
 
   /// Returns the next token ignoring comments.
-  [[nodiscard]] const FormatToken *getNextNonComment() const {
-    const FormatToken *Tok = Next;
+  [[nodiscard]] FormatToken *getNextNonComment() const {
+    FormatToken *Tok = Next;
     while (Tok && Tok->is(tok::comment))
       Tok = Tok->Next;
     return Tok;
@@ -1792,6 +1796,27 @@
                                     kw_input, kw_output, kw_sequence)));
   }
 
+  bool isVerilogQualifier(const FormatToken &Tok) const {
+    switch (Tok.Tok.getKind()) {
+    case tok::kw_extern:
+    case tok::kw_signed:
+    case tok::kw_static:
+    case tok::kw_unsigned:
+    case tok::kw_virtual:
+      return true;
+    case tok::identifier:
+      return Tok.isOneOf(
+          kw_let, kw_var, kw_ref, kw_automatic, kw_bins, kw_coverpoint,
+          kw_ignore_bins, kw_illegal_bins, kw_inout, kw_input, kw_interconnect,
+          kw_local, kw_localparam, kw_output, kw_parameter, kw_pure, kw_rand,
+          kw_randc, kw_scalared, kw_specparam, kw_tri, kw_tri0, kw_tri1,
+          kw_triand, kw_trior, kw_trireg, kw_uwire, kw_vectored, kw_wand,
+          kw_wildcard, kw_wire, kw_wor);
+    default:
+      return false;
+    }
+  }
+
 private:
   /// The JavaScript keywords beyond the C++ keyword set.
   std::unordered_set<IdentifierInfo *> JsExtraKeywords;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to