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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits