Changes to TableGen
-------------------
+* Named arguments are supported. Arguments can be specified in the form of
+ ``name=value``.
+
Changes to Interprocedural Optimizations
----------------------------------------
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
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`] ";"
#include <optional>
#include <string>
#include <utility>
+#include <variant>
#include <vector>
namespace llvm {
};
// 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;
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; }
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();
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;
}
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);
}
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);
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;
/// 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) {
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;
}
/// Record* on error.
///
/// SubMultiClassRef ::= MultiClassID
-/// SubMultiClassRef ::= MultiClassID '<' ValueList '>'
+/// SubMultiClassRef ::= MultiClassID '<' ArgValueList '>'
///
SubMultiClassReference TGParser::
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;
}
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());
// 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;
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);
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());
}
}
}
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);
--- /dev/null
+// 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
}
#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> {
}
}
#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];
}
#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;
}
#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];
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
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