[llvm] 91ccbc6 - [TableGen] Support named arguments

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 20 01:05:39 PDT 2023


Author: wangpc
Date: 2023-07-20T16:03:17+08:00
New Revision: 91ccbc6c1c4c121935ee4fbfa0db13ad86590a59

URL: https://github.com/llvm/llvm-project/commit/91ccbc6c1c4c121935ee4fbfa0db13ad86590a59
DIFF: https://github.com/llvm/llvm-project/commit/91ccbc6c1c4c121935ee4fbfa0db13ad86590a59.diff

LOG: [TableGen] Support named arguments

We provide a way to specify arguments in the form of `name=value`
so that we don't have to specify all optional arguments before the
one we'd like to change. Required arguments can alse be specified
in this way.

Note that the argument can only be specified once regardless of
the way (named or positional) to specify and positional arguments
should be put before named arguments.

Reviewed By: reames

Differential Revision: https://reviews.llvm.org/D152998

Added: 
    llvm/test/TableGen/named-arguments.td

Modified: 
    llvm/docs/ReleaseNotes.rst
    llvm/docs/TableGen/ProgRef.rst
    llvm/include/llvm/TableGen/Record.h
    llvm/lib/TableGen/Record.cpp
    llvm/lib/TableGen/TGParser.cpp
    llvm/lib/TableGen/TGParser.h
    llvm/test/TableGen/template-args.td

Removed: 
    


################################################################################
diff  --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index 2431ff52d6e2ca..1073b5e2204995 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/docs/ReleaseNotes.rst
@@ -99,6 +99,9 @@ Changes to building LLVM
 Changes to TableGen
 -------------------
 
+* Named arguments are supported. Arguments can be specified in the form of
+  ``name=value``.
+
 Changes to Interprocedural Optimizations
 ----------------------------------------
 

diff  --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 0158b4c1be54e5..aa29d830ec9077 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -475,7 +475,7 @@ sense after reading the remainder of this guide.
        def Foo#i;
 
 .. productionlist::
-   SimpleValue8: `ClassID` "<" `ValueListNE` ">"
+   SimpleValue8: `ClassID` "<" `ArgValueList` ">"
 
 This form creates a new anonymous record definition (as would be created by an
 unnamed ``def`` inheriting from the given class with the given template
@@ -642,12 +642,31 @@ of the fields of the class or record.
    RecordBody: `ParentClassList` `Body`
    ParentClassList: [":" `ParentClassListNE`]
    ParentClassListNE: `ClassRef` ("," `ClassRef`)*
-   ClassRef: (`ClassID` | `MultiClassID`) ["<" [`ValueList`] ">"]
+   ClassRef: (`ClassID` | `MultiClassID`) ["<" [`ArgValueList`] ">"]
+   ArgValueList: `PostionalArgValueList` [","] `NamedArgValueList`
+   PostionalArgValueList: [`Value` {"," `Value`}*]
+   NamedArgValueList: [`NameValue` "=" `Value` {"," `NameValue` "=" `Value`}*]
 
 A :token:`ParentClassList` containing a :token:`MultiClassID` is valid only
 in the class list of a ``defm`` statement. In that case, the ID must be the
 name of a multiclass.
 
+The argument values can be specified in two forms:
+
+* Positional argument (``value``). The value is assigned to the argument in the
+  corresponding position. For ``Foo<a0, a1>``, ``a0`` will be assigned to first
+  argument and ``a1`` will be assigned to second argument.
+* Named argument (``name=value``). The value is assigned to the argument with
+  the specified name. For ``Foo<a=a0, b=a1>``, ``a0`` will be assigned to the
+  argument with name ``a`` and ``a1`` will be assigned to the argument with
+  name ``b``.
+
+Required arguments can alse be specified as named argument.
+
+Note that the argument can only be specified once regardless of the way (named
+or positional) to specify and positional arguments should be put before named
+arguments.
+
 .. productionlist::
    Body: ";" | "{" `BodyItem`* "}"
    BodyItem: (`Type` | "code") `TokIdentifier` ["=" `Value`] ";"

diff  --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index b77336a896fc4d..06e4abb27e5998 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -36,6 +36,7 @@
 #include <optional>
 #include <string>
 #include <utility>
+#include <variant>
 #include <vector>
 
 namespace llvm {
@@ -482,11 +483,21 @@ class UnsetInit : public Init {
 };
 
 // Represent an argument.
+using ArgAuxType = std::variant<unsigned, Init *>;
 class ArgumentInit : public Init, public FoldingSetNode {
+public:
+  enum Kind {
+    Positional,
+    Named,
+  };
+
+private:
   Init *Value;
+  ArgAuxType Aux;
 
 protected:
-  explicit ArgumentInit(Init *Value) : Init(IK_ArgumentInit), Value(Value) {}
+  explicit ArgumentInit(Init *Value, ArgAuxType Aux)
+      : Init(IK_ArgumentInit), Value(Value), Aux(Aux) {}
 
 public:
   ArgumentInit(const ArgumentInit &) = delete;
@@ -496,14 +507,33 @@ class ArgumentInit : public Init, public FoldingSetNode {
 
   RecordKeeper &getRecordKeeper() const { return Value->getRecordKeeper(); }
 
-  static ArgumentInit *get(Init *Value);
+  static ArgumentInit *get(Init *Value, ArgAuxType Aux);
+
+  bool isPositional() const { return Aux.index() == Positional; }
+  bool isNamed() const { return Aux.index() == Named; }
 
   Init *getValue() const { return Value; }
+  unsigned getIndex() const {
+    assert(isPositional() && "Should be positional!");
+    return std::get<Positional>(Aux);
+  }
+  Init *getName() const {
+    assert(isNamed() && "Should be named!");
+    return std::get<Named>(Aux);
+  }
+  ArgumentInit *cloneWithValue(Init *Value) const { return get(Value, Aux); }
 
   void Profile(FoldingSetNodeID &ID) const;
 
   Init *resolveReferences(Resolver &R) const override;
-  std::string getAsString() const override { return Value->getAsString(); }
+  std::string getAsString() const override {
+    if (isPositional())
+      return utostr(getIndex()) + ": " + Value->getAsString();
+    if (isNamed())
+      return getName()->getAsString() + ": " + Value->getAsString();
+    llvm_unreachable("Unsupported argument type!");
+    return "";
+  }
 
   bool isComplete() const override { return false; }
   bool isConcrete() const override { return false; }

diff  --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index c38e824143ad40..20db470855a102 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -367,17 +367,24 @@ Init *UnsetInit::convertInitializerTo(RecTy *Ty) const {
   return const_cast<UnsetInit *>(this);
 }
 
-static void ProfileArgumentInit(FoldingSetNodeID &ID, Init *Value) {
+static void ProfileArgumentInit(FoldingSetNodeID &ID, Init *Value,
+                                ArgAuxType Aux) {
+  auto I = Aux.index();
+  ID.AddInteger(I);
+  if (I == ArgumentInit::Positional)
+    ID.AddInteger(std::get<ArgumentInit::Positional>(Aux));
+  if (I == ArgumentInit::Named)
+    ID.AddPointer(std::get<ArgumentInit::Named>(Aux));
   ID.AddPointer(Value);
 }
 
 void ArgumentInit::Profile(FoldingSetNodeID &ID) const {
-  ProfileArgumentInit(ID, Value);
+  ProfileArgumentInit(ID, Value, Aux);
 }
 
-ArgumentInit *ArgumentInit::get(Init *Value) {
+ArgumentInit *ArgumentInit::get(Init *Value, ArgAuxType Aux) {
   FoldingSetNodeID ID;
-  ProfileArgumentInit(ID, Value);
+  ProfileArgumentInit(ID, Value, Aux);
 
   RecordKeeper &RK = Value->getRecordKeeper();
   detail::RecordKeeperImpl &RKImpl = RK.getImpl();
@@ -385,7 +392,7 @@ ArgumentInit *ArgumentInit::get(Init *Value) {
   if (ArgumentInit *I = RKImpl.TheArgumentInitPool.FindNodeOrInsertPos(ID, IP))
     return I;
 
-  ArgumentInit *I = new (RKImpl.Allocator) ArgumentInit(Value);
+  ArgumentInit *I = new (RKImpl.Allocator) ArgumentInit(Value, Aux);
   RKImpl.TheArgumentInitPool.InsertNode(I, IP);
   return I;
 }
@@ -393,7 +400,7 @@ ArgumentInit *ArgumentInit::get(Init *Value) {
 Init *ArgumentInit::resolveReferences(Resolver &R) const {
   Init *NewValue = Value->resolveReferences(R);
   if (NewValue != Value)
-    return ArgumentInit::get(NewValue);
+    return cloneWithValue(NewValue);
 
   return const_cast<ArgumentInit *>(this);
 }
@@ -2219,13 +2226,16 @@ DefInit *VarDefInit::instantiate() {
     ArrayRef<Init *> TArgs = Class->getTemplateArgs();
     MapResolver R(NewRec);
 
-    for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
-      if (i < args_size())
-        R.set(TArgs[i], getArg(i)->getValue());
-      else
-        R.set(TArgs[i], NewRec->getValue(TArgs[i])->getValue());
+    for (unsigned I = 0, E = TArgs.size(); I != E; ++I) {
+      R.set(TArgs[I], NewRec->getValue(TArgs[I])->getValue());
+      NewRec->removeValue(TArgs[I]);
+    }
 
-      NewRec->removeValue(TArgs[i]);
+    for (auto *Arg : args()) {
+      if (Arg->isPositional())
+        R.set(TArgs[Arg->getIndex()], Arg->getValue());
+      if (Arg->isNamed())
+        R.set(Arg->getName(), Arg->getValue());
     }
 
     NewRec->resolveReferences(R);

diff  --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index c9d623031fa44b..759e15f4c443ba 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -570,20 +570,35 @@ bool TGParser::resolveArguments(Record *Rec, ArrayRef<ArgumentInit *> ArgValues,
   assert(ArgValues.size() <= ArgNames.size() &&
          "Too many template arguments allowed");
 
-  // Loop over the template argument names. If a value was specified,
-  // handle the (name, value) pair. If not and there was no default, complain.
-  for (unsigned I = 0, E = ArgNames.size(); I != E; ++I) {
-    if (I < ArgValues.size())
-      ArgValueHandler(ArgNames[I], ArgValues[I]->getValue());
-    else {
-      Init *Default = Rec->getValue(ArgNames[I])->getValue();
-      if (!Default->isComplete())
-        return Error(Loc, "Value not specified for template argument '" +
-                              ArgNames[I]->getAsUnquotedString() + "' (#" +
-                              Twine(I) + ") of parent class '" +
-                              Rec->getNameInitAsString() + "'");
-      ArgValueHandler(ArgNames[I], Default);
+  // Loop over the template arguments and handle the (name, value) pair.
+  SmallVector<Init *, 2> UnsolvedArgNames(ArgNames);
+  for (auto *Arg : ArgValues) {
+    Init *ArgName = nullptr;
+    Init *ArgValue = Arg->getValue();
+    if (Arg->isPositional())
+      ArgName = ArgNames[Arg->getIndex()];
+    if (Arg->isNamed())
+      ArgName = Arg->getName();
+
+    // We can only specify the template argument once.
+    if (!is_contained(UnsolvedArgNames, ArgName))
+      return Error(Loc, "We can only specify the template argument '" +
+                            ArgName->getAsUnquotedString() + "' once");
+
+    ArgValueHandler(ArgName, ArgValue);
+    llvm::erase_value(UnsolvedArgNames, ArgName);
+  }
+
+  // For unsolved arguments, if there is no default value, complain.
+  for (auto *UnsolvedArgName : UnsolvedArgNames) {
+    Init *Default = Rec->getValue(UnsolvedArgName)->getValue();
+    if (!Default->isComplete()) {
+      return Error(Loc, "value not specified for template argument (" +
+                            UnsolvedArgName->getAsUnquotedString() +
+                            ") of multiclass '" + Rec->getNameInitAsString() +
+                            "'");
     }
+    ArgValueHandler(UnsolvedArgName, Default);
   }
 
   return false;
@@ -719,7 +734,7 @@ MultiClass *TGParser::ParseMultiClassID() {
 /// multiclass. This returns a SubClassRefTy with a null Record* on error.
 ///
 ///  SubClassRef ::= ClassID
-///  SubClassRef ::= ClassID '<' ValueList '>'
+///  SubClassRef ::= ClassID '<' ArgValueList '>'
 ///
 SubClassReference TGParser::
 ParseSubClassReference(Record *CurRec, bool isDefm) {
@@ -740,7 +755,8 @@ ParseSubClassReference(Record *CurRec, bool isDefm) {
     return Result;
   }
 
-  if (ParseTemplateArgValueList(Result.TemplateArgs, CurRec, Result.Rec)) {
+  if (ParseTemplateArgValueList(Result.TemplateArgs, CurRec, Result.Rec,
+                                isDefm)) {
     Result.Rec = nullptr; // Error parsing value list.
     return Result;
   }
@@ -760,7 +776,7 @@ ParseSubClassReference(Record *CurRec, bool isDefm) {
 /// Record* on error.
 ///
 ///  SubMultiClassRef ::= MultiClassID
-///  SubMultiClassRef ::= MultiClassID '<' ValueList '>'
+///  SubMultiClassRef ::= MultiClassID '<' ArgValueList '>'
 ///
 SubMultiClassReference TGParser::
 ParseSubMultiClassReference(MultiClass *CurMC) {
@@ -777,7 +793,7 @@ ParseSubMultiClassReference(MultiClass *CurMC) {
   }
 
   if (ParseTemplateArgValueList(Result.TemplateArgs, &CurMC->Rec,
-                                &Result.MC->Rec)) {
+                                &Result.MC->Rec, true)) {
     Result.MC = nullptr; // Error parsing value list.
     return Result;
   }
@@ -2579,10 +2595,13 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
   case tgtok::Id: {
     SMRange NameLoc = Lex.getLocRange();
     StringInit *Name = StringInit::get(Records, Lex.getCurStrVal());
-    if (Lex.Lex() != tgtok::less)  // consume the Id.
-      return ParseIDValue(CurRec, Name, NameLoc, Mode);    // Value ::= IDValue
+    tgtok::TokKind Next = Lex.Lex();
+    if (Next == tgtok::equal) // Named argument.
+      return Name;
+    if (Next != tgtok::less)                            // consume the Id.
+      return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue
 
-    // Value ::= CLASSID '<' ValueListNE '>' (CLASSID has been consumed)
+    // Value ::= CLASSID '<' ArgValueList '>' (CLASSID has been consumed)
     // This is supposed to synthesize a new anonymous definition, deriving
     // from the class with the template arguments, but no body.
     Record *Class = Records.getClass(Name->getValue());
@@ -3113,34 +3132,72 @@ void TGParser::ParseValueList(SmallVectorImpl<Init *> &Result, Record *CurRec,
 
 // ParseTemplateArgValueList - Parse a template argument list with the syntax
 // shown, filling in the Result vector. The open angle has been consumed.
-// An empty argument list is allowed. Return false if okay, true if an 
+// An empty argument list is allowed. Return false if okay, true if an
 // error was detected.
 //
-//   TemplateArgList ::= '<' [Value {',' Value}*] '>'
+//   ArgValueList ::= '<' PostionalArgValueList [','] NamedArgValueList '>'
+//   PostionalArgValueList ::= [Value {',' Value}*]
+//   NamedArgValueList ::= [NameValue '=' Value {',' NameValue '=' Value}*]
 bool TGParser::ParseTemplateArgValueList(
-    SmallVectorImpl<ArgumentInit *> &Result, Record *CurRec, Record *ArgsRec) {
-
+    SmallVectorImpl<ArgumentInit *> &Result, Record *CurRec, Record *ArgsRec,
+    bool IsDefm) {
   assert(Result.empty() && "Result vector is not empty");
   ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
-  unsigned ArgIndex = 0;
-  RecTy *ItemType;
 
   if (consume(tgtok::greater)) // empty value list
     return false;
 
+  bool HasNamedArg = false;
+  unsigned ArgIndex = 0;
   while (true) {
     if (ArgIndex >= TArgs.size()) {
       TokError("Too many template arguments: " + utostr(ArgIndex + 1));
       return true;
     }
-    const RecordVal *Arg = ArgsRec->getValue(TArgs[ArgIndex]);
-    assert(Arg && "Template argument record not found");
 
-    ItemType = Arg->getType();
-    Init *Value = ParseValue(CurRec, ItemType);
+    SMLoc ValueLoc = Lex.getLoc();
+    // If we are parsing named argument, we don't need to know the argument name
+    // and argument type will be resolved after we know the name.
+    Init *Value = ParseValue(
+        CurRec,
+        HasNamedArg ? nullptr : ArgsRec->getValue(TArgs[ArgIndex])->getType());
     if (!Value)
       return true;
-    Result.push_back(ArgumentInit::get(Value));
+
+    // If we meet '=', then we are parsing named arguments.
+    if (Lex.getCode() == tgtok::equal) {
+      if (!isa<StringInit>(Value))
+        return Error(ValueLoc,
+                     "The name of named argument should be a valid identifier");
+
+      auto *Name = cast<StringInit>(Value);
+      Init *QualifiedName =
+          QualifyName(*ArgsRec, CurMultiClass, Name, IsDefm ? "::" : ":");
+      auto *NamedArg = ArgsRec->getValue(QualifiedName);
+      if (!NamedArg)
+        return Error(ValueLoc,
+                     "Argument " + Name->getAsString() + " doesn't exist");
+
+      Lex.Lex(); // eat the '='.
+      ValueLoc = Lex.getLoc();
+      Value = ParseValue(CurRec, NamedArg->getType());
+      // Named value can't be uninitialized.
+      if (isa<UnsetInit>(Value))
+        return Error(ValueLoc,
+                     "The value of named argument should be initialized, "
+                     "but we got '" +
+                         Value->getAsString() + "'");
+
+      Result.push_back(ArgumentInit::get(Value, QualifiedName));
+      HasNamedArg = true;
+    } else {
+      // Positional arguments should be put before named arguments.
+      if (HasNamedArg)
+        return Error(ValueLoc,
+                     "Positional argument should be put before named argument");
+
+      Result.push_back(ArgumentInit::get(Value, ArgIndex));
+    }
 
     if (consume(tgtok::greater)) // end of argument list?
       return false;
@@ -4248,9 +4305,15 @@ bool TGParser::CheckTemplateArgValues(
   ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
 
   for (unsigned I = 0, E = Values.size(); I < E; ++I) {
-    RecordVal *Arg = ArgsRec->getValue(TArgs[I]);
-    RecTy *ArgType = Arg->getType();
     auto *Value = Values[I];
+    Init *ArgName = nullptr;
+    if (Value->isPositional())
+      ArgName = TArgs[Value->getIndex()];
+    if (Value->isNamed())
+      ArgName = Value->getName();
+
+    RecordVal *Arg = ArgsRec->getValue(ArgName);
+    RecTy *ArgType = Arg->getType();
 
     if (TypedInit *ArgValue = dyn_cast<TypedInit>(Value->getValue())) {
       auto *CastValue = ArgValue->getCastTo(ArgType);
@@ -4258,14 +4321,13 @@ bool TGParser::CheckTemplateArgValues(
         assert((!isa<TypedInit>(CastValue) ||
                 cast<TypedInit>(CastValue)->getType()->typeIsA(ArgType)) &&
                "result of template arg value cast has wrong type");
-        Values[I] = ArgumentInit::get(CastValue);
+        Values[I] = Value->cloneWithValue(CastValue);
       } else {
-        PrintFatalError(Loc,
-                        "Value specified for template argument '" +
-                            Arg->getNameInitAsString() + "' (#" + Twine(I) +
-                            ") is of type " + ArgValue->getType()->getAsString() +
-                            "; expected type " + ArgType->getAsString() + ": " +
-                            ArgValue->getAsString());
+        PrintFatalError(Loc, "Value specified for template argument '" +
+                                 Arg->getNameInitAsString() + "' is of type " +
+                                 ArgValue->getType()->getAsString() +
+                                 "; expected type " + ArgType->getAsString() +
+                                 ": " + ArgValue->getAsString());
       }
     }
   }

diff  --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index 5f27a86be8eff0..d42cdad88a843d 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -289,7 +289,8 @@ class TGParser {
   void ParseValueList(SmallVectorImpl<llvm::Init*> &Result,
                       Record *CurRec, RecTy *ItemType = nullptr);
   bool ParseTemplateArgValueList(SmallVectorImpl<llvm::ArgumentInit *> &Result,
-                                 Record *CurRec, Record *ArgsRec);
+                                 Record *CurRec, Record *ArgsRec,
+                                 bool IsDefm = false);
   void ParseDagArgList(
       SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result,
       Record *CurRec);

diff  --git a/llvm/test/TableGen/named-arguments.td b/llvm/test/TableGen/named-arguments.td
new file mode 100644
index 00000000000000..5c2d6b4cfb7015
--- /dev/null
+++ b/llvm/test/TableGen/named-arguments.td
@@ -0,0 +1,139 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
+// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
+// RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s
+// RUN: not llvm-tblgen -DERROR4 %s 2>&1 | FileCheck --check-prefix=ERROR4 %s
+// RUN: not llvm-tblgen -DERROR5 %s 2>&1 | FileCheck --check-prefix=ERROR5 %s
+// RUN: not llvm-tblgen -DERROR6 %s 2>&1 | FileCheck --check-prefix=ERROR6 %s
+// RUN: not llvm-tblgen -DERROR7 %s 2>&1 | FileCheck --check-prefix=ERROR7 %s
+// RUN: not llvm-tblgen -DERROR8 %s 2>&1 | FileCheck --check-prefix=ERROR8 %s
+
+class TestClass<int a, int b = 2, int c = 3> {
+  int value =  !add(a, b, c);
+}
+// CHECK:      def testClass1 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass2 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass3 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass4 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass5 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass6 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass7 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testClass8 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+def testClass1: TestClass<1>;
+def testClass2: TestClass<1, 2>;
+def testClass3: TestClass<1, 2, 3>;
+def testClass4: TestClass<1, b=2>;
+def testClass5: TestClass<1, c=3>;
+def testClass6: TestClass<1, b=2, c=3>;
+def testClass7: TestClass<1, c=3, b=2>;
+def testClass8: TestClass<a=1, c=3, b=2>;
+
+multiclass TestMultiClass<int a, int b = 2, int c = 3> {
+  def "": TestClass<a, b=b, c=c>;
+}
+
+// CHECK:      def testMultiClass1 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass2 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass3 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass4 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass5 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass6 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass7 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+// CHECK:      def testMultiClass8 {
+// CHECK-NEXT:   int value = 6;
+// CHECK-NEXT: }
+defm testMultiClass1: TestMultiClass<1>;
+defm testMultiClass2: TestMultiClass<1, 2>;
+defm testMultiClass3: TestMultiClass<1, 2, 3>;
+defm testMultiClass4: TestMultiClass<1, b=2>;
+defm testMultiClass5: TestMultiClass<1, c=3>;
+defm testMultiClass6: TestMultiClass<1, b=2, c=3>;
+defm testMultiClass7: TestMultiClass<1, c=3, b=2>;
+defm testMultiClass8: TestMultiClass<a=1, b=2, c=3>;
+
+class TestSubroutine<int a, int b=a>{
+  int value=!add(a, b);
+}
+
+// CHECK:      def testSubroutine {
+// CHECK-NEXT:   int value1 = 2;
+// CHECK-NEXT:   int value2 = 2;
+// CHECK-NEXT:   int value3 = 2;
+// CHECK-NEXT: }
+def testSubroutine {
+  int value1=TestSubroutine<1>.value;
+  int value2=TestSubroutine<1, b=1>.value;
+  int value3=TestSubroutine<b=1, a=1>.value;
+}
+
+#ifdef ERROR1
+// ERROR1: Argument "d" doesn't exist
+def testError1: TestClass<1, d=3>;
+#endif
+
+#ifdef ERROR2
+// ERROR2: The name of named argument should be a valid identifier
+def testError2: TestClass<1, 3=0>;
+#endif
+
+#ifdef ERROR3
+// ERROR3: Positional argument should be put before named argument
+def testError3: TestClass<1, b=1, 2>;
+#endif
+
+#ifdef ERROR4
+// ERROR4: The value of named argument should be initialized, but we got '?'
+def testError4: TestClass<1, b=?>;
+#endif
+
+#ifdef ERROR5
+// ERROR5: We can only specify the template argument 'TestClass:a' once
+def testError5: TestClass<1, a=1>;
+#endif
+
+#ifdef ERROR6
+// ERROR6: We can only specify the template argument 'TestMultiClass::a' once
+defm testError6: TestMultiClass<1, a=1>;
+#endif
+
+#ifdef ERROR7
+// ERROR7: We can only specify the template argument 'TestSubroutine:a' once
+def testError7 {
+  int value=TestSubroutine<1, a=1>.value;
+}
+#endif
+
+#ifdef ERROR8
+// ERROR8: We can only specify the template argument 'TestClass:b' once
+def testError8: TestClass<a=1, b=1, b=1>;
+#endif

diff  --git a/llvm/test/TableGen/template-args.td b/llvm/test/TableGen/template-args.td
index eeba513087017f..172d8289c9fd1e 100644
--- a/llvm/test/TableGen/template-args.td
+++ b/llvm/test/TableGen/template-args.td
@@ -25,7 +25,7 @@ def Rec1 : Class1<"Alice"> {
 }
 
 #ifdef ERROR1
-// ERROR1: Value specified for template argument 'Class1:nm' (#0) is of type int
+// ERROR1: Value specified for template argument 'Class1:nm' is of type int
 
 def Rec2 : Class1<42> {
 }
@@ -52,7 +52,7 @@ def Rec4 : Class2<42> {
 }
 
 #ifdef ERROR2
-// ERROR2: Value specified for template argument 'Class2:cd' (#0) is of type string
+// ERROR2: Value specified for template argument 'Class2:cd' is of type string
 
 def Rec5 : Class2<"oops"> {
   list<int> CodeList = [Code];
@@ -69,7 +69,7 @@ def Rec6 {
 }
 
 #ifdef ERROR3
-// ERROR3: Value specified for template argument 'Class1:nm' (#0) is of type int
+// ERROR3: Value specified for template argument 'Class1:nm' is of type int
 
 def Rec7 {
   string Name = Class1<42>.Name;
@@ -84,7 +84,7 @@ def Rec8 {
 }
 
 #ifdef ERROR4
-// ERROR4: Value specified for template argument 'Class2:cd' (#0) is of type string
+// ERROR4: Value specified for template argument 'Class2:cd' is of type string
 
 def Rec9 {
   list<int> CodeList = [Class2<"huh?">.Code];
@@ -110,7 +110,7 @@ multiclass MC1<string nm> {
 defm RecMC1 : MC1<"Carol">;
 
 #ifdef ERROR5
-// ERROR5: Value specified for template argument 'MC1::nm' (#0) is of type int
+// ERROR5: Value specified for template argument 'MC1::nm' is of type int
 
 defm RecMC2 : MC1<42>;
 #endif
@@ -137,7 +137,7 @@ multiclass MC2<bits<8> cd> {
 defm RecMC3 : MC2<42>;
 
 #ifdef ERROR6
-// ERROR6: Value specified for template argument 'MC2::cd' (#0) is of type string
+// ERROR6: Value specified for template argument 'MC2::cd' is of type string
 
 defm RecMC4 : MC2<"Bob">;
 #endif


        


More information about the llvm-commits mailing list