TableGen has the following reserved keywords, which cannot be used as
identifiers::
- bit bits class code dag
- def else false foreach defm
- defset defvar field if in
- include int let list multiclass
- string then true
+ assert bit bits class code
+ dag def else false foreach
+ defm defset defvar field if
+ in include int let list
+ multiclass string then true
.. warning::
The ``field`` reserved word is deprecated.
.. productionlist::
TableGenFile: `Statement`*
- Statement: `Class` | `Def` | `Defm` | `Defset` | `Defvar` | `Foreach`
- :| `If` | `Let` | `MultiClass`
+ Statement: `Assert` | `Class` | `Def` | `Defm` | `Defset` | `Defvar`
+ :| `Foreach` | `If` | `Let` | `MultiClass`
The following sections describe each of these top-level statements.
BodyItem: (`Type` | "code") `TokIdentifier` ["=" `Value`] ";"
:| "let" `TokIdentifier` ["{" `RangeList` "}"] "=" `Value` ";"
:| "defvar" `TokIdentifier` "=" `Value` ";"
+ :| `Assert`
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
The ``if`` statement can also be used in a record :token:`Body`.
+``assert`` --- check that a condition is true
+---------------------------------------------
+
+The ``assert`` statement checks a boolean condition to be sure that it is true
+and prints an error message if it is not.
+
+.. productionlist::
+ Assert: "assert" `condition` "," `message` ";"
+
+If the boolean condition is true, the statement does nothing. If the
+condition is false, it prints a nonfatal error message. The **message**, which
+can be an arbitrary string expression, is included in the error message as a
+note. The exact behavior of the ``assert`` statement depends on its
+placement.
+
+* At top level, the assertion is checked immediately.
+
+* In a record definition, the statement is saved and all assertions are
+ checked after the record is completely built.
+
+* In a class definition, the assertions are saved and inherited by all
+ the record definitions that inherit from the class. The assertions are
+ then checked when the records are completely built. [this placement is not
+ yet available]
+
+* In a multiclass definition, ... [this placement is not yet available]
+
+
Additional Details
==================
SmallVector<SMLoc, 4> Locs;
SmallVector<Init *, 0> TemplateArgs;
SmallVector<RecordVal, 0> Values;
+ // Vector of [source location, condition Init, message Init].
+ SmallVector<std::tuple<SMLoc, Init *, Init *>, 0> Assertions;
// All superclasses in the inheritance forest in post-order (yes, it
// must be a forest; diamond-shaped inheritance is not allowed).
ArrayRef<RecordVal> getValues() const { return Values; }
+ ArrayRef<std::tuple<SMLoc, Init *, Init *>> getAssertions() const {
+ return Assertions;
+ }
+
ArrayRef<std::pair<Record *, SMRange>> getSuperClasses() const {
return SuperClasses;
}
removeValue(StringInit::get(Name));
}
+ void addAssertion(SMLoc Loc, Init *Condition, Init *Message) {
+ Assertions.push_back(std::make_tuple(Loc, Condition, Message));
+ }
+
bool isSubClassOf(const Record *R) const {
for (const auto &SCPair : SuperClasses)
if (SCPair.first == R)
.Case("if", tgtok::If)
.Case("then", tgtok::Then)
.Case("else", tgtok::ElseKW)
+ .Case("assert", tgtok::Assert)
.Default(tgtok::Id);
// A couple of tokens require special processing.
// Reserved keywords. ('ElseKW' is named to distinguish it from the
// existing 'Else' that means the preprocessor #else.)
- Bit, Bits, Class, Code, Dag, Def, Defm, Defset, Defvar, ElseKW, FalseKW,
- Field, Foreach, If, In, Include, Int, Let, List, MultiClass,
+ Assert, Bit, Bits, Class, Code, Dag, Def, Defm, Defset, Defvar, ElseKW,
+ FalseKW, Field, Foreach, If, In, Include, Int, Let, List, MultiClass,
String, Then, TrueKW,
// Bang operators.
Rec->resolveReferences();
checkConcrete(*Rec);
+ CheckRecordAsserts(*Rec);
+
if (!isa<StringInit>(Rec->getNameInit())) {
PrintError(Rec->getLoc(), Twine("record name '") +
Rec->getNameInit()->getAsString() +
// Parser Code
//===----------------------------------------------------------------------===//
-/// isObjectStart - Return true if this is a valid first token for an Object.
+/// isObjectStart - Return true if this is a valid first token for a statement.
static bool isObjectStart(tgtok::TokKind K) {
- return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm ||
- K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach ||
- K == tgtok::Defset || K == tgtok::Defvar || K == tgtok::If;
+ return K == tgtok::Assert || K == tgtok::Class || K == tgtok::Def ||
+ K == tgtok::Defm || K == tgtok::Defset || K == tgtok::Defvar ||
+ K == tgtok::Foreach || K == tgtok::If || K == tgtok::Let ||
+ K == tgtok::MultiClass;
}
bool TGParser::consume(tgtok::TokKind K) {
}
}
-/// ParseIDValue - This is just like ParseIDValue above, but it assumes the ID
-/// has already been read.
+/// ParseIDValue
Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
IDParseMode Mode) {
if (CurRec) {
return R;
}
-/// ParseValue - Parse a tblgen value. This returns null on error.
+/// ParseValue - Parse a TableGen value. This returns null on error.
///
/// Value ::= SimpleValue ValueSuffix*
/// ValueSuffix ::= '{' BitList '}'
return false;
}
-/// ParseBodyItem - Parse a single item at within the body of a def or class.
+/// ParseBodyItem - Parse a single item within the body of a def or class.
///
/// BodyItem ::= Declaration ';'
/// BodyItem ::= LET ID OptionalBitList '=' Value ';'
/// BodyItem ::= Defvar
+/// BodyItem ::= Assert
bool TGParser::ParseBodyItem(Record *CurRec) {
+ if (Lex.getCode() == tgtok::Assert)
+ return ParseAssert(nullptr, CurRec);
+
if (Lex.getCode() == tgtok::Defvar)
return ParseDefvar();
return false;
}
+/// ParseAssert - Parse an assert statement.
+///
+/// Assert ::= ASSERT condition , message ;
+bool TGParser::ParseAssert(MultiClass *CurMultiClass, Record *CurRec) {
+ SMLoc Loc = Lex.getLoc();
+ assert(Lex.getCode() == tgtok::Assert && "Unknown tok");
+ Lex.Lex(); // Eat the 'assert' token.
+
+ SMLoc ConditionLoc = Lex.getLoc();
+ Init *Condition = ParseValue(CurRec);
+ if (!Condition)
+ return true;
+
+ if (!consume(tgtok::comma)) {
+ TokError("expected ',' in assert statement");
+ return true;
+ }
+
+ Init *Message = ParseValue(CurRec);
+ if (!Message)
+ return true;
+
+ if (!consume(tgtok::semi))
+ return TokError("expected ';'");
+
+ if (CurMultiClass) {
+ assert(false && "assert in multiclass not yet supported");
+ } else if (CurRec) {
+ CurRec->addAssertion(ConditionLoc, Condition, Message);
+ } else { // at top level
+ RecordResolver R(*CurRec);
+ Init *Value = Condition->resolveReferences(R);
+ Init *Text = Message->resolveReferences(R);
+ CheckAssert(ConditionLoc, Value, Text);
+ }
+
+ return false;
+}
+
/// ParseClass - Parse a tblgen class definition.
///
/// ClassInst ::= CLASS ID TemplateArgList? ObjectBody
while (Lex.getCode() != tgtok::r_brace) {
switch (Lex.getCode()) {
default:
- return TokError("expected 'let', 'def', 'defm', 'defvar', 'foreach' "
- "or 'if' in multiclass body");
- case tgtok::Let:
+ return TokError("expected 'assert', 'def', 'defm', 'defvar', "
+ "'foreach', 'if', or 'let' in multiclass body");
+ case tgtok::Assert:
+ return TokError("an assert statement in a multiclass is not yet supported");
+
case tgtok::Def:
case tgtok::Defm:
case tgtok::Defvar:
case tgtok::Foreach:
case tgtok::If:
+ case tgtok::Let:
if (ParseObject(CurMultiClass))
return true;
break;
/// Object ::= LETCommand Object
/// Object ::= Defset
/// Object ::= Defvar
+/// Object ::= Assert
bool TGParser::ParseObject(MultiClass *MC) {
switch (Lex.getCode()) {
default:
- return TokError("Expected class, def, defm, defset, multiclass, let, "
- "foreach or if");
- case tgtok::Let: return ParseTopLevelLet(MC);
- case tgtok::Def: return ParseDef(MC);
- case tgtok::Foreach: return ParseForeach(MC);
- case tgtok::If: return ParseIf(MC);
- case tgtok::Defm: return ParseDefm(MC);
+ return TokError(
+ "Expected assert, class, def, defm, defset, foreach, if, or let");
+ case tgtok::Assert: return ParseAssert(MC, nullptr);
+ case tgtok::Def: return ParseDef(MC);
+ case tgtok::Defm: return ParseDefm(MC);
+ case tgtok::Defvar: return ParseDefvar();
+ case tgtok::Foreach: return ParseForeach(MC);
+ case tgtok::If: return ParseIf(MC);
+ case tgtok::Let: return ParseTopLevelLet(MC);
case tgtok::Defset:
if (MC)
return TokError("defset is not allowed inside multiclass");
return ParseDefset();
- case tgtok::Defvar:
- return ParseDefvar();
case tgtok::Class:
if (MC)
return TokError("class is not allowed inside multiclass");
return TokError("Unexpected input at top level");
}
+// Check an assertion: Obtain the condition value and be sure it is true.
+// If not, print a nonfatal error along with the message.
+void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {
+ auto *CondValue = dyn_cast_or_null<IntInit>(
+ Condition->convertInitializerTo(IntRecTy::get()));
+ if (CondValue) {
+ if (!CondValue->getValue()) {
+ PrintError(Loc, "assertion failed");
+ if (auto *MessageInit = dyn_cast<StringInit>(Message))
+ PrintNote(MessageInit->getValue());
+ else
+ PrintNote("(assert message is not a string)");
+ }
+ } else {
+ PrintError(Loc, "assert condition must of type bit, bits, or int.");
+ }
+}
+
+// Check all record assertions: For each one, resolve the condition
+// and message, then call CheckAssert().
+void TGParser::CheckRecordAsserts(Record &Rec) {
+ RecordResolver R(Rec);
+ R.setFinal(true);
+
+ for (auto Assertion : Rec.getAssertions()) {
+ Init *Condition = std::get<1>(Assertion)->resolveReferences(R);
+ Init *Message = std::get<2>(Assertion)->resolveReferences(R);
+ CheckAssert(std::get<0>(Assertion), Condition, Message);
+ }
+}
+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void RecordsEntry::dump() const {
if (Loop)
bool ParseForeach(MultiClass *CurMultiClass);
bool ParseIf(MultiClass *CurMultiClass);
bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
+ bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec);
bool ParseTopLevelLet(MultiClass *CurMultiClass);
void ParseLetList(SmallVectorImpl<LetRecord> &Result);
MultiClass *ParseMultiClassID();
bool ApplyLetStack(Record *CurRec);
bool ApplyLetStack(RecordsEntry &Entry);
+ void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
+ void CheckRecordAsserts(Record &Rec);
};
} // end namespace llvm
--- /dev/null
+// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s
+
+// Test the assert statement at top level.
+
+// CHECK: assertion failed
+// CHECK-NOT: note: primary name is too short
+// CHECK: note: primary name is too long
+
+defvar Name = "Grace Brewster Murray Hopper";
+
+assert !ge(!size(Name), 20), "primary name is too short: " # Name;
+assert !le(!size(Name), 20), "primary name is too long: " # Name;
+
+// CHECK: assertion failed
+// CHECK: note: first name is incorrect
+
+def Rec1 {
+ string name = "Fred Smith";
+}
+
+assert !eq(!substr(Rec1.name, 0, 3), "Jane"),
+ !strconcat("first name is incorrect: ", Rec1.name);
+
+// CHECK: assertion failed
+// CHECK: note: record Rec2 is broken
+
+def Rec2 {
+ bit broken = true;
+}
+
+assert !not(Rec2.broken), "record Rec2 is broken";
+
+// CHECK: assertion failed
+// CHECK: note: cube of 9
+
+class Cube<int n> {
+ int result = !mul(n, n, n);
+}
+
+assert !eq(Cube<9>.result, 81), "cube of 9 should be 729";
+
+// Test the assert statement in a record definition.
+
+// CHECK: assertion failed
+// CHECK-NOT: primary first name is not "Grace"
+// CHECK: primary first name is not "Grack"
+// CHECK: assertion failed
+// CHECK: foo field should be
+
+def Rec10 {
+ assert !eq(!substr(Name, 0, 5), "Grace"), "primary first name is not \"Grace\"";
+ assert !eq(!substr(Name, 0, 5), "Grack"), "primary first name is not \"Grack\"";
+ string foo = "Foo";
+ assert !eq(foo, "foo"), "foo field should be \"Foo\"";
+}
+
+// CHECK: assertion failed
+// CHECK: note: magic field is incorrect: 42
+
+def Rec11 {
+ int magic = 13;
+ assert !eq(magic, 13), "magic field is incorrect: " # magic;
+ let magic = 42;
+}
+
+// CHECK: assertion failed
+// CHECK: note: var field has wrong value
+
+def Rec12 {
+ defvar prefix = "foo_";
+ string var = prefix # "snork";
+ assert !eq(var, "foo_snorx"), "var field has wrong value: " # var;
+}
+
+// CHECK: assertion failed
+// CHECK: note: kind field has wrong value
+
+class Kind {
+ int kind = 7;
+}
+
+def Rec13 : Kind {
+ let kind = 8;
+ assert !eq(kind, 7), "kind field has wrong value: " # kind;
+}
+
+// CHECK: assertion failed
+// CHECK: note: double_result should be
+
+def Rec14 : Cube<3> {
+ int double_result = !mul(result, 2);
+ assert !eq(double_result, 53), "double_result should be 54";
+}
+
+// Test the assert statement in a class definition.
+
+// Test the assert statement in a multiclass.
+