1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 20:23:11 +01:00

[TableGen] Improve handling of template arguments

This requires changes to TableGen files and some C++ files due to
incompatible multiclass template arguments that slipped through
before the improved handling.
This commit is contained in:
Paul C. Anagnostopoulos 2021-02-25 16:33:08 -05:00
parent 6f5491a95a
commit f8fbe9eb04
7 changed files with 346 additions and 192 deletions

View File

@ -299,7 +299,7 @@ wide range of records conveniently and compactly.
:token:`ClassID` :token:`ClassID`
Specifying a class name in a type context indicates Specifying a class name in a type context indicates
that the type of the defined value must that the type of the defined value must
be a subclass of the specified class. This is useful in conjunction with be a subclass of the specified class. This is useful in conjunction with
the ``list`` type; for example, to constrain the elements of the list to a the ``list`` type; for example, to constrain the elements of the list to a
common base class (e.g., a ``list<Register>`` can only contain definitions common base class (e.g., a ``list<Register>`` can only contain definitions
derived from the ``Register`` class). derived from the ``Register`` class).
@ -554,19 +554,22 @@ classes and records can inherit.
TemplateArgDecl: `Type` `TokIdentifier` ["=" `Value`] TemplateArgDecl: `Type` `TokIdentifier` ["=" `Value`]
A class can be parameterized by a list of "template arguments," whose values A class can be parameterized by a list of "template arguments," whose values
can be used in the class's record body. These template arguments are can be used in the class's record body. These template arguments are
specified each time the class is inherited by another class or record. specified each time the class is inherited by another class or record.
If a template argument is not assigned a default value with ``=``, it is If a template argument is not assigned a default value with ``=``, it is
uninitialized (has the "value" ``?``) and must be specified in the template uninitialized (has the "value" ``?``) and must be specified in the template
argument list when the class is inherited. If an argument is assigned a argument list when the class is inherited (required argument). If an
default value, then it need not be specified in the argument list. The argument is assigned a default value, then it need not be specified in the
template argument default values are evaluated from left to right. argument list (optional argument). In the declaration, all required template
arguments must precede any optional arguments. The template argument default
values are evaluated from left to right.
The :token:`RecordBody` is defined below. It can include a list of The :token:`RecordBody` is defined below. It can include a list of
superclasses from which the current class inherits, along with field definitions superclasses from which the current class inherits, along with field
and other statements. When a class ``C`` inherits from another class ``D``, definitions and other statements. When a class ``C`` inherits from another
the fields of ``D`` are effectively merged into the fields of ``C``. class ``D``, the fields of ``D`` are effectively merged into the fields of
``C``.
A given class can only be defined once. A ``class`` statement is A given class can only be defined once. A ``class`` statement is
considered to define the class if *any* of the following are true (the considered to define the class if *any* of the following are true (the
@ -605,7 +608,7 @@ of the fields of the class or record.
RecordBody: `ParentClassList` `Body` RecordBody: `ParentClassList` `Body`
ParentClassList: [":" `ParentClassListNE`] ParentClassList: [":" `ParentClassListNE`]
ParentClassListNE: `ClassRef` ("," `ClassRef`)* ParentClassListNE: `ClassRef` ("," `ClassRef`)*
ClassRef: (`ClassID` | `MultiClassID`) ["<" `ValueList` ">"] ClassRef: (`ClassID` | `MultiClassID`) ["<" [`ValueList`] ">"]
A :token:`ParentClassList` containing a :token:`MultiClassID` is valid only 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 in the class list of a ``defm`` statement. In that case, the ID must be the

View File

@ -2024,6 +2024,12 @@ public:
void set(Init *Key, Init *Value) { Map[Key] = {Value, false}; } void set(Init *Key, Init *Value) { Map[Key] = {Value, false}; }
bool isComplete(Init *VarName) const {
auto It = Map.find(VarName);
assert(It != Map.end() && "key must be present in map");
return It->second.V->isComplete();
}
Init *resolve(Init *VarName) override; Init *resolve(Init *VarName) override;
}; };

View File

@ -2344,13 +2344,13 @@ void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
if (TypedInit *VRT = dyn_cast<TypedInit>(VR)) if (TypedInit *VRT = dyn_cast<TypedInit>(VR))
Type = Type =
(Twine("of type '") + VRT->getType()->getAsString() + "' ").str(); (Twine("of type '") + VRT->getType()->getAsString() + "' ").str();
PrintFatalError(getLoc(), Twine("Invalid value ") + Type + PrintFatalError(
"is found when setting '" + getLoc(),
Value.getNameInitAsString() + Twine("Invalid value ") + Type + "found when setting field '" +
"' of type '" + Value.getNameInitAsString() + "' of type '" +
Value.getType()->getAsString() + Value.getType()->getAsString() +
"' after resolving references: " + "' after resolving references: " + VR->getAsUnquotedString() +
VR->getAsUnquotedString() + "\n"); "\n");
} }
} }
} }

View File

@ -229,38 +229,33 @@ bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName,
/// args as SubClass's template arguments. /// args as SubClass's template arguments.
bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) { bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) {
Record *SC = SubClass.Rec; Record *SC = SubClass.Rec;
// Add all of the values in the subclass into the current class.
for (const RecordVal &Val : SC->getValues())
if (AddValue(CurRec, SubClass.RefRange.Start, Val))
return true;
ArrayRef<Init *> TArgs = SC->getTemplateArgs();
// Ensure that an appropriate number of template arguments are specified.
if (TArgs.size() < SubClass.TemplateArgs.size())
return Error(SubClass.RefRange.Start,
"More template args specified than expected");
// Loop over all of the template arguments, setting them to the specified
// value or leaving them as the default if necessary.
MapResolver R(CurRec); MapResolver R(CurRec);
for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { // Loop over all the subclass record's fields. Add template arguments
if (i < SubClass.TemplateArgs.size()) { // to the resolver map. Add regular fields to the new record.
// If a value is specified for this template arg, set it now. for (const RecordVal &Field : SC->getValues()) {
if (SetValue(CurRec, SubClass.RefRange.Start, TArgs[i], if (Field.isTemplateArg()) {
None, SubClass.TemplateArgs[i])) R.set(Field.getNameInit(), Field.getValue());
} else {
if (AddValue(CurRec, SubClass.RefRange.Start, Field))
return true; return true;
} else if (!CurRec->getValue(TArgs[i])->getValue()->isComplete()) {
return Error(SubClass.RefRange.Start,
"Value not specified for template argument #" +
Twine(i) + " (" + TArgs[i]->getAsUnquotedString() +
") of subclass '" + SC->getNameInitAsString() + "'!");
} }
}
R.set(TArgs[i], CurRec->getValue(TArgs[i])->getValue()); ArrayRef<Init *> TArgs = SC->getTemplateArgs();
assert(SubClass.TemplateArgs.size() <= TArgs.size() &&
"Too many template arguments allowed");
CurRec->removeValue(TArgs[i]); // Loop over the template argument names. If a value was specified,
// reset the map value. If not and there was no default, complain.
for (unsigned I = 0, E = TArgs.size(); I != E; ++I) {
if (I < SubClass.TemplateArgs.size())
R.set(TArgs[I], SubClass.TemplateArgs[I]);
else if (!R.isComplete(TArgs[I]))
return Error(SubClass.RefRange.Start,
"Value not specified for template argument '" +
TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) +
") of parent class '" + SC->getNameInitAsString() + "'");
} }
Init *Name; Init *Name;
@ -584,8 +579,8 @@ MultiClass *TGParser::ParseMultiClassID() {
return Result; return Result;
} }
/// ParseSubClassReference - Parse a reference to a subclass or to a templated /// ParseSubClassReference - Parse a reference to a subclass or a
/// subclass. This returns a SubClassRefTy with a null Record* on error. /// multiclass. This returns a SubClassRefTy with a null Record* on error.
/// ///
/// SubClassRef ::= ClassID /// SubClassRef ::= ClassID
/// SubClassRef ::= ClassID '<' ValueList '>' /// SubClassRef ::= ClassID '<' ValueList '>'
@ -609,25 +604,18 @@ ParseSubClassReference(Record *CurRec, bool isDefm) {
return Result; return Result;
} }
if (Lex.getCode() == tgtok::greater) { if (ParseTemplateArgValueList(Result.TemplateArgs, CurRec, Result.Rec)) {
TokError("subclass reference requires a non-empty list of template values"); Result.Rec = nullptr; // Error parsing value list.
Result.Rec = nullptr;
return Result; return Result;
} }
ParseValueList(Result.TemplateArgs, CurRec, Result.Rec); if (CheckTemplateArgValues(Result.TemplateArgs, Result.RefRange.Start,
if (Result.TemplateArgs.empty()) { Result.Rec)) {
Result.Rec = nullptr; // Error parsing value list. Result.Rec = nullptr; // Error checking value list.
return Result; return Result;
} }
if (!consume(tgtok::greater)) {
TokError("expected '>' in template value list");
Result.Rec = nullptr;
return Result;
}
Result.RefRange.End = Lex.getLoc(); Result.RefRange.End = Lex.getLoc();
return Result; return Result;
} }
@ -652,23 +640,12 @@ ParseSubMultiClassReference(MultiClass *CurMC) {
return Result; return Result;
} }
if (Lex.getCode() == tgtok::greater) { if (ParseTemplateArgValueList(Result.TemplateArgs, &CurMC->Rec,
TokError("subclass reference requires a non-empty list of template values"); &Result.MC->Rec)) {
Result.MC = nullptr; Result.MC = nullptr; // Error parsing value list.
return Result; return Result;
} }
ParseValueList(Result.TemplateArgs, &CurMC->Rec, &Result.MC->Rec);
if (Result.TemplateArgs.empty()) {
Result.MC = nullptr; // Error parsing value list.
return Result;
}
if (!consume(tgtok::greater)) {
TokError("expected '>' in template value list");
Result.MC = nullptr;
return Result;
}
Result.RefRange.End = Lex.getLoc(); Result.RefRange.End = Lex.getLoc();
return Result; return Result;
@ -2032,15 +2009,9 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
if (Lex.Lex() != tgtok::less) // consume the Id. if (Lex.Lex() != tgtok::less) // consume the Id.
return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue
// Value ::= ID '<' ValueListNE '>' // Value ::= CLASSID '<' ValueListNE '>' (CLASSID has been consumed)
if (Lex.Lex() == tgtok::greater) { // This is supposed to synthesize a new anonymous definition, deriving
TokError("expected non-empty value list"); // from the class with the template arguments, but no body.
return nullptr;
}
// This is a CLASS<initvalslist> expression. This is supposed to synthesize
// a new anonymous definition, deriving from CLASS<initvalslist> with no
// body.
Record *Class = Records.getClass(Name->getValue()); Record *Class = Records.getClass(Name->getValue());
if (!Class) { if (!Class) {
Error(NameLoc, "Expected a class name, got '" + Name->getValue() + "'"); Error(NameLoc, "Expected a class name, got '" + Name->getValue() + "'");
@ -2048,44 +2019,26 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
} }
SmallVector<Init *, 8> Args; SmallVector<Init *, 8> Args;
ParseValueList(Args, CurRec, Class); Lex.Lex(); // consume the <
if (Args.empty()) return nullptr; if (ParseTemplateArgValueList(Args, CurRec, Class))
return nullptr; // Error parsing value list.
if (!consume(tgtok::greater)) { if (CheckTemplateArgValues(Args, NameLoc, Class))
TokError("expected '>' at end of value list"); return nullptr; // Error checking template argument values.
return nullptr;
}
// Typecheck the template arguments list // Loop through the arguments that were not specified and make sure
ArrayRef<Init *> ExpectedArgs = Class->getTemplateArgs(); // they have a complete value.
if (ExpectedArgs.size() < Args.size()) { // TODO: If we just keep a required argument count, we can do away
Error(NameLoc, // with this checking.
"More template args specified than expected"); ArrayRef<Init *> TArgs = Class->getTemplateArgs();
return nullptr; for (unsigned I = Args.size(), E = TArgs.size(); I < E; ++I) {
} RecordVal *Arg = Class->getValue(TArgs[I]);
if (!Arg->getValue()->isComplete())
for (unsigned i = 0, e = ExpectedArgs.size(); i != e; ++i) { Error(NameLoc, "Value not specified for template argument '" +
RecordVal *ExpectedArg = Class->getValue(ExpectedArgs[i]); TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) +
if (i < Args.size()) { ") of parent class '" +
if (TypedInit *TI = dyn_cast<TypedInit>(Args[i])) { Class->getNameInitAsString() + "'");
RecTy *ExpectedType = ExpectedArg->getType();
if (!TI->getType()->typeIsConvertibleTo(ExpectedType)) {
Error(NameLoc,
"Value specified for template argument #" + Twine(i) + " (" +
ExpectedArg->getNameInitAsString() + ") is of type '" +
TI->getType()->getAsString() + "', expected '" +
ExpectedType->getAsString() + "': " + TI->getAsString());
return nullptr;
}
continue;
}
} else if (ExpectedArg->getValue()->isComplete())
continue;
Error(NameLoc,
"Value not specified for template argument #" + Twine(i) + " (" +
ExpectedArgs[i]->getAsUnquotedString() + ")");
return nullptr;
} }
return VarDefInit::get(Class, Args)->Fold(); return VarDefInit::get(Class, Args)->Fold();
@ -2158,7 +2111,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
} }
if (Lex.getCode() != tgtok::r_square) { if (Lex.getCode() != tgtok::r_square) {
ParseValueList(Vals, CurRec, nullptr, ParseValueList(Vals, CurRec,
GivenListTy ? GivenListTy->getElementType() : nullptr); GivenListTy ? GivenListTy->getElementType() : nullptr);
if (Vals.empty()) return nullptr; if (Vals.empty()) return nullptr;
} }
@ -2522,32 +2475,15 @@ void TGParser::ParseDagArgList(
} }
} }
/// ParseValueList - Parse a comma separated list of values, returning them as a /// ParseValueList - Parse a comma separated list of values, returning them
/// vector. Note that this always expects to be able to parse at least one /// in a vector. Note that this always expects to be able to parse at least one
/// value. It returns an empty list if this is not possible. /// value. It returns an empty list if this is not possible.
/// ///
/// ValueList ::= Value (',' Value) /// ValueList ::= Value (',' Value)
/// ///
void TGParser::ParseValueList(SmallVectorImpl<Init*> &Result, Record *CurRec, void TGParser::ParseValueList(SmallVectorImpl<Init *> &Result, Record *CurRec,
Record *ArgsRec, RecTy *EltTy) { RecTy *ItemType) {
RecTy *ItemType = EltTy;
unsigned int ArgN = 0;
if (ArgsRec && !EltTy) {
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
if (TArgs.empty()) {
TokError("template argument provided to non-template class");
Result.clear();
return;
}
const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]);
if (!RV) {
errs() << "Cannot find template arg " << ArgN << " (" << TArgs[ArgN]
<< ")\n";
}
assert(RV && "Template argument record not found??");
ItemType = RV->getType();
++ArgN;
}
Result.push_back(ParseValue(CurRec, ItemType)); Result.push_back(ParseValue(CurRec, ItemType));
if (!Result.back()) { if (!Result.back()) {
Result.clear(); Result.clear();
@ -2558,19 +2494,6 @@ void TGParser::ParseValueList(SmallVectorImpl<Init*> &Result, Record *CurRec,
// ignore trailing comma for lists // ignore trailing comma for lists
if (Lex.getCode() == tgtok::r_square) if (Lex.getCode() == tgtok::r_square)
return; return;
if (ArgsRec && !EltTy) {
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
if (ArgN >= TArgs.size()) {
TokError("too many template arguments");
Result.clear();
return;
}
const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]);
assert(RV && "Template argument record not found??");
ItemType = RV->getType();
++ArgN;
}
Result.push_back(ParseValue(CurRec, ItemType)); Result.push_back(ParseValue(CurRec, ItemType));
if (!Result.back()) { if (!Result.back()) {
Result.clear(); Result.clear();
@ -2579,9 +2502,48 @@ 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
// error was detected.
//
// TemplateArgList ::= '<' [Value {',' Value}*] '>'
bool TGParser::ParseTemplateArgValueList(SmallVectorImpl<Init *> &Result,
Record *CurRec, Record *ArgsRec) {
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;
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);
if (!Value)
return true;
Result.push_back(Value);
if (consume(tgtok::greater)) // end of argument list?
return false;
if (!consume(tgtok::comma)) // must be comma
return true;
++ArgIndex;
}
}
/// ParseDeclaration - Read a declaration, returning the name of field ID, or an /// ParseDeclaration - Read a declaration, returning the name of field ID, or an
/// empty string on error. This can happen in a number of different context's, /// empty string on error. This can happen in a number of different contexts,
/// including within a def or in the template args for a def (which which case /// including within a def or in the template args for a class (in which case
/// CurRec will be non-null) and within the template args for a multiclass (in /// CurRec will be non-null) and within the template args for a multiclass (in
/// which case CurRec will be null, but CurMultiClass will be set). This can /// which case CurRec will be null, but CurMultiClass will be set). This can
/// also happen within a def that is within a multiclass, which will set both /// also happen within a def that is within a multiclass, which will set both
@ -2612,23 +2574,28 @@ Init *TGParser::ParseDeclaration(Record *CurRec,
Init *DeclName = StringInit::get(Str); Init *DeclName = StringInit::get(Str);
Lex.Lex(); Lex.Lex();
if (ParsingTemplateArgs) { bool BadField;
if (CurRec) if (!ParsingTemplateArgs) { // def, possibly in a multiclass
DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":"); BadField = AddValue(CurRec, IdLoc,
else RecordVal(DeclName, IdLoc, Type,
assert(CurMultiClass); HasField ? RecordVal::FK_NonconcreteOK
if (CurMultiClass) : RecordVal::FK_Normal));
DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName,
"::");
}
// Add the field to the record. } else if (CurRec) { // class template argument
if (AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type, DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":");
HasField ? RecordVal::FK_NonconcreteOK BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type,
: RecordVal::FK_Normal))) RecordVal::FK_TemplateArg));
} else { // multiclass template argument
assert(CurMultiClass && "invalid context for template argument");
DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName, "::");
BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type,
RecordVal::FK_TemplateArg));
}
if (BadField)
return nullptr; return nullptr;
// If a value is present, parse it. // If a value is present, parse it and set new field's value.
if (consume(tgtok::equal)) { if (consume(tgtok::equal)) {
SMLoc ValLoc = Lex.getLoc(); SMLoc ValLoc = Lex.getLoc();
Init *Val = ParseValue(CurRec, Type); Init *Val = ParseValue(CurRec, Type);
@ -2715,7 +2682,7 @@ VarInit *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) {
if (!Ranges.empty()) { if (!Ranges.empty()) {
assert(!IterType && "Type already initialized?"); assert(!IterType && "Type already initialized?");
IterType = IntRecTy::get(); IterType = IntRecTy::get();
std::vector<Init*> Values; std::vector<Init *> Values;
for (unsigned R : Ranges) for (unsigned R : Ranges)
Values.push_back(IntInit::get(R)); Values.push_back(IntInit::get(R));
ForeachListValue = ListInit::get(Values, IterType); ForeachListValue = ListInit::get(Values, IterType);
@ -2729,7 +2696,7 @@ VarInit *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) {
/// ParseTemplateArgList - Read a template argument list, which is a non-empty /// ParseTemplateArgList - Read a template argument list, which is a non-empty
/// sequence of template-declarations in <>'s. If CurRec is non-null, these are /// sequence of template-declarations in <>'s. If CurRec is non-null, these are
/// template args for a def, which may or may not be in a multiclass. If null, /// template args for a class, which may or may not be in a multiclass. If null,
/// these are the template args for a multiclass. /// these are the template args for a multiclass.
/// ///
/// TemplateArgList ::= '<' Declaration (',' Declaration)* '>' /// TemplateArgList ::= '<' Declaration (',' Declaration)* '>'
@ -3493,32 +3460,28 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
while (true) { while (true) {
if (!Ref.Rec) return true; if (!Ref.Rec) return true;
// To instantiate a multiclass, we need to first get the multiclass, then // To instantiate a multiclass, we get the multiclass and then loop
// instantiate each def contained in the multiclass with the SubClassRef // through its template argument names. Substs contains a substitution
// template parameters. // value for each argument, either the value specified or the default.
// Then we can resolve the template arguments.
MultiClass *MC = MultiClasses[std::string(Ref.Rec->getName())].get(); MultiClass *MC = MultiClasses[std::string(Ref.Rec->getName())].get();
assert(MC && "Didn't lookup multiclass correctly?"); assert(MC && "Didn't lookup multiclass correctly?");
ArrayRef<Init*> TemplateVals = Ref.TemplateArgs;
// Verify that the correct number of template arguments were specified. ArrayRef<Init *> TemplateVals = Ref.TemplateArgs;
ArrayRef<Init *> TArgs = MC->Rec.getTemplateArgs(); ArrayRef<Init *> TArgs = MC->Rec.getTemplateArgs();
if (TArgs.size() < TemplateVals.size())
return Error(SubClassLoc,
"more template args specified than multiclass expects");
SubstStack Substs; SubstStack Substs;
for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
if (i < TemplateVals.size()) { if (i < TemplateVals.size()) {
Substs.emplace_back(TArgs[i], TemplateVals[i]); Substs.emplace_back(TArgs[i], TemplateVals[i]);
} else { } else {
Init *Default = MC->Rec.getValue(TArgs[i])->getValue(); Init *Default = MC->Rec.getValue(TArgs[i])->getValue();
if (!Default->isComplete()) { if (!Default->isComplete())
return Error(SubClassLoc, return Error(SubClassLoc,
"value not specified for template argument #" + "value not specified for template argument '" +
Twine(i) + " (" + TArgs[i]->getAsUnquotedString() + TArgs[i]->getAsUnquotedString() + "' (#" +
") of multiclass '" + MC->Rec.getNameInitAsString() + Twine(i) + ") of multiclass '" +
"'"); MC->Rec.getNameInitAsString() + "'");
}
Substs.emplace_back(TArgs[i], Default); Substs.emplace_back(TArgs[i], Default);
} }
} }
@ -3537,7 +3500,7 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
SubClassLoc = Lex.getLoc(); SubClassLoc = Lex.getLoc();
// A defm can inherit from regular classes (non-multiclass) as // A defm can inherit from regular classes (non-multiclasses) as
// long as they come in the end of the inheritance list. // long as they come in the end of the inheritance list.
InheritFromClass = (Records.getClass(Lex.getCurStrVal()) != nullptr); InheritFromClass = (Records.getClass(Lex.getCurStrVal()) != nullptr);
@ -3642,6 +3605,41 @@ bool TGParser::ParseFile() {
return TokError("Unexpected token at top level"); return TokError("Unexpected token at top level");
} }
// Check the types of the template argument values for a class
// inheritance, multiclass invocation, or anonymous class invocation.
// If necessary, replace an argument with a cast to the required type.
// The argument count has already been checked.
bool TGParser::CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
SMLoc Loc, Record *ArgsRec) {
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];
if (TypedInit *ArgValue = dyn_cast<TypedInit>(Value)) {
auto *CastValue = ArgValue->getCastTo(ArgType);
if (CastValue) {
assert((!isa<TypedInit>(CastValue) ||
cast<TypedInit>(CastValue)->getType()->typeIsA(ArgType)) &&
"result of template arg value cast has wrong type");
Values[I] = 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());
}
}
}
return false;
}
// Check an assertion: Obtain the condition value and be sure it is true. // Check an assertion: Obtain the condition value and be sure it is true.
// If not, print a nonfatal error along with the message. // If not, print a nonfatal error along with the message.
void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) { void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {

View File

@ -243,8 +243,10 @@ private: // Parser methods.
IDParseMode Mode = ParseValueMode); IDParseMode Mode = ParseValueMode);
Init *ParseValue(Record *CurRec, RecTy *ItemType = nullptr, Init *ParseValue(Record *CurRec, RecTy *ItemType = nullptr,
IDParseMode Mode = ParseValueMode); IDParseMode Mode = ParseValueMode);
void ParseValueList(SmallVectorImpl<llvm::Init*> &Result, Record *CurRec, void ParseValueList(SmallVectorImpl<llvm::Init*> &Result,
Record *ArgsRec = nullptr, RecTy *EltTy = nullptr); Record *CurRec, RecTy *ItemType = nullptr);
bool ParseTemplateArgValueList(SmallVectorImpl<llvm::Init *> &Result,
Record *CurRec, Record *ArgsRec);
void ParseDagArgList( void ParseDagArgList(
SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result, SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result,
Record *CurRec); Record *CurRec);
@ -264,6 +266,8 @@ private: // Parser methods.
MultiClass *ParseMultiClassID(); MultiClass *ParseMultiClassID();
bool ApplyLetStack(Record *CurRec); bool ApplyLetStack(Record *CurRec);
bool ApplyLetStack(RecordsEntry &Entry); bool ApplyLetStack(RecordsEntry &Entry);
bool CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
SMLoc Loc, Record *ArgsRec);
void CheckAssert(SMLoc Loc, Init *Condition, Init *Message); void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
void CheckRecordAsserts(Record &Rec); void CheckRecordAsserts(Record &Rec);
}; };

View File

@ -1,13 +1,14 @@
// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s // RUN: not llvm-tblgen %s 2>&1 | FileCheck %s
// XFAIL: vg_leak // XFAIL: vg_leak
class A<A x> { class Cl<Cl rec> {
A a = x; Cl Arec = rec;
} }
// At the time A0 is referenced, A has not yet been established as a superclass. // At the time A0 is referenced, A has not yet been established as a superclass.
// This kind of self-reference is discourage, but if you *really* want it, you // This kind of self-reference is discourage, but if you *really* want it, you
// can force it with !cast. // can force it with !cast.
// //
// CHECK: Field 'A:x' of type 'A' is incompatible with value // CHECK: alue specified for template argument 'Cl:rec'
def A0 : A<A0>;
def Rec0 : Cl<Rec0>;

View File

@ -0,0 +1,142 @@
// 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
// This file tests that template arguments are type-checked and cast
// if necessary.
// Class template arguments.
class Class1<string nm> {
string Name = nm;
}
// CHECK: def Rec1
// CHECK: string Name = "Alice"
// CHECK: string NameName = "AliceAlice"
def Rec1 : Class1<"Alice"> {
string NameName = Name # Name;
}
#ifdef ERROR1
// ERROR1: Value specified for template argument 'Class1:nm' (#0) is of type int
def Rec2 : Class1<42> {
}
#endif
class Class2<bits<8> cd> {
int Code = cd;
}
// CHECK: def Rec3
// CHECK: int Code = 42
// CHECK: list<int> CodeList = [42]
def Rec3 : Class2<0b00101010> {
list<int> CodeList = [Code];
}
// CHECK: def Rec4
// CHECK: int Code = 42
// CHECK: list<int> CodeList = [42]
def Rec4 : Class2<42> {
list<int> CodeList = [Code];
}
#ifdef ERROR2
// ERROR2: Value specified for template argument 'Class2:cd' (#0) is of type string
def Rec5 : Class2<"oops"> {
list<int> CodeList = [Code];
}
#endif
// Anonymous class instantiation template arguments.
// CHECK: def Rec6
// CHECK: string Name = "Ted"
def Rec6 {
string Name = Class1<"Ted">.Name;
}
#ifdef ERROR3
// ERROR3: Value specified for template argument 'Class1:nm' (#0) is of type int
def Rec7 {
string Name = Class1<42>.Name;
}
#endif
// CHECK: def Rec8
// CHECK: list<int> CodeList = [42]
def Rec8 {
list<int> CodeList = [Class2<42>.Code];
}
#ifdef ERROR4
// ERROR4: Value specified for template argument 'Class2:cd' (#0) is of type string
def Rec9 {
list<int> CodeList = [Class2<"huh?">.Code];
}
#endif
// Multiclass template arguments.
multiclass MC1<string nm> {
def _1 {
string Name = nm;
}
def _2 {
string NameNmae = nm # nm;
}
}
// CHECK: def RecMC1_1
// CHECK: string Name = "Carol"
// CHECK: def RecMC1_2
// CHECK: string NameNmae = "CarolCarol"
defm RecMC1 : MC1<"Carol">;
#ifdef ERROR5
// ERROR5: Value specified for template argument 'MC1::nm' (#0) is of type int
defm RecMC2 : MC1<42>;
#endif
multiclass MC2<bits<8> cd> {
def _1 {
bits<8> Code = cd;
}
def _2 {
int Code = cd;
}
def _3 {
list<int> CodeList = [cd];
}
}
// CHECK: def RecMC3_1
// CHECK: bits<8> Code = { 0, 0, 1, 0, 1, 0, 1, 0 }
// CHECK: def RecMC3_2
// CHECK: int Code = 42
// CHECK: def RecMC3_3
// CHECK: list<int> CodeList = [42]
defm RecMC3 : MC2<42>;
#ifdef ERROR6
// ERROR6: Value specified for template argument 'MC2::cd' (#0) is of type string
defm RecMC4 : MC2<"Bob">;
#endif