[TableGen] Eliminate the 'code' type

Update the documentation.

Rework various backends that relied on the code type.

Differential Revision: https://reviews.llvm.org/D92269
This commit is contained in:
Paul C. Anagnostopoulos 2020-11-24 13:09:02 -05:00
parent 1365718778
commit 415fab6f67
31 changed files with 279 additions and 286 deletions

View File

@ -217,8 +217,6 @@ std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
StringRef Value;
if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue()))
Value = SV->getValue();
else if (auto *CV = dyn_cast_or_null<CodeInit>(V->getValue()))
Value = CV->getValue();
if (!Value.empty())
return Field == Primary ? Value.str() : escapeRST(Value);
}

View File

@ -693,8 +693,8 @@ This class provides six fields.
table that holds the entries. If unspecified, the ``FilterClass`` name is
used.
* ``list<string> Fields``. A list of the names of the fields in the
collected records that contain the data for the table entries. The order of
* ``list<string> Fields``. A list of the names of the fields *in the
collected records* that contain the data for the table entries. The order of
this list determines the order of the values in the C++ initializers. See
below for information about the types of these fields.
@ -706,13 +706,26 @@ This class provides six fields.
* ``bit PrimaryKeyEarlyOut``. See the third example below.
TableGen attempts to deduce the type of each of the table fields. It can
deduce ``bit``, ``bits<n>``, ``string``, ``Intrinsic``, and ``Instruction``.
These can be used in the primary key. TableGen also deduces ``code``, but it
cannot be used in the primary key. Any other field types must be specified
TableGen attempts to deduce the type of each of the table fields so that it
can format the C++ initializers in the emitted table. It can deduce ``bit``,
``bits<n>``, ``string``, ``Intrinsic``, and ``Instruction``. These can be
used in the primary key. Any other field types must be specified
explicitly; this is done as shown in the second example below. Such fields
cannot be used in the primary key.
One special case of the field type has to do with code. Arbitrary code is
represented by a string, but has to be emitted as a C++ initializer without
quotes. If the code field was defined using a code literal (``[{...}]``),
then TableGen will know to emit it without quotes. However, if it was
defined using a string literal or complex string expression, then TableGen
will not know. In this case, you can force TableGen to treat the field as
code by including the following line in the ``GenericTable`` record, where
*xxx* is the code field name.
.. code-block:: text
string TypeOf_xxx = "code";
Here is an example where TableGen can deduce the field types. Note that the
table entry records are anonymous; the names of entry records are
irrelevant.
@ -793,7 +806,7 @@ pointer if no entry is found.
This example includes a field whose type TableGen cannot deduce. The ``Kind``
field uses the enumerated type ``CEnum`` defined above. To inform TableGen
of the type, the class derived from ``GenericTable`` must include a field
of the type, the record derived from ``GenericTable`` must include a string field
named ``TypeOf_``\ *field*, where *field* is the name of the field whose type
is required.
@ -802,7 +815,7 @@ is required.
def CTable : GenericTable {
let FilterClass = "CEntry";
let Fields = ["Name", "Kind", "Encoding"];
GenericEnum TypeOf_Kind = CEnum;
string TypeOf_Kind = "CEnum";
let PrimaryKey = ["Encoding"];
let PrimaryKeyName = "lookupCEntryByEncoding";
}

View File

@ -287,9 +287,9 @@ value. The static function ``get()`` can be used to obtain the singleton
This class, a subclass of ``Init``, acts as the parent class of the classes
that represent specific value types (except for the unset value). These
classes include ``BitInit``, ``BitsInit``, ``CodeInit``, ``DagInit``,
``DefInit``, ``IntInit``, ``ListInit``, and ``StringInit``. (There are
additional derived types used by the TableGen parser.)
classes include ``BitInit``, ``BitsInit``, ``DagInit``, ``DefInit``,
``IntInit``, ``ListInit``, and ``StringInit``. (There are additional derived
types used by the TableGen parser.)
This class includes a data member that specifies the ``RecTy`` type of the
value. It provides a function to get that ``RecTy`` type.
@ -330,18 +330,6 @@ The class provides the following additional functions.
* A function that gets a bit specified by an integer index.
``CodeInit``
~~~~~~~~~~~~
The ``CodeInit`` class is a subclass of ``TypedInit``. Its instances
represent arbitrary-length strings produced from ``code`` literals in the
TableGen files. It includes a data member that contains a ``StringRef`` of
the value.
The class provides the usual ``get()`` and ``getValue()`` functions. The
latter function returns the ``StringRef``.
``DagInit``
~~~~~~~~~~~

View File

@ -167,10 +167,11 @@ TableGen has two kinds of string literals:
.. productionlist::
TokString: '"' (non-'"' characters and escapes) '"'
TokCodeFragment: "[{" (shortest text not containing "}]") "}]"
TokCode: "[{" (shortest text not containing "}]") "}]"
A :token:`TokCodeFragment` is nothing more than a multi-line string literal
delimited by ``[{`` and ``}]``. It can break across lines.
A :token:`TokCode` is nothing more than a multi-line string literal
delimited by ``[{`` and ``}]``. It can break across lines and the
line breaks are retained in the string.
The current implementation accepts the following escape sequences::
@ -254,7 +255,7 @@ high-level types (e.g., ``dag``). This flexibility allows you to describe a
wide range of records conveniently and compactly.
.. productionlist::
Type: "bit" | "int" | "string" | "code" | "dag"
Type: "bit" | "int" | "string" | "dag"
:| "bits" "<" `TokInteger` ">"
:| "list" "<" `Type` ">"
:| `ClassID`
@ -271,11 +272,6 @@ wide range of records conveniently and compactly.
The ``string`` type represents an ordered sequence of characters of arbitrary
length.
``code``
The ``code`` type represents a code fragment. The values are the same as
those for the ``string`` type; the ``code`` type is provided just to indicate
the programmer's intention.
``bits<``\ *n*\ ``>``
The ``bits`` type is a fixed-sized integer of arbitrary length *n* that
is treated as separate bits. These bits can be accessed individually.
@ -348,12 +344,12 @@ Simple values
The :token:`SimpleValue` has a number of forms.
.. productionlist::
SimpleValue: `TokInteger` | `TokString`+ | `TokCodeFragment`
SimpleValue: `TokInteger` | `TokString`+ | `TokCode`
A value can be an integer literal, a string literal, or a code fragment
literal. Multiple adjacent string literals are concatenated as in C/C++; the
simple value is the concatenation of the strings. Code fragments become
strings and then are indistinguishable from them.
A value can be an integer literal, a string literal, or a code literal.
Multiple adjacent string literals are concatenated as in C/C++; the simple
value is the concatenation of the strings. Code literals become strings and
are then indistinguishable from them.
.. productionlist::
SimpleValue2: "true" | "false"
@ -616,14 +612,15 @@ name of a multiclass.
.. productionlist::
Body: ";" | "{" `BodyItem`* "}"
BodyItem: `Type` `TokIdentifier` ["=" `Value`] ";"
BodyItem: (`Type` | "code") `TokIdentifier` ["=" `Value`] ";"
:| "let" `TokIdentifier` ["{" `RangeList` "}"] "=" `Value` ";"
:| "defvar" `TokIdentifier` "=" `Value` ";"
A field definition in the body specifies a field to be included in the class
or record. If no initial value is specified, then the field's value is
uninitialized. The type must be specified; TableGen will not infer it from
the value.
the value. The keyword ``code`` may be used to emphasize that the field
has a string value that is code.
The ``let`` form is used to reset a field to a new value. This can be done
for fields defined directly in the body or fields inherited from

View File

@ -22,6 +22,7 @@ namespace llvm {
void PrintNote(const Twine &Msg);
void PrintNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalNote(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalNote(ArrayRef<SMLoc> ErrorLoc,
const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalNote(const Record *Rec,
@ -37,6 +38,7 @@ void PrintError(const Twine &Msg);
void PrintError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg);
void PrintError(const char *Loc, const Twine &Msg);
void PrintError(const Record *Rec, const Twine &Msg);
void PrintError(const RecordVal *RecVal, const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(ArrayRef<SMLoc> ErrorLoc,

View File

@ -58,7 +58,6 @@ public:
enum RecTyKind {
BitRecTyKind,
BitsRecTyKind,
CodeRecTyKind,
IntRecTyKind,
StringRecTyKind,
ListRecTyKind,
@ -138,24 +137,6 @@ public:
bool typeIsA(const RecTy *RHS) const override;
};
/// 'code' - Represent a code fragment
class CodeRecTy : public RecTy {
static CodeRecTy Shared;
CodeRecTy() : RecTy(CodeRecTyKind) {}
public:
static bool classof(const RecTy *RT) {
return RT->getRecTyKind() == CodeRecTyKind;
}
static CodeRecTy *get() { return &Shared; }
std::string getAsString() const override { return "code"; }
bool typeIsConvertibleTo(const RecTy *RHS) const override;
};
/// 'int' - Represent an integer value of no particular size
class IntRecTy : public RecTy {
static IntRecTy Shared;
@ -306,7 +287,6 @@ protected:
IK_FirstTypedInit,
IK_BitInit,
IK_BitsInit,
IK_CodeInit,
IK_DagInit,
IK_DefInit,
IK_FieldInit,
@ -597,16 +577,18 @@ public:
/// "foo" - Represent an initialization by a string value.
class StringInit : public TypedInit {
//// enum StringFormat {
//// SF_String, // Format as "text"
//// SF_Code, // Format as [{text}]
//// };
public:
enum StringFormat {
SF_String, // Format as "text"
SF_Code, // Format as [{text}]
};
private:
StringRef Value;
//// StringFormat Format;
StringFormat Format;
explicit StringInit(StringRef V)
: TypedInit(IK_StringInit, StringRecTy::get()), Value(V) {}
explicit StringInit(StringRef V, StringFormat Fmt)
: TypedInit(IK_StringInit, StringRecTy::get()), Value(V), Format(Fmt) {}
public:
StringInit(const StringInit &) = delete;
@ -616,48 +598,25 @@ public:
return I->getKind() == IK_StringInit;
}
static StringInit *get(StringRef);
static StringInit *get(StringRef, StringFormat Fmt = SF_String);
static StringFormat determineFormat(StringFormat Fmt1, StringFormat Fmt2) {
return (Fmt1 == SF_Code || Fmt2 == SF_Code) ? SF_Code : SF_String;
}
StringRef getValue() const { return Value; }
StringFormat getFormat() const { return Format; }
bool hasCodeFormat() const { return Format == SF_Code; }
Init *convertInitializerTo(RecTy *Ty) const override;
bool isConcrete() const override { return true; }
std::string getAsString() const override { return "\"" + Value.str() + "\""; }
std::string getAsUnquotedString() const override {
return std::string(Value);
}
Init *getBit(unsigned Bit) const override {
llvm_unreachable("Illegal bit reference off string");
}
};
class CodeInit : public TypedInit {
StringRef Value;
explicit CodeInit(StringRef V)
: TypedInit(IK_CodeInit, static_cast<RecTy *>(CodeRecTy::get())),
Value(V) {}
public:
CodeInit(const StringInit &) = delete;
CodeInit &operator=(const StringInit &) = delete;
static bool classof(const Init *I) {
return I->getKind() == IK_CodeInit;
}
static CodeInit *get(StringRef);
StringRef getValue() const { return Value; }
Init *convertInitializerTo(RecTy *Ty) const override;
bool isConcrete() const override { return true; }
std::string getAsString() const override {
return "[{" + Value.str() + "}]";
if (Format == SF_String)
return "\"" + Value.str() + "\"";
else
return "[{" + Value.str() + "}]";
}
std::string getAsUnquotedString() const override {
@ -1438,6 +1397,9 @@ public:
/// Get the type of the field value as a RecTy.
RecTy *getType() const { return TyAndPrefix.getPointer(); }
/// Get the type of the field for printing purposes.
std::string getPrintType() const;
/// Get the value of the field as an Init.
Init *getValue() const { return Value; }
@ -1675,11 +1637,6 @@ public:
/// not a string and llvm::Optional() if the field does not exist.
llvm::Optional<StringRef> getValueAsOptionalString(StringRef FieldName) const;
/// This method looks up the specified field and returns
/// its value as a string, throwing an exception if the field if the value is
/// not a code block and llvm::Optional() if the field does not exist.
llvm::Optional<StringRef> getValueAsOptionalCode(StringRef FieldName) const;
/// This method looks up the specified field and returns
/// its value as a BitsInit, throwing an exception if the field does not exist
/// or if the value is not the right type.

View File

@ -67,9 +67,13 @@ class GenericTable {
// List of the names of fields of collected records that contain the data for
// table entries, in the order that is used for initialization in C++.
//
// For each field of the table named XXX, TableGen will look for a value
// called TypeOf_XXX and use that as a more detailed description of the
// type of the field if present. This is required for fields whose type
// TableGen needs to know the type of the fields so that it can format
// the initializers correctly. It can infer the type of bit, bits, string,
// Intrinsic, and Instruction values.
//
// For each field of the table named xxx, TableGen will look for a field
// named TypeOf_xxx and use that as a more detailed description of the
// type of the field. This is required for fields whose type
// cannot be deduced automatically, such as enum fields. For example:
//
// def MyEnum : GenericEnum {
@ -85,15 +89,15 @@ class GenericTable {
// def MyTable : GenericTable {
// let FilterClass = "MyTableEntry";
// let Fields = ["V", ...];
// GenericEnum TypeOf_V = MyEnum;
// string TypeOf_V = "MyEnum";
// }
//
// Fields of type bit, bits<N>, string, Intrinsic, and Instruction (or
// derived classes of those) are supported natively.
// If a string field was initialized with a code literal, TableGen will
// emit the code verbatim. However, if a string field was initialized
// in some other way, but should be interpreted as code, then a TypeOf_xxx
// field is necessary, with a value of "code":
//
// Additionally, fields of type `code` can appear, where the value is used
// verbatim as an initializer. However, these fields cannot be used as
// search keys.
// string TypeOf_Predicate = "code";
list<string> Fields;
// (Optional) List of fields that make up the primary key.

View File

@ -52,6 +52,13 @@ void PrintNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg) {
// Functions to print fatal notes.
void PrintFatalNote(const Twine &Msg) {
PrintNote(Msg);
// The following call runs the file cleanup handlers.
sys::RunInterruptHandlers();
std::exit(1);
}
void PrintFatalNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg) {
PrintNote(NoteLoc, Msg);
// The following call runs the file cleanup handlers.
@ -107,6 +114,12 @@ void PrintError(const Record *Rec, const Twine &Msg) {
PrintMessage(Rec->getLoc(), SourceMgr::DK_Error, Msg);
}
// This method takes a RecordVal and uses the source location
// stored in it.
void PrintError(const RecordVal *RecVal, const Twine &Msg) {
PrintMessage(RecVal->getLoc(), SourceMgr::DK_Error, Msg);
}
// Functions to print fatal errors.
void PrintFatalError(const Twine &Msg) {

View File

@ -59,8 +59,6 @@ json::Value JSONEmitter::translateInit(const Init &I) {
return Int->getValue();
} else if (auto *Str = dyn_cast<StringInit>(&I)) {
return Str->getValue();
} else if (auto *Code = dyn_cast<CodeInit>(&I)) {
return Code->getValue();
} else if (auto *List = dyn_cast<ListInit>(&I)) {
json::Array array;
for (auto val : *List)

View File

@ -43,15 +43,11 @@ using namespace llvm;
static BumpPtrAllocator Allocator;
STATISTIC(CodeInitsConstructed,
"The total number of unique CodeInits constructed");
//===----------------------------------------------------------------------===//
// Type implementations
//===----------------------------------------------------------------------===//
BitRecTy BitRecTy::Shared;
CodeRecTy CodeRecTy::Shared;
IntRecTy IntRecTy::Shared;
StringRecTy StringRecTy::Shared;
DagRecTy DagRecTy::Shared;
@ -113,18 +109,13 @@ bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind;
}
bool CodeRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
RecTyKind Kind = RHS->getRecTyKind();
return Kind == CodeRecTyKind || Kind == StringRecTyKind;
}
std::string StringRecTy::getAsString() const {
return "string";
}
bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
RecTyKind Kind = RHS->getRecTyKind();
return Kind == StringRecTyKind || Kind == CodeRecTyKind;
return Kind == StringRecTyKind;
}
std::string ListRecTy::getAsString() const {
@ -514,38 +505,26 @@ IntInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
return BitsInit::get(NewBits);
}
CodeInit *CodeInit::get(StringRef V) {
static StringMap<CodeInit*, BumpPtrAllocator &> ThePool(Allocator);
StringInit *StringInit::get(StringRef V, StringFormat Fmt) {
static StringMap<StringInit*, BumpPtrAllocator &> StringPool(Allocator);
static StringMap<StringInit*, BumpPtrAllocator &> CodePool(Allocator);
auto &Entry = *ThePool.insert(std::make_pair(V, nullptr)).first;
if (!Entry.second)
Entry.second = new(Allocator) CodeInit(Entry.getKey());
return Entry.second;
}
StringInit *StringInit::get(StringRef V) {
static StringMap<StringInit*, BumpPtrAllocator &> ThePool(Allocator);
auto &Entry = *ThePool.insert(std::make_pair(V, nullptr)).first;
if (!Entry.second)
Entry.second = new(Allocator) StringInit(Entry.getKey());
return Entry.second;
if (Fmt == SF_String) {
auto &Entry = *StringPool.insert(std::make_pair(V, nullptr)).first;
if (!Entry.second)
Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt);
return Entry.second;
} else {
auto &Entry = *CodePool.insert(std::make_pair(V, nullptr)).first;
if (!Entry.second)
Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt);
return Entry.second;
}
}
Init *StringInit::convertInitializerTo(RecTy *Ty) const {
if (isa<StringRecTy>(Ty))
return const_cast<StringInit *>(this);
if (isa<CodeRecTy>(Ty))
return CodeInit::get(getValue());
return nullptr;
}
Init *CodeInit::convertInitializerTo(RecTy *Ty) const {
if (isa<CodeRecTy>(Ty))
return const_cast<CodeInit *>(this);
if (isa<StringRecTy>(Ty))
return StringInit::get(getValue());
return nullptr;
}
@ -868,7 +847,9 @@ static StringInit *ConcatStringInits(const StringInit *I0,
const StringInit *I1) {
SmallString<80> Concat(I0->getValue());
Concat.append(I1->getValue());
return StringInit::get(Concat);
return StringInit::get(Concat,
StringInit::determineFormat(I0->getFormat(),
I1->getFormat()));
}
static StringInit *interleaveStringList(const ListInit *List,
@ -876,12 +857,15 @@ static StringInit *interleaveStringList(const ListInit *List,
if (List->size() == 0)
return StringInit::get("");
SmallString<80> Result(dyn_cast<StringInit>(List->getElement(0))->getValue());
StringInit::StringFormat Fmt = StringInit::SF_String;
for (unsigned I = 1, E = List->size(); I < E; ++I) {
Result.append(Delim->getValue());
Result.append(dyn_cast<StringInit>(List->getElement(I))->getValue());
auto *StrInit = dyn_cast<StringInit>(List->getElement(I));
Result.append(StrInit->getValue());
Fmt = StringInit::determineFormat(Fmt, StrInit->getFormat());
}
return StringInit::get(Result);
return StringInit::get(Result, Fmt);
}
static StringInit *interleaveIntList(const ListInit *List,
@ -2139,6 +2123,21 @@ StringRef RecordVal::getName() const {
return cast<StringInit>(getNameInit())->getValue();
}
std::string RecordVal::getPrintType() const {
if (getType() == StringRecTy::get()) {
if (auto *StrInit = dyn_cast<StringInit>(Value)) {
if (StrInit->hasCodeFormat())
return "code";
else
return "string";
} else {
return "string";
}
} else {
return TyAndPrefix.getPointer()->getAsString();
}
}
bool RecordVal::setValue(Init *V) {
if (V) {
Value = V->getCastTo(getType());
@ -2193,7 +2192,7 @@ LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; }
void RecordVal::print(raw_ostream &OS, bool PrintSem) const {
if (getPrefix()) OS << "field ";
OS << *getType() << " " << getNameInitAsString();
OS << getPrintType() << " " << getNameInitAsString();
if (getValue())
OS << " = " << *getValue();
@ -2365,6 +2364,7 @@ StringRef Record::getValueAsString(StringRef FieldName) const {
"' does not have a field named `" + FieldName + "'!\n");
return S.getValue();
}
llvm::Optional<StringRef>
Record::getValueAsOptionalString(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
@ -2375,28 +2375,11 @@ Record::getValueAsOptionalString(StringRef FieldName) const {
if (StringInit *SI = dyn_cast<StringInit>(R->getValue()))
return SI->getValue();
if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue()))
return CI->getValue();
PrintFatalError(getLoc(),
"Record `" + getName() + "', ` field `" + FieldName +
"' exists but does not have a string initializer!");
}
llvm::Optional<StringRef>
Record::getValueAsOptionalCode(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (!R || !R->getValue())
return llvm::Optional<StringRef>();
if (isa<UnsetInit>(R->getValue()))
return llvm::Optional<StringRef>();
if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue()))
return CI->getValue();
PrintFatalError(getLoc(),
"Record `" + getName() + "', field `" + FieldName +
"' exists but does not have a code initializer!");
}
BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
@ -2473,8 +2456,6 @@ Record::getValueAsListOfStrings(StringRef FieldName) const {
for (Init *I : List->getValues()) {
if (StringInit *SI = dyn_cast<StringInit>(I))
Strings.push_back(SI->getValue());
else if (CodeInit *CI = dyn_cast<CodeInit>(I))
Strings.push_back(CI->getValue());
else
PrintFatalError(getLoc(),
Twine("Record `") + getName() + "', field `" + FieldName +

View File

@ -540,7 +540,7 @@ tgtok::TokKind TGLexer::LexBracket() {
}
}
return ReturnError(CodeStart-2, "Unterminated Code Block");
return ReturnError(CodeStart - 2, "Unterminated code block");
}
/// LexExclaim - Lex '!' and '![a-zA-Z]+'.

View File

@ -86,8 +86,8 @@ class TGLexer {
// Information about the current token.
const char *TokStart = nullptr;
tgtok::TokKind CurCode = tgtok::TokKind::Eof;
std::string CurStrVal; // This is valid for ID, STRVAL, VARNAME, CODEFRAGMENT
int64_t CurIntVal = 0; // This is valid for INTVAL.
std::string CurStrVal; // This is valid for Id, StrVal, VarName, CodeFragment
int64_t CurIntVal = 0; // This is valid for IntVal.
/// CurBuffer - This is the current buffer index we're lexing from as managed
/// by the SourceMgr object.

View File

@ -799,8 +799,8 @@ bool TGParser::ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges) {
RecTy *TGParser::ParseType() {
switch (Lex.getCode()) {
default: TokError("Unknown token when expecting a type"); return nullptr;
case tgtok::String: Lex.Lex(); return StringRecTy::get();
case tgtok::Code: Lex.Lex(); return CodeRecTy::get();
case tgtok::String:
case tgtok::Code: Lex.Lex(); return StringRecTy::get();
case tgtok::Bit: Lex.Lex(); return BitRecTy::get();
case tgtok::Int: Lex.Lex(); return IntRecTy::get();
case tgtok::Dag: Lex.Lex(); return DagRecTy::get();
@ -1637,6 +1637,9 @@ RecTy *TGParser::ParseOperatorType() {
return nullptr;
}
if (Lex.getCode() == tgtok::Code)
TokError("the 'code' type is not allowed in bang operators; use 'string'");
Type = ParseType();
if (!Type) {
@ -1920,7 +1923,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
break;
}
case tgtok::CodeFragment:
R = CodeInit::get(Lex.getCurStrVal());
R = StringInit::get(Lex.getCurStrVal(), StringInit::SF_Code);
Lex.Lex();
break;
case tgtok::question:

View File

@ -51,7 +51,7 @@ def MIMGBaseOpcodesTable : GenericTable {
let Fields = ["BaseOpcode", "Store", "Atomic", "AtomicX2", "Sampler",
"Gather4", "NumExtraArgs", "Gradients", "G16", "Coordinates",
"LodOrClampOrMip", "HasD16"];
GenericEnum TypeOf_BaseOpcode = MIMGBaseOpcode;
string TypeOf_BaseOpcode = "MIMGBaseOpcode";
let PrimaryKey = ["BaseOpcode"];
let PrimaryKeyName = "getMIMGBaseOpcodeInfo";
@ -65,7 +65,7 @@ def MIMGDimInfoTable : GenericTable {
let FilterClass = "AMDGPUDimProps";
let CppTypeName = "MIMGDimInfo";
let Fields = ["Dim", "NumCoords", "NumGradients", "DA", "Encoding", "AsmSuffix"];
GenericEnum TypeOf_Dim = MIMGDim;
string TypeOf_Dim = "MIMGDim";
let PrimaryKey = ["Dim"];
let PrimaryKeyName = "getMIMGDimInfo";
@ -95,8 +95,8 @@ def MIMGLZMappingTable : GenericTable {
let FilterClass = "MIMGLZMapping";
let CppTypeName = "MIMGLZMappingInfo";
let Fields = ["L", "LZ"];
GenericEnum TypeOf_L = MIMGBaseOpcode;
GenericEnum TypeOf_LZ = MIMGBaseOpcode;
string TypeOf_L = "MIMGBaseOpcode";
string TypeOf_LZ = "MIMGBaseOpcode";
let PrimaryKey = ["L"];
let PrimaryKeyName = "getMIMGLZMappingInfo";
@ -111,8 +111,8 @@ def MIMGMIPMappingTable : GenericTable {
let FilterClass = "MIMGMIPMapping";
let CppTypeName = "MIMGMIPMappingInfo";
let Fields = ["MIP", "NONMIP"];
GenericEnum TypeOf_MIP = MIMGBaseOpcode;
GenericEnum TypeOf_NONMIP = MIMGBaseOpcode;
string TypeOf_MIP = "MIMGBaseOpcode";
string TypeOf_NONMIP = "MIMGBaseOpcode";
let PrimaryKey = ["MIP"];
let PrimaryKeyName = "getMIMGMIPMappingInfo";
@ -127,8 +127,8 @@ def MIMGG16MappingTable : GenericTable {
let FilterClass = "MIMGG16Mapping";
let CppTypeName = "MIMGG16MappingInfo";
let Fields = ["G", "G16"];
GenericEnum TypeOf_G = MIMGBaseOpcode;
GenericEnum TypeOf_G16 = MIMGBaseOpcode;
string TypeOf_G = "MIMGBaseOpcode";
string TypeOf_G16 = "MIMGBaseOpcode";
let PrimaryKey = ["G"];
let PrimaryKeyName = "getMIMGG16MappingInfo";
@ -168,8 +168,8 @@ def MIMGInfoTable : GenericTable {
let FilterClass = "MIMG";
let CppTypeName = "MIMGInfo";
let Fields = ["Opcode", "BaseOpcode", "MIMGEncoding", "VDataDwords", "VAddrDwords"];
GenericEnum TypeOf_BaseOpcode = MIMGBaseOpcode;
GenericEnum TypeOf_MIMGEncoding = MIMGEncoding;
string TypeOf_BaseOpcode = "MIMGBaseOpcode";
string TypeOf_MIMGEncoding = "MIMGEncoding";
let PrimaryKey = ["BaseOpcode", "MIMGEncoding", "VDataDwords", "VAddrDwords"];
let PrimaryKeyName = "getMIMGOpcodeHelper";
@ -926,8 +926,8 @@ def ImageDimIntrinsicTable : GenericTable {
"DMaskIndex", "VAddrStart", "GradientStart", "CoordStart", "LodIndex", "MipIndex", "VAddrEnd",
"RsrcIndex", "SampIndex", "UnormIndex", "TexFailCtrlIndex", "CachePolicyIndex",
"GradientTyArg", "CoordTyArg"];
GenericEnum TypeOf_BaseOpcode = MIMGBaseOpcode;
GenericEnum TypeOf_Dim = MIMGDim;
string TypeOf_BaseOpcode = "MIMGBaseOpcode";
string TypeOf_Dim = "MIMGDim";
let PrimaryKey = ["Intr"];
let PrimaryKeyName = "getImageDimIntrinsicInfo";

View File

@ -1,22 +1,49 @@
// RUN: llvm-tblgen %s | FileCheck %s
// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
// XFAIL: vg_leak
// CHECK: --- Defs ---
// CHECK: def A1
// CHECK: code CodeCode = [{code here;}]
// CHECK: code StringCode = [{code here;}]
// CHECK: def A0 {
// CHECK: code Code = [{Simple}];
// CHECK: }
// CHECK: def A2
// CHECK: string CodeCode = "string here"
// CHECK: string StringCode = "string here"
// CHECK: def B0 {
// CHECK: code Code = [{With paste 7}];
// CHECK: }
// CHECK: def B1
// CHECK: string CodeCode = "with paste 7"
// CHECK: string StringCode = "with paste 7"
// CHECK: def C1
// CHECK: code CodeCode = [{with concat 42}]
// CHECK: code StringCode = [{with concat 42}]
// CHECK: def D1
// CHECK: code CodeCode = [{with concat 108!}]
// CHECK: code StringCode = [{with concat 108!}]
class A<code c> {
code Code = c;
code CodeCode = c;
string StringCode = c;
}
def A0 : A<"Simple">;
def A1 : A<[{code here;}]>;
def A2 : A<"string here">;
class B<int i> : A<"With paste " # i>;
class B<int i> : A<"with paste " # i>;
class C<int i> : A<!strconcat([{with concat }], !cast<string>(i))>;
class D<int i> : A<!strconcat([{with concat }], !cast<string>(i), "!")>;
def B0 : B<7>;
def B1 : B<7>;
def C1 : C<42>;
def D1 : D<108>;
#ifdef ERROR1
// ERROR1: the 'code' type is not allowed
def Zerror1 {
code Code = !cast<code>("i = 0;");
}
#endif

View File

@ -55,19 +55,26 @@ def ATable : GenericTable {
// CHECK-LABEL: GET_BTable_IMPL
// CHECK: constexpr BTypeName BTable[] = {
// CHECK: { "BAlice", 0xAC, },
// CHECK: { "BBob", 0x14, Bob == 13 },
// CHECK: { "BCharlie", 0x80, Charlie == 42 },
// CHECK: { "BEve", 0x4C, Eve == 108 },
// CHECK: };
// CHECK: const BTypeName *lookupBTableByName(StringRef Name) {
// CHECK: return &BTable[Idx->_index];
// CHECK: }
class BEntry<bits<16> enc> {
class BEntry<bits<16> enc, code test = [{}]> {
string Name = NAME;
bits<16> Encoding = enc;
code Test = test;
}
def BAlice : BEntry<0xac>;
def BBob : BEntry<0x14>;
def BCharlie : BEntry<0x80>;
def BEve : BEntry<0x4c>;
def BBob : BEntry<0x14, [{Bob == 13}]>;
def BCharlie : BEntry<0x80, "Charlie == 42">;
def BEve : BEntry<0x4c, [{Eve == }] # 108>;
def BValues : GenericEnum {
let FilterClass = "BEntry";
@ -78,7 +85,8 @@ def BValues : GenericEnum {
def BTable : GenericTable {
let FilterClass = "BEntry";
string CppTypeName = "BTypeName";
let Fields = ["Name", "Encoding"];
let Fields = ["Name", "Encoding", "Test"];
string TypeOf_Test = "code";
}
def lookupBTableByName : SearchIndex {
@ -126,7 +134,7 @@ def CTable : GenericTable {
let FilterClass = "CEntry";
let Fields = ["Name", "Kind", "Encoding"];
GenericEnum TypeOf_Kind = CEnum;
string TypeOf_Kind = "CEnum";
let PrimaryKey = ["Encoding"];
let PrimaryKeyName = "lookupCEntryByEncoding";

View File

@ -28,9 +28,9 @@ def Rec1 {
}
// CHECK: def Rec2
// CHECK: Test1 = "01234567";
// CHECK: Test2 = "0, 1, 2, 3, 4, 5, 6, 7";
// CHECK: Test3 = "0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 42";
// CHECK: Test1 = "01234567";
// CHECK: Test2 = "0, 1, 2, 3, 4, 5, 6, 7";
// CHECK: Test3 = "0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 42";
def Rec2 {
string Test1 = !interleave(IntList, "");
@ -39,9 +39,9 @@ def Rec2 {
}
// CHECK: def Rec3
// CHECK: Test1 = "271";
// CHECK: Test2 = "2, 7, 1";
// CHECK: Test3 = "2 & 7 & 1 & 0";
// CHECK: Test1 = "271";
// CHECK: Test2 = "2, 7, 1";
// CHECK: Test3 = "2 & 7 & 1 & 0";
def Rec3 {
string Test1 = !interleave(BitsList, "");
@ -50,9 +50,9 @@ def Rec3 {
}
// CHECK: def Rec4
// CHECK: Test1 = "01101";
// CHECK: Test2 = "0, 1, 1, 0, 1";
// CHECK: Test3 = "0 and 1 and 1 and 0 and 1 and 1";
// CHECK: Test1 = "01101";
// CHECK: Test2 = "0, 1, 1, 0, 1";
// CHECK: Test3 = "0 and 1 and 1 and 0 and 1 and 1";
def Rec4 {
string Test1 = !interleave(BitList, "");
@ -61,14 +61,23 @@ def Rec4 {
}
// CHECK: def Rec5
// CHECK: Colors = ["red", "green", "yellow"];
// CHECK: ColorList = "redify, greenify, yellowify";
// CHECK: Colors = ["red", "green", "yellow"];
// CHECK: ColorList = "redify, greenify, yellowify";
def Rec5 {
list<string> Colors = ["red", "green", "yellow"];
string ColorList = !interleave(Ishify<Colors>.ret, ", ");
}
// CHECK: def Rec6
// CHECK: code OperatorList = [{+, -, *, /, ?:, ;}];
def Rec6 {
list<string> Operators = ["+", "-", "*", "/", "?:"];
code OperatorList = !interleave(!listconcat(Operators, [[{;}]]), ", ");
}
#ifdef ERROR1
def op;

View File

@ -1,5 +1,5 @@
// RUN: not llvm-tblgen -I %p %s 2>&1 | FileCheck %s
// CHECK: error: Unterminated Code Block
// CHECK: error: Unterminated code block
include "unterminated-code-block-include.inc" }]>;

View File

@ -1265,13 +1265,10 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
<< " break;\n";
for (unsigned i = 0; i < MCOpPredicates.size(); ++i) {
Init *MCOpPred = MCOpPredicates[i]->getValueInit("MCOperandPredicate");
if (CodeInit *SI = dyn_cast<CodeInit>(MCOpPred)) {
O << " case " << i + 1 << ": {\n"
<< SI->getValue() << "\n"
<< " }\n";
} else
llvm_unreachable("Unexpected MCOperandPredicate field!");
StringRef MCOpPred = MCOpPredicates[i]->getValueAsString("MCOperandPredicate");
O << " case " << i + 1 << ": {\n"
<< MCOpPred.data() << "\n"
<< " }\n";
}
O << " }\n"
<< "}\n\n";

View File

@ -346,8 +346,7 @@ Transition::Transition(Record *R, Automaton *Parent) {
} else if (isa<IntRecTy>(SymbolV->getType())) {
Actions.emplace_back(nullptr, R->getValueAsInt(A), "");
Types.emplace_back("unsigned");
} else if (isa<StringRecTy>(SymbolV->getType()) ||
isa<CodeRecTy>(SymbolV->getType())) {
} else if (isa<StringRecTy>(SymbolV->getType())) {
Actions.emplace_back(nullptr, 0, std::string(R->getValueAsString(A)));
Types.emplace_back("std::string");
} else {

View File

@ -150,7 +150,7 @@ protected:
/// A block of arbitrary C++ to finish testing the match.
/// FIXME: This is a temporary measure until we have actual pattern matching
const CodeInit *MatchingFixupCode = nullptr;
const StringInit *MatchingFixupCode = nullptr;
/// The MatchData defined by the match stage and required by the apply stage.
/// This allows the plumbing of arbitrary data from C++ predicates between the
@ -199,7 +199,7 @@ public:
unsigned allocUID() { return UID++; }
StringRef getName() const { return TheDef.getName(); }
const Record &getDef() const { return TheDef; }
const CodeInit *getMatchingFixupCode() const { return MatchingFixupCode; }
const StringInit *getMatchingFixupCode() const { return MatchingFixupCode; }
size_t getNumRoots() const { return Roots.size(); }
GIMatchDag &getMatchDag() { return MatchDag; }
@ -514,10 +514,10 @@ bool CombineRule::parseMatcher(const CodeGenTarget &Target) {
// Parse arbitrary C++ code we have in lieu of supporting MIR matching
if (const CodeInit *CodeI = dyn_cast<CodeInit>(Matchers->getArg(I))) {
if (const StringInit *StringI = dyn_cast<StringInit>(Matchers->getArg(I))) {
assert(!MatchingFixupCode &&
"Only one block of arbitrary code is currently permitted");
MatchingFixupCode = CodeI;
MatchingFixupCode = StringI;
MatchDag.setHasPostMatchPredicate(true);
continue;
}
@ -807,7 +807,7 @@ void GICombinerEmitter::generateCodeForTree(raw_ostream &OS,
}
OS << ") {\n" << Indent << " ";
if (const CodeInit *Code = dyn_cast<CodeInit>(Applyer->getArg(0))) {
if (const StringInit *Code = dyn_cast<StringInit>(Applyer->getArg(0))) {
OS << CodeExpander(Code->getAsUnquotedString(), Expansions,
RuleDef.getLoc(), ShowExpansions)
<< "\n"

View File

@ -533,14 +533,11 @@ static unsigned getPredicates(DenseMap<const Record *, unsigned> &PredicateMap,
static void printPredicates(std::vector<const Record *> &Predicates,
StringRef Name, raw_ostream &o) {
for (unsigned i = 0; i < Predicates.size(); ++i) {
Init *Pred = Predicates[i]->getValueInit(Name);
if (CodeInit *SI = dyn_cast<CodeInit>(Pred))
o << " case " << i + 1 << ": {\n"
<< " // " << Predicates[i]->getName().str() << "\n"
<< " " << SI->getValue() << "\n"
<< " }\n";
else
llvm_unreachable("Unexpected predicate field!");
StringRef Pred = Predicates[i]->getValueAsString(Name);
o << " case " << i + 1 << ": {\n"
<< " // " << Predicates[i]->getName().str() << "\n"
<< " " << Pred.data() << "\n"
<< " }\n";
}
}

View File

@ -54,6 +54,7 @@ struct GenericEnum {
struct GenericField {
std::string Name;
RecTy *RecType = nullptr;
bool IsCode = false;
bool IsIntrinsic = false;
bool IsInstruction = false;
GenericEnum *Enum = nullptr;
@ -111,14 +112,15 @@ private:
std::string primaryRepresentation(SMLoc Loc, const GenericField &Field,
Init *I) {
if (StringInit *SI = dyn_cast<StringInit>(I))
return SI->getAsString();
else if (BitsInit *BI = dyn_cast<BitsInit>(I))
if (StringInit *SI = dyn_cast<StringInit>(I)) {
if (Field.IsCode || SI->hasCodeFormat())
return std::string(SI->getValue());
else
return SI->getAsString();
} else if (BitsInit *BI = dyn_cast<BitsInit>(I))
return "0x" + utohexstr(getAsInt(BI));
else if (BitInit *BI = dyn_cast<BitInit>(I))
return BI->getValue() ? "true" : "false";
else if (CodeInit *CI = dyn_cast<CodeInit>(I))
return std::string(CI->getValue());
else if (Field.IsIntrinsic)
return "Intrinsic::" + getIntrinsic(I).EnumName;
else if (Field.IsInstruction)
@ -150,10 +152,6 @@ private:
bool compareBy(Record *LHS, Record *RHS, const SearchIndex &Index);
bool isIntegral(Init *I) {
return isa<BitsInit>(I) || isa<CodeInit>(I) || isIntrinsic(I);
}
std::string searchableFieldType(const GenericTable &Table,
const SearchIndex &Index,
const GenericField &Field, TypeContext Ctx) {
@ -545,13 +543,19 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
OS << "#endif\n\n";
}
bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *II) {
if (auto DI = dyn_cast<DefInit>(II)) {
Record *TypeRec = DI->getDef();
if (TypeRec->isSubClassOf("GenericEnum")) {
Field.Enum = EnumMap[TypeRec];
Field.RecType = RecordRecTy::get(Field.Enum->Class);
bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *TypeOf) {
if (auto Type = dyn_cast<StringInit>(TypeOf)) {
if (Type->getValue() == "code") {
Field.IsCode = true;
return true;
} else {
if (Record *TypeRec = Records.getDef(Type->getValue())) {
if (TypeRec->isSubClassOf("GenericEnum")) {
Field.Enum = EnumMap[TypeRec];
Field.RecType = RecordRecTy::get(Field.Enum->Class);
return true;
}
}
}
}
@ -708,12 +712,14 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
for (const auto &FieldName : Fields) {
Table->Fields.emplace_back(FieldName); // Construct a GenericField.
if (auto TypeOfVal = TableRec->getValue(("TypeOf_" + FieldName).str())) {
if (!parseFieldType(Table->Fields.back(), TypeOfVal->getValue())) {
PrintFatalError(TypeOfVal,
Twine("Table '") + Table->Name +
"' has invalid 'TypeOf_" + FieldName +
"': " + TypeOfVal->getValue()->getAsString());
if (auto TypeOfRecordVal = TableRec->getValue(("TypeOf_" + FieldName).str())) {
if (!parseFieldType(Table->Fields.back(), TypeOfRecordVal->getValue())) {
PrintError(TypeOfRecordVal,
Twine("Table '") + Table->Name +
"' has invalid 'TypeOf_" + FieldName +
"': " + TypeOfRecordVal->getValue()->getAsString());
PrintFatalNote("The 'TypeOf_xxx' field must be a string naming a "
"GenericEnum record, or \"code\"");
}
}
}

View File

@ -28,7 +28,6 @@
#include "llvm/Support/SMLoc.h"
namespace llvm {
class CodeInit;
class DefInit;
class Record;
class StringInit;

View File

@ -18,7 +18,6 @@
using namespace mlir;
using namespace mlir::tblgen;
using llvm::CodeInit;
using llvm::DefInit;
using llvm::Init;
using llvm::Record;
@ -27,8 +26,6 @@ using llvm::StringInit;
// Returns the initializer's value as string if the given TableGen initializer
// is a code or string initializer. Returns the empty StringRef otherwise.
static StringRef getValueAsString(const Init *init) {
if (const auto *code = dyn_cast<CodeInit>(init))
return code->getValue().trim();
if (const auto *str = dyn_cast<StringInit>(init))
return str->getValue().trim();
return {};

View File

@ -38,7 +38,7 @@ std::string Dialect::getCppClassName() const {
static StringRef getAsStringOrEmpty(const llvm::Record &record,
StringRef fieldName) {
if (auto valueInit = record.getValueInit(fieldName)) {
if (llvm::isa<llvm::CodeInit, llvm::StringInit>(valueInit))
if (llvm::isa<llvm::StringInit>(valueInit))
return record.getValueAsString(fieldName);
}
return "";

View File

@ -547,12 +547,12 @@ StringRef Operator::getSummary() const {
bool Operator::hasAssemblyFormat() const {
auto *valueInit = def.getValueInit("assemblyFormat");
return isa<llvm::CodeInit, llvm::StringInit>(valueInit);
return isa<llvm::StringInit>(valueInit);
}
StringRef Operator::getAssemblyFormat() const {
return TypeSwitch<llvm::Init *, StringRef>(def.getValueInit("assemblyFormat"))
.Case<llvm::StringInit, llvm::CodeInit>(
.Case<llvm::StringInit>(
[&](auto *init) { return init->getValue(); });
}

View File

@ -55,7 +55,7 @@ bool DagLeaf::isEnumAttrCase() const {
}
bool DagLeaf::isStringAttr() const {
return isa<llvm::StringInit, llvm::CodeInit>(def);
return isa<llvm::StringInit>(def);
}
Constraint DagLeaf::getAsConstraint() const {

View File

@ -46,7 +46,7 @@ Optional<StringRef> TypeConstraint::getBuilderCall() const {
if (!builderCall || !builderCall->getValue())
return llvm::None;
return TypeSwitch<llvm::Init *, Optional<StringRef>>(builderCall->getValue())
.Case<llvm::StringInit, llvm::CodeInit>([&](auto *init) {
.Case<llvm::StringInit>([&](auto *init) {
StringRef value = init->getValue();
return value.empty() ? Optional<StringRef>() : value;
})

View File

@ -78,10 +78,10 @@ llvm::Optional<StringRef> TypeDef::getMnemonic() const {
return def->getValueAsOptionalString("mnemonic");
}
llvm::Optional<StringRef> TypeDef::getPrinterCode() const {
return def->getValueAsOptionalCode("printer");
return def->getValueAsOptionalString("printer");
}
llvm::Optional<StringRef> TypeDef::getParserCode() const {
return def->getValueAsOptionalCode("parser");
return def->getValueAsOptionalString("parser");
}
bool TypeDef::genAccessors() const {
return def->getValueAsBit("genAccessors");
@ -114,7 +114,7 @@ llvm::Optional<StringRef> TypeParameter::getAllocator() const {
llvm::RecordVal *code = typeParameter->getDef()->getValue("allocator");
if (!code)
return llvm::Optional<StringRef>();
if (llvm::CodeInit *ci = dyn_cast<llvm::CodeInit>(code->getValue()))
if (llvm::StringInit *ci = dyn_cast<llvm::StringInit>(code->getValue()))
return ci->getValue();
if (isa<llvm::UnsetInit>(code->getValue()))
return llvm::Optional<StringRef>();

View File

@ -137,7 +137,7 @@ static std::string replaceAllSubstrs(std::string str, const std::string &match,
static inline bool hasStringAttribute(const Record &record,
StringRef fieldName) {
auto valueInit = record.getValueInit(fieldName);
return isa<CodeInit, StringInit>(valueInit);
return isa<StringInit>(valueInit);
}
static std::string getArgumentName(const Operator &op, int index) {
@ -1796,15 +1796,15 @@ void OpEmitter::genPrinter() {
return;
auto valueInit = def.getValueInit("printer");
CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
if (!codeInit)
StringInit *stringInit = dyn_cast<StringInit>(valueInit);
if (!stringInit)
return;
auto *method =
opClass.addMethodAndPrune("void", "print", "::mlir::OpAsmPrinter &", "p");
FmtContext fctx;
fctx.addSubst("cppClass", opClass.getClassName());
auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r");
auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r");
method->body() << " " << tgfmt(printer, &fctx);
}
@ -1816,8 +1816,8 @@ void OpEmitter::genVerifier() {
<< "return ::mlir::failure();\n";
auto *valueInit = def.getValueInit("verifier");
CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
bool hasCustomVerify = codeInit && !codeInit->getValue().empty();
StringInit *stringInit = dyn_cast<StringInit>(valueInit);
bool hasCustomVerify = stringInit && !stringInit->getValue().empty();
populateSubstitutions(op, "this->getAttr", "this->getODSOperands",
"this->getODSResults", verifyCtx);
@ -1841,7 +1841,7 @@ void OpEmitter::genVerifier() {
if (hasCustomVerify) {
FmtContext fctx;
fctx.addSubst("cppClass", opClass.getClassName());
auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r");
auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r");
body << " " << tgfmt(printer, &fctx);
} else {
body << " return ::mlir::success();\n";