diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -94,6 +94,9 @@ 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 --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -475,7 +475,7 @@ 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 @@ 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`` 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``, ``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 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -318,7 +318,8 @@ IK_VarDefInit, IK_LastTypedInit, IK_UnsetInit, - IK_PositionalArgumentInit + IK_PositionalArgumentInit, + IK_NamedArgumentInit }; private: @@ -493,7 +494,8 @@ ArgumentInit &operator=(const ArgumentInit &) = delete; static bool classof(const Init *I) { - return I->getKind() == IK_PositionalArgumentInit; + return I->getKind() == IK_PositionalArgumentInit || + I->getKind() == IK_NamedArgumentInit; } RecordKeeper &getRecordKeeper() const { return Value->getRecordKeeper(); } @@ -539,6 +541,35 @@ } }; +// Represent a named argument. +class NamedArgumentInit : public ArgumentInit, public FoldingSetNode { + Init *Name; + + NamedArgumentInit(Init *Name, Init *Value) + : ArgumentInit(IK_NamedArgumentInit, Value), Name(Name) {} + +public: + NamedArgumentInit(const NamedArgumentInit &) = delete; + NamedArgumentInit &operator=(const NamedArgumentInit &) = delete; + + static bool classof(const Init *I) { + return I->getKind() == IK_NamedArgumentInit; + } + + static NamedArgumentInit *get(Init *Name, Init *Value); + + ArgumentInit *changeValue(Init *Value) override { return get(Name, Value); } + + Init *getName() const { return Name; } + + void Profile(FoldingSetNodeID &ID) const; + + Init *resolveReferences(Resolver &R) const override; + std::string getAsString() const override { + return Name->getAsString() + ": " + getValue()->getAsString(); + } +}; + /// 'true'/'false' - Represent a concrete initializer for a bit. class BitInit final : public TypedInit { friend detail::RecordKeeperImpl; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -71,6 +71,7 @@ BitInit FalseBitInit; FoldingSet ThePositionalArgumentInitPool; + FoldingSet TheNamedArgumentInitPool; FoldingSet TheBitsInitPool; std::map TheIntInitPool; StringMap StringInitStringPool; @@ -404,6 +405,41 @@ return const_cast(this); } +static void ProfileNamedArgumentInit(FoldingSetNodeID &ID, Init *Name, + Init *Value) { + ID.AddPointer(Name); + ID.AddPointer(Value); +} + +void NamedArgumentInit::Profile(FoldingSetNodeID &ID) const { + ProfileNamedArgumentInit(ID, Name, getValue()); +} + +NamedArgumentInit *NamedArgumentInit::get(Init *Name, Init *Value) { + FoldingSetNodeID ID; + ProfileNamedArgumentInit(ID, Name, Value); + + RecordKeeper &RK = Value->getRecordKeeper(); + detail::RecordKeeperImpl &RKImpl = RK.getImpl(); + void *IP = nullptr; + if (NamedArgumentInit *I = + RKImpl.TheNamedArgumentInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + NamedArgumentInit *I = new (RKImpl.Allocator) NamedArgumentInit(Name, Value); + RKImpl.TheNamedArgumentInitPool.InsertNode(I, IP); + return I; +} + +Init *NamedArgumentInit::resolveReferences(Resolver &R) const { + Init *OldValue = getValue(); + Init *NewValue = OldValue->resolveReferences(R); + if (NewValue != OldValue) + return NamedArgumentInit::get(Name, NewValue); + + return const_cast(this); +} + BitInit *BitInit::get(RecordKeeper &RK, bool V) { return V ? &RK.getImpl().TrueBitInit : &RK.getImpl().FalseBitInit; } @@ -2233,6 +2269,8 @@ for (auto *Arg : args()) { if (auto *PosArg = dyn_cast(Arg)) R.set(TArgs[PosArg->getIndex()], PosArg->getValue()); + else if (auto *NamedArg = dyn_cast(Arg)) + R.set(NamedArg->getName(), NamedArg->getValue()); else llvm_unreachable("Unsupported subtype of ArgumentInit!"); } diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -277,7 +277,7 @@ Init *ParseDeclaration(Record *CurRec, bool ParsingTemplateArgs); VarInit *ParseForeachDeclaration(Init *&ForeachListValue); - SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm); + SubClassReference ParseSubClassReference(Record *CurRec, bool IsDefm); SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC); Init *ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, @@ -289,7 +289,8 @@ void ParseValueList(SmallVectorImpl &Result, Record *CurRec, RecTy *ItemType = nullptr); bool ParseTemplateArgValueList(SmallVectorImpl &Result, - Record *CurRec, Record *ArgsRec); + Record *CurRec, Record *ArgsRec, + bool IsDefm = false); void ParseDagArgList( SmallVectorImpl> &Result, Record *CurRec); diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -118,11 +118,6 @@ Init *NewName = BinOpInit::getStrConcat(CurRec.getNameInit(), StringInit::get(RK, Scoper)); NewName = BinOpInit::getStrConcat(NewName, Name); - if (CurMultiClass && Scoper != "::") { - Init *Prefix = BinOpInit::getStrConcat(CurMultiClass->Rec.getNameInit(), - StringInit::get(RK, "::")); - NewName = BinOpInit::getStrConcat(Prefix, NewName); - } if (BinOpInit *BinOp = dyn_cast(NewName)) NewName = BinOp->Fold(&CurRec); @@ -583,6 +578,8 @@ Init *ArgValue = Arg->getValue(); if (auto *PosArg = dyn_cast(Arg)) ArgName = ArgNames[PosArg->getIndex()]; + else if (auto *NamedArg = dyn_cast(Arg)) + ArgName = NamedArg->getName(); else llvm_unreachable("Unsupported subtype of ArgumentInit!"); @@ -740,14 +737,14 @@ /// 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) { +ParseSubClassReference(Record *CurRec, bool IsDefm) { SubClassReference Result; Result.RefRange.Start = Lex.getLoc(); - if (isDefm) { + if (IsDefm) { if (MultiClass *MC = ParseMultiClassID()) Result.Rec = &MC->Rec; } else { @@ -761,7 +758,8 @@ 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; } @@ -781,7 +779,7 @@ /// Record* on error. /// /// SubMultiClassRef ::= MultiClassID -/// SubMultiClassRef ::= MultiClassID '<' ValueList '>' +/// SubMultiClassRef ::= MultiClassID '<' ArgValueList '>' /// SubMultiClassReference TGParser:: ParseSubMultiClassReference(MultiClass *CurMC) { @@ -798,7 +796,7 @@ } if (ParseTemplateArgValueList(Result.TemplateArgs, &CurMC->Rec, - &Result.MC->Rec)) { + &Result.MC->Rec, true)) { Result.MC = nullptr; // Error parsing value list. return Result; } @@ -2600,10 +2598,13 @@ 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()); @@ -3137,31 +3138,71 @@ // 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 &Result, Record *CurRec, Record *ArgsRec) { - + SmallVectorImpl &Result, Record *CurRec, Record *ArgsRec, + bool IsDefm) { assert(Result.empty() && "Result vector is not empty"); ArrayRef 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(PositionalArgumentInit::get(ArgIndex, Value)); + + // If we meet '=', then we are parsing named arguments. + if (Lex.getCode() == tgtok::equal) { + if (auto *Name = dyn_cast(Value)) { + Init *QualifiedName = + QualifyName(*ArgsRec, CurMultiClass, Name, IsDefm ? "::" : ":"); + auto *NamedArg = ArgsRec->getValue(QualifiedName); + if (!NamedArg) { + Error(ValueLoc, "Argument " + Name->getAsString() + " doesn't exist"); + return true; + } + Lex.Lex(); // eat the '='. + ValueLoc = Lex.getLoc(); + Value = ParseValue(CurRec, NamedArg->getType()); + // Named value can't be uninitialized. + if (isa(Value)) { + Error(ValueLoc, "The value of named argument should be initialized, " + "but we got '" + + Value->getAsString() + "'"); + return true; + } + Result.push_back(NamedArgumentInit::get(QualifiedName, Value)); + HasNamedArg = true; + } else { + Error(ValueLoc, + "The name of named argument should be a valid identifier"); + return true; + } + } else { + // Positional arguments should be put before named arguments. + if (HasNamedArg) { + Error(ValueLoc, + "Positional argument should be put before named argument"); + return true; + } + Result.push_back(PositionalArgumentInit::get(ArgIndex, Value)); + } if (consume(tgtok::greater)) // end of argument list? return false; @@ -4273,6 +4314,8 @@ ArgumentInit *Value = Values[I]; if (auto *PosArg = dyn_cast(Value)) ArgName = TArgs[PosArg->getIndex()]; + else if (auto *NamedArg = dyn_cast(Value)) + ArgName = NamedArg->getName(); else llvm_unreachable("Unsupported subtype of ArgumentInit!"); diff --git a/llvm/test/TableGen/named-arguments.td b/llvm/test/TableGen/named-arguments.td new file mode 100644 --- /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 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; + +multiclass TestMultiClass { + def "": TestClass; +} + +// 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; + +class TestSubroutine{ + 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.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; +#endif