aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/TableGenFundamentals.html42
-rw-r--r--include/llvm/TableGen/Record.h1
-rw-r--r--lib/TableGen/TGLexer.cpp1
-rw-r--r--lib/TableGen/TGLexer.h2
-rw-r--r--lib/TableGen/TGParser.cpp224
-rw-r--r--lib/TableGen/TGParser.h40
-rw-r--r--test/TableGen/ForeachList.td76
-rw-r--r--test/TableGen/ForeachLoop.td43
-rw-r--r--test/TableGen/NestedForeach.td74
-rw-r--r--test/TableGen/SiblingForeach.td277
-rw-r--r--utils/emacs/tablegen-mode.el2
-rw-r--r--utils/vim/tablegen.vim2
12 files changed, 777 insertions, 7 deletions
diff --git a/docs/TableGenFundamentals.html b/docs/TableGenFundamentals.html
index 6db1827..45baf19 100644
--- a/docs/TableGenFundamentals.html
+++ b/docs/TableGenFundamentals.html
@@ -37,6 +37,7 @@
<ol>
<li><a href="#include">File inclusion</a></li>
<li><a href="#globallet">'let' expressions</a></li>
+ <li><a href="#foreach">'foreach' blocks</a></li>
</ol></li>
</ol></li>
<li><a href="#backends">TableGen backends</a>
@@ -401,6 +402,14 @@ which case the user must specify it explicitly.</dd>
<dt><tt>list[4-7,17,2-3]</tt></dt>
<dd>A slice of the 'list' list, including elements 4,5,6,7,17,2, and 3 from
it. Elements may be included multiple times.</dd>
+<dt><tt>foreach &lt;var&gt; = &lt;list&gt; in { &lt;body&gt; }</tt></dt>
+<dt><tt>foreach &lt;var&gt; = &lt;list&gt; in &lt;def&gt;</tt></dt>
+ <dd> Replicate &lt;body&gt; or &lt;def&gt;, replacing instances of
+ &lt;var&gt; with each value in &lt;list&gt;. &lt;var&gt; is scoped at the
+ level of the <tt>foreach</tt> loop and must not conflict with any other object
+ introduced in &lt;body&gt; or &lt;def&gt;. Currently only <tt>def</tt>s are
+ expanded within &lt;body&gt;.
+ </dd>
<dt><tt>(DEF a, b)</tt></dt>
<dd>a dag value. The first element is required to be a record definition, the
remaining elements in the list may be arbitrary other values, including nested
@@ -880,6 +889,39 @@ several levels of multiclass instanciations. This also avoids the need of using
</pre>
</div>
+<!-- -------------------------------------------------------------------------->
+<h4>
+ <a name="foreach">Looping</a>
+</h4>
+
+<div>
+<p>TableGen supports the '<tt>foreach</tt>' block, which textually replicates
+the loop body, substituting iterator values for iterator references in the
+body. Example:</p>
+
+<div class="doc_code">
+<pre>
+<b>foreach</b> i = [0, 1, 2, 3] in {
+ <b>def</b> R#i : Register&lt;...&gt;;
+ <b>def</b> F#i : Register&lt;...&gt;;
+}
+</pre>
+</div>
+
+<p>This will create objects <tt>R0</tt>, <tt>R1</tt>, <tt>R2</tt> and
+<tt>R3</tt>. <tt>foreach</tt> blocks may be nested. If there is only
+one item in the body the braces may be elided:</p>
+
+<div class="doc_code">
+<pre>
+<b>foreach</b> i = [0, 1, 2, 3] in
+ <b>def</b> R#i : Register&lt;...&gt;;
+
+</pre>
+</div>
+
+</div>
+
</div>
</div>
diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h
index f51af28..5e68c10 100644
--- a/include/llvm/TableGen/Record.h
+++ b/include/llvm/TableGen/Record.h
@@ -1535,6 +1535,7 @@ struct MultiClass {
class RecordKeeper {
std::map<std::string, Record*> Classes, Defs;
+
public:
~RecordKeeper() {
for (std::map<std::string, Record*>::iterator I = Classes.begin(),
diff --git a/lib/TableGen/TGLexer.cpp b/lib/TableGen/TGLexer.cpp
index 79d9ed2..ff322e7 100644
--- a/lib/TableGen/TGLexer.cpp
+++ b/lib/TableGen/TGLexer.cpp
@@ -274,6 +274,7 @@ tgtok::TokKind TGLexer::LexIdentifier() {
.Case("dag", tgtok::Dag)
.Case("class", tgtok::Class)
.Case("def", tgtok::Def)
+ .Case("foreach", tgtok::Foreach)
.Case("defm", tgtok::Defm)
.Case("multiclass", tgtok::MultiClass)
.Case("field", tgtok::Field)
diff --git a/lib/TableGen/TGLexer.h b/lib/TableGen/TGLexer.h
index 0246ab6..8a850b5 100644
--- a/lib/TableGen/TGLexer.h
+++ b/lib/TableGen/TGLexer.h
@@ -42,7 +42,7 @@ namespace tgtok {
paste, // #
// Keywords.
- Bit, Bits, Class, Code, Dag, Def, Defm, Field, In, Int, Let, List,
+ Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List,
MultiClass, String,
// !keywords.
diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp
index c7cf097..04c4fc1 100644
--- a/lib/TableGen/TGParser.cpp
+++ b/lib/TableGen/TGParser.cpp
@@ -289,6 +289,113 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
return false;
}
+/// ProcessForeachDefs - Given a record, apply all of the variable
+/// values in all surrounding foreach loops, creating new records for
+/// each combination of values.
+bool TGParser::ProcessForeachDefs(Record *CurRec, MultiClass *CurMultiClass,
+ SMLoc Loc) {
+ // We want to instantiate a new copy of CurRec for each combination
+ // of nested loop iterator values. We don't want top instantiate
+ // any copies until we have values for each loop iterator.
+ IterSet IterVals;
+ for (LoopVector::iterator Loop = Loops.begin(), LoopEnd = Loops.end();
+ Loop != LoopEnd;
+ ++Loop) {
+ // Process this loop.
+ if (ProcessForeachDefs(CurRec, CurMultiClass, Loc,
+ IterVals, *Loop, Loop+1)) {
+ Error(Loc,
+ "Could not process loops for def " + CurRec->getNameInitAsString());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// ProcessForeachDefs - Given a record, a loop and a loop iterator,
+/// apply each of the variable values in this loop and then process
+/// subloops.
+bool TGParser::ProcessForeachDefs(Record *CurRec, MultiClass *CurMultiClass,
+ SMLoc Loc, IterSet &IterVals,
+ ForeachLoop &CurLoop,
+ LoopVector::iterator NextLoop) {
+ Init *IterVar = CurLoop.IterVar;
+ ListInit *List = dynamic_cast<ListInit *>(CurLoop.ListValue);
+
+ if (List == 0) {
+ Error(Loc, "Loop list is not a list");
+ return true;
+ }
+
+ // Process each value.
+ for (int64_t i = 0; i < List->getSize(); ++i) {
+ Init *ItemVal = List->resolveListElementReference(*CurRec, 0, i);
+ IterVals.push_back(IterRecord(IterVar, ItemVal));
+
+ if (IterVals.size() == Loops.size()) {
+ // Ok, we have all of the iterator values for this point in the
+ // iteration space. Instantiate a new record to reflect this
+ // combination of values.
+ Record *IterRec = new Record(*CurRec);
+
+ // Set the iterator values now.
+ for (IterSet::iterator i = IterVals.begin(), iend = IterVals.end();
+ i != iend;
+ ++i) {
+ VarInit *IterVar = dynamic_cast<VarInit *>(i->IterVar);
+ if (IterVar == 0) {
+ Error(Loc, "foreach iterator is unresolved");
+ return true;
+ }
+
+ TypedInit *IVal = dynamic_cast<TypedInit *>(i->IterValue);
+ if (IVal == 0) {
+ Error(Loc, "foreach iterator value is untyped");
+ return true;
+ }
+
+ IterRec->addValue(RecordVal(IterVar->getName(), IVal->getType(), false));
+
+ if (SetValue(IterRec, Loc, IterVar->getName(),
+ std::vector<unsigned>(), IVal)) {
+ Error(Loc, "when instantiating this def");
+ return true;
+ }
+
+ // Resolve it next.
+ IterRec->resolveReferencesTo(IterRec->getValue(IterVar->getName()));
+
+ // Remove it.
+ IterRec->removeValue(IterVar->getName());
+ }
+
+ if (Records.getDef(IterRec->getNameInitAsString())) {
+ Error(Loc, "def already exists: " + IterRec->getNameInitAsString());
+ return true;
+ }
+
+ Records.addDef(IterRec);
+ IterRec->resolveReferences();
+ }
+
+ if (NextLoop != Loops.end()) {
+ // Process nested loops.
+ if (ProcessForeachDefs(CurRec, CurMultiClass, Loc, IterVals, *NextLoop,
+ NextLoop+1)) {
+ Error(Loc,
+ "Could not process loops for def " +
+ CurRec->getNameInitAsString());
+ return true;
+ }
+ }
+
+ // We're done with this iterator.
+ IterVals.pop_back();
+ }
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Parser Code
//===----------------------------------------------------------------------===//
@@ -296,7 +403,8 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
/// isObjectStart - Return true if this is a valid first token for an Object.
static bool isObjectStart(tgtok::TokKind K) {
return K == tgtok::Class || K == tgtok::Def ||
- K == tgtok::Defm || K == tgtok::Let || K == tgtok::MultiClass;
+ K == tgtok::Defm || K == tgtok::Let ||
+ K == tgtok::MultiClass || K == tgtok::Foreach;
}
static std::string GetNewAnonymousName() {
@@ -698,6 +806,15 @@ Init *TGParser::ParseIDValue(Record *CurRec,
}
}
+ // If this is in a foreach loop, make sure it's not a loop iterator
+ for (LoopVector::iterator i = Loops.begin(), iend = Loops.end();
+ i != iend;
+ ++i) {
+ VarInit *IterVar = dynamic_cast<VarInit *>(i->IterVar);
+ if (IterVar && IterVar->getName() == Name)
+ return IterVar;
+ }
+
if (Mode == ParseNameMode)
return StringInit::get(Name);
@@ -1353,7 +1470,7 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
switch (Lex.getCode()) {
default: return Result;
case tgtok::l_brace: {
- if (Mode == ParseNameMode)
+ if (Mode == ParseNameMode || Mode == ParseForeachMode)
// This is the beginning of the object body.
return Result;
@@ -1605,6 +1722,50 @@ Init *TGParser::ParseDeclaration(Record *CurRec,
return DeclName;
}
+/// ParseForeachDeclaration - Read a foreach declaration, returning
+/// the name of the declared object or a NULL Init on error. Return
+/// the name of the parsed initializer list through ForeachListName.
+///
+/// ForeachDeclaration ::= ID '=' Value
+///
+Init *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) {
+ if (Lex.getCode() != tgtok::Id) {
+ TokError("Expected identifier in foreach declaration");
+ return 0;
+ }
+
+ Init *DeclName = StringInit::get(Lex.getCurStrVal());
+ Lex.Lex();
+
+ // If a value is present, parse it.
+ if (Lex.getCode() != tgtok::equal) {
+ TokError("Expected '=' in foreach declaration");
+ return 0;
+ }
+ Lex.Lex(); // Eat the '='
+
+ // Expect a list initializer.
+ ForeachListValue = ParseValue(0, 0, ParseForeachMode);
+
+ TypedInit *TypedList = dynamic_cast<TypedInit *>(ForeachListValue);
+ if (TypedList == 0) {
+ TokError("Value list is untyped");
+ return 0;
+ }
+
+ RecTy *ValueType = TypedList->getType();
+ ListRecTy *ListType = dynamic_cast<ListRecTy *>(ValueType);
+ if (ListType == 0) {
+ TokError("Value list is not of list type");
+ return 0;
+ }
+
+ RecTy *IterType = ListType->getElementType();
+ VarInit *IterVar = VarInit::get(DeclName, IterType);
+
+ return IterVar;
+}
+
/// ParseTemplateArgList - Read a template argument list, which is a non-empty
/// 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,
@@ -1817,6 +1978,63 @@ bool TGParser::ParseDef(MultiClass *CurMultiClass) {
}
}
+ if (ProcessForeachDefs(CurRec, CurMultiClass, DefLoc)) {
+ Error(DefLoc,
+ "Could not process loops for def" + CurRec->getNameInitAsString());
+ return true;
+ }
+
+ return false;
+}
+
+/// ParseForeach - Parse a for statement. Return the record corresponding
+/// to it. This returns true on error.
+///
+/// Foreach ::= FOREACH Declaration IN '{ ObjectList '}'
+/// Foreach ::= FOREACH Declaration IN Object
+///
+bool TGParser::ParseForeach(MultiClass *CurMultiClass) {
+ assert(Lex.getCode() == tgtok::Foreach && "Unknown tok");
+ Lex.Lex(); // Eat the 'for' token.
+
+ // Make a temporary object to record items associated with the for
+ // loop.
+ Init *ListValue = 0;
+ Init *IterName = ParseForeachDeclaration(ListValue);
+ if (IterName == 0)
+ return TokError("expected declaration in for");
+
+ if (Lex.getCode() != tgtok::In)
+ return TokError("Unknown tok");
+ Lex.Lex(); // Eat the in
+
+ // Create a loop object and remember it.
+ Loops.push_back(ForeachLoop(IterName, ListValue));
+
+ if (Lex.getCode() != tgtok::l_brace) {
+ // FOREACH Declaration IN Object
+ if (ParseObject(CurMultiClass))
+ return true;
+ }
+ else {
+ SMLoc BraceLoc = Lex.getLoc();
+ // Otherwise, this is a group foreach.
+ Lex.Lex(); // eat the '{'.
+
+ // Parse the object list.
+ if (ParseObjectList(CurMultiClass))
+ return true;
+
+ if (Lex.getCode() != tgtok::r_brace) {
+ TokError("expected '}' at end of foreach command");
+ return Error(BraceLoc, "to match this '{'");
+ }
+ Lex.Lex(); // Eat the }
+ }
+
+ // We've processed everything in this loop.
+ Loops.pop_back();
+
return false;
}
@@ -2010,6 +2228,7 @@ bool TGParser::ParseMultiClass() {
case tgtok::Let:
case tgtok::Def:
case tgtok::Defm:
+ case tgtok::Foreach:
if (ParseObject(CurMultiClass))
return true;
break;
@@ -2305,6 +2524,7 @@ bool TGParser::ParseObject(MultiClass *MC) {
return TokError("Expected class, def, defm, multiclass or let definition");
case tgtok::Let: return ParseTopLevelLet(MC);
case tgtok::Def: return ParseDef(MC);
+ case tgtok::Foreach: return ParseForeach(MC);
case tgtok::Defm: return ParseDefm(MC);
case tgtok::Class: return ParseClass();
case tgtok::MultiClass: return ParseMultiClass();
diff --git a/lib/TableGen/TGParser.h b/lib/TableGen/TGParser.h
index 54cd99a..509b02c 100644
--- a/lib/TableGen/TGParser.h
+++ b/lib/TableGen/TGParser.h
@@ -42,11 +42,25 @@ namespace llvm {
}
};
+ /// ForeachLoop - Record the iteration state associated with a for loop.
+ /// This is used to instantiate items in the loop body.
+ struct ForeachLoop {
+ Init *IterVar;
+ Init *ListValue;
+
+ ForeachLoop(Init *IVar, Init *LValue) : IterVar(IVar), ListValue(LValue) {};
+ };
+
class TGParser {
TGLexer Lex;
std::vector<std::vector<LetRecord> > LetStack;
std::map<std::string, MultiClass*> MultiClasses;
+ /// Loops - Keep track of any foreach loops we are within.
+ ///
+ typedef std::vector<ForeachLoop> LoopVector;
+ LoopVector Loops;
+
/// CurMultiClass - If we are parsing a 'multiclass' definition, this is the
/// current value.
MultiClass *CurMultiClass;
@@ -60,8 +74,10 @@ class TGParser {
// in the middle of creating in. For those situations, allow the
// parser to ignore missing object errors.
enum IDParseMode {
- ParseValueMode, // We are parsing a value we expect to look up.
- ParseNameMode // We are parsing a name of an object that does not yet exist.
+ ParseValueMode, // We are parsing a value we expect to look up.
+ ParseNameMode, // We are parsing a name of an object that does not yet
+ // exist.
+ ParseForeachMode // We are parsing a foreach init.
};
public:
@@ -82,6 +98,7 @@ public:
const std::vector<std::string> &getDependencies() const {
return Lex.getDependencies();
}
+
private: // Semantic analysis methods.
bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV);
bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName,
@@ -94,6 +111,23 @@ private: // Semantic analysis methods.
bool AddSubMultiClass(MultiClass *CurMC,
SubMultiClassReference &SubMultiClass);
+ // IterRecord: Map an iterator name to a value.
+ struct IterRecord {
+ Init *IterVar;
+ Init *IterValue;
+ IterRecord(Init *Var, Init *Val) : IterVar(Var), IterValue(Val) {}
+ };
+
+ // IterSet: The set of all iterator values at some point in the
+ // iteration space.
+ typedef std::vector<IterRecord> IterSet;
+
+ bool ProcessForeachDefs(Record *CurRec, MultiClass *CurMultiClass,
+ SMLoc Loc);
+ bool ProcessForeachDefs(Record *CurRec, MultiClass *CurMultiClass,
+ SMLoc Loc, IterSet &IterVals, ForeachLoop &CurLoop,
+ LoopVector::iterator NextLoop);
+
private: // Parser methods.
bool ParseObjectList(MultiClass *MC = 0);
bool ParseObject(MultiClass *MC);
@@ -116,6 +150,7 @@ private: // Parser methods.
SMLoc DefmPrefixLoc);
bool ParseDefm(MultiClass *CurMultiClass);
bool ParseDef(MultiClass *CurMultiClass);
+ bool ParseForeach(MultiClass *CurMultiClass);
bool ParseTopLevelLet(MultiClass *CurMultiClass);
std::vector<LetRecord> ParseLetList();
@@ -125,6 +160,7 @@ private: // Parser methods.
bool ParseTemplateArgList(Record *CurRec);
Init *ParseDeclaration(Record *CurRec, bool ParsingTemplateArgs);
+ Init *ParseForeachDeclaration(Init *&ForeachListValue);
SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm);
SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC);
diff --git a/test/TableGen/ForeachList.td b/test/TableGen/ForeachList.td
new file mode 100644
index 0000000..99b7e14
--- /dev/null
+++ b/test/TableGen/ForeachList.td
@@ -0,0 +1,76 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+class Register<string name, int idx> {
+ string Name = name;
+ int Index = idx;
+}
+
+foreach i = [0, 1, 2, 3, 4, 5, 6, 7] in {
+ def R#i : Register<"R"#i, i>;
+ def F#i : Register<"F"#i, i>;
+}
+
+// CHECK: def F0
+// CHECK: string Name = "F0";
+// CHECK: int Index = 0;
+
+// CHECK: def F1
+// CHECK: string Name = "F1";
+// CHECK: int Index = 1;
+
+// CHECK: def F2
+// CHECK: string Name = "F2";
+// CHECK: int Index = 2;
+
+// CHECK: def F3
+// CHECK: string Name = "F3";
+// CHECK: int Index = 3;
+
+// CHECK: def F4
+// CHECK: string Name = "F4";
+// CHECK: int Index = 4;
+
+// CHECK: def F5
+// CHECK: string Name = "F5";
+// CHECK: int Index = 5;
+
+// CHECK: def F6
+// CHECK: string Name = "F6";
+// CHECK: int Index = 6;
+
+// CHECK: def F7
+// CHECK: string Name = "F7";
+// CHECK: int Index = 7;
+
+// CHECK: def R0
+// CHECK: string Name = "R0";
+// CHECK: int Index = 0;
+
+// CHECK: def R1
+// CHECK: string Name = "R1";
+// CHECK: int Index = 1;
+
+// CHECK: def R2
+// CHECK: string Name = "R2";
+// CHECK: int Index = 2;
+
+// CHECK: def R3
+// CHECK: string Name = "R3";
+// CHECK: int Index = 3;
+
+// CHECK: def R4
+// CHECK: string Name = "R4";
+// CHECK: int Index = 4;
+
+// CHECK: def R5
+// CHECK: string Name = "R5";
+// CHECK: int Index = 5;
+
+// CHECK: def R6
+// CHECK: string Name = "R6";
+// CHECK: int Index = 6;
+
+// CHECK: def R7
+// CHECK: string Name = "R7";
+// CHECK: int Index = 7;
diff --git a/test/TableGen/ForeachLoop.td b/test/TableGen/ForeachLoop.td
new file mode 100644
index 0000000..e2defe9
--- /dev/null
+++ b/test/TableGen/ForeachLoop.td
@@ -0,0 +1,43 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+class Register<string name, int idx> {
+ string Name = name;
+ int Index = idx;
+}
+
+foreach i = [0, 1, 2, 3, 4, 5, 6, 7] in
+ def R#i : Register<"R"#i, i>;
+
+
+// CHECK: def R0
+// CHECK: string Name = "R0";
+// CHECK: int Index = 0;
+
+// CHECK: def R1
+// CHECK: string Name = "R1";
+// CHECK: int Index = 1;
+
+// CHECK: def R2
+// CHECK: string Name = "R2";
+// CHECK: int Index = 2;
+
+// CHECK: def R3
+// CHECK: string Name = "R3";
+// CHECK: int Index = 3;
+
+// CHECK: def R4
+// CHECK: string Name = "R4";
+// CHECK: int Index = 4;
+
+// CHECK: def R5
+// CHECK: string Name = "R5";
+// CHECK: int Index = 5;
+
+// CHECK: def R6
+// CHECK: string Name = "R6";
+// CHECK: int Index = 6;
+
+// CHECK: def R7
+// CHECK: string Name = "R7";
+// CHECK: int Index = 7;
diff --git a/test/TableGen/NestedForeach.td b/test/TableGen/NestedForeach.td
new file mode 100644
index 0000000..e8c16f7
--- /dev/null
+++ b/test/TableGen/NestedForeach.td
@@ -0,0 +1,74 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+class Droid<string series, int release, string model, int patchlevel> {
+ string Series = series;
+ int Release = release;
+ string Model = model;
+ int Patchlevel = patchlevel;
+}
+
+foreach S = ["R", "C"] in {
+ foreach R = [2, 3, 4] in {
+ foreach M = ["D", "P", "Q"] in {
+ foreach P = [0, 2, 4] in {
+ def S#R#M#P : Droid<S, R, M, P>;
+ }
+ }
+ }
+}
+
+// CHECK: def C2D0
+// CHECK: def C2D2
+// CHECK: def C2D4
+// CHECK: def C2P0
+// CHECK: def C2P2
+// CHECK: def C2P4
+// CHECK: def C2Q0
+// CHECK: def C2Q2
+// CHECK: def C2Q4
+// CHECK: def C3D0
+// CHECK: def C3D2
+// CHECK: def C3D4
+// CHECK: def C3P0
+// CHECK: def C3P2
+// CHECK: def C3P4
+// CHECK: def C3Q0
+// CHECK: def C3Q2
+// CHECK: def C3Q4
+// CHECK: def C4D0
+// CHECK: def C4D2
+// CHECK: def C4D4
+// CHECK: def C4P0
+// CHECK: def C4P2
+// CHECK: def C4P4
+// CHECK: def C4Q0
+// CHECK: def C4Q2
+// CHECK: def C4Q4
+// CHECK: def R2D0
+// CHECK: def R2D2
+// CHECK: def R2D4
+// CHECK: def R2P0
+// CHECK: def R2P2
+// CHECK: def R2P4
+// CHECK: def R2Q0
+// CHECK: def R2Q2
+// CHECK: def R2Q4
+// CHECK: def R3D0
+// CHECK: def R3D2
+// CHECK: def R3D4
+// CHECK: def R3P0
+// CHECK: def R3P2
+// CHECK: def R3P4
+// CHECK: def R3Q0
+// CHECK: def R3Q2
+// CHECK: def R3Q4
+// CHECK: def R4D0
+// CHECK: def R4D2
+// CHECK: def R4D4
+// CHECK: def R4P0
+// CHECK: def R4P2
+// CHECK: def R4P4
+// CHECK: def R4Q0
+// CHECK: def R4Q2
+// CHECK: def R4Q4
diff --git a/test/TableGen/SiblingForeach.td b/test/TableGen/SiblingForeach.td
new file mode 100644
index 0000000..a11f6f8
--- /dev/null
+++ b/test/TableGen/SiblingForeach.td
@@ -0,0 +1,277 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+class Set<int i = 0, int j = 0, int k = 0> {
+ int I = i;
+ int J = j;
+ int K = k;
+}
+
+foreach i = [1, 2, 3] in {
+ def I1_#i : Set<i>;
+ foreach j = [1, 2, 3] in {
+ def I1_#i#_J1_#j : Set<i, j>;
+ }
+ def I2_#i : Set<i>;
+ foreach j = [4, 5, 6] in {
+ foreach k = [1, 2, 3] in {
+ def I3_#i#_J2_#j#_K1_#k : Set<i, j, k>;
+ }
+ def I4_#i#_J3_#j : Set<i, j>;
+ }
+}
+
+// CHECK: def I1_1
+// CHECK: int I = 1;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I1_1_J1_1
+// CHECK: int I = 1;
+// CHECK: int J = 1;
+// CHECK: int K = 0;
+
+// CHECK: def I1_1_J1_2
+// CHECK: int I = 1;
+// CHECK: int J = 2;
+// CHECK: int K = 0;
+
+// CHECK: def I1_1_J1_3
+// CHECK: int I = 1;
+// CHECK: int J = 3;
+// CHECK: int K = 0;
+
+// CHECK: def I1_2
+// CHECK: int I = 2;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I1_2_J1_1
+// CHECK: int I = 2;
+// CHECK: int J = 1;
+// CHECK: int K = 0;
+
+// CHECK: def I1_2_J1_2
+// CHECK: int I = 2;
+// CHECK: int J = 2;
+// CHECK: int K = 0;
+
+// CHECK: def I1_2_J1_3
+// CHECK: int I = 2;
+// CHECK: int J = 3;
+// CHECK: int K = 0;
+
+// CHECK: def I1_3
+// CHECK: int I = 3;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I1_3_J1_1
+// CHECK: int I = 3;
+// CHECK: int J = 1;
+// CHECK: int K = 0;
+
+// CHECK: def I1_3_J1_2
+// CHECK: int I = 3;
+// CHECK: int J = 2;
+// CHECK: int K = 0;
+
+// CHECK: def I1_3_J1_3
+// CHECK: int I = 3;
+// CHECK: int J = 3;
+// CHECK: int K = 0;
+
+// CHECK: def I2_1
+// CHECK: int I = 1;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I2_2
+// CHECK: int I = 2;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I2_3
+// CHECK: int I = 3;
+// CHECK: int J = 0;
+// CHECK: int K = 0;
+
+// CHECK: def I3_1_J2_4_K1_1
+// CHECK: int I = 1;
+// CHECK: int J = 4;
+// CHECK: int K = 1;
+
+// CHECK: def I3_1_J2_4_K1_2
+// CHECK: int I = 1;
+// CHECK: int J = 4;
+// CHECK: int K = 2;
+
+// CHECK: def I3_1_J2_4_K1_3
+// CHECK: int I = 1;
+// CHECK: int J = 4;
+// CHECK: int K = 3;
+
+// CHECK: def I3_1_J2_5_K1_1
+// CHECK: int I = 1;
+// CHECK: int J = 5;
+// CHECK: int K = 1;
+
+// CHECK: def I3_1_J2_5_K1_2
+// CHECK: int I = 1;
+// CHECK: int J = 5;
+// CHECK: int K = 2;
+
+// CHECK: def I3_1_J2_5_K1_3
+// CHECK: int I = 1;
+// CHECK: int J = 5;
+// CHECK: int K = 3;
+
+// CHECK: def I3_1_J2_6_K1_1
+// CHECK: int I = 1;
+// CHECK: int J = 6;
+// CHECK: int K = 1;
+
+// CHECK: def I3_1_J2_6_K1_2
+// CHECK: int I = 1;
+// CHECK: int J = 6;
+// CHECK: int K = 2;
+
+// CHECK: def I3_1_J2_6_K1_3
+// CHECK: int I = 1;
+// CHECK: int J = 6;
+// CHECK: int K = 3;
+
+// CHECK: def I3_2_J2_4_K1_1
+// CHECK: int I = 2;
+// CHECK: int J = 4;
+// CHECK: int K = 1;
+
+// CHECK: def I3_2_J2_4_K1_2
+// CHECK: int I = 2;
+// CHECK: int J = 4;
+// CHECK: int K = 2;
+
+// CHECK: def I3_2_J2_4_K1_3
+// CHECK: int I = 2;
+// CHECK: int J = 4;
+// CHECK: int K = 3;
+
+// CHECK: def I3_2_J2_5_K1_1
+// CHECK: int I = 2;
+// CHECK: int J = 5;
+// CHECK: int K = 1;
+
+// CHECK: def I3_2_J2_5_K1_2
+// CHECK: int I = 2;
+// CHECK: int J = 5;
+// CHECK: int K = 2;
+
+// CHECK: def I3_2_J2_5_K1_3
+// CHECK: int I = 2;
+// CHECK: int J = 5;
+// CHECK: int K = 3;
+
+// CHECK: def I3_2_J2_6_K1_1
+// CHECK: int I = 2;
+// CHECK: int J = 6;
+// CHECK: int K = 1;
+
+// CHECK: def I3_2_J2_6_K1_2
+// CHECK: int I = 2;
+// CHECK: int J = 6;
+// CHECK: int K = 2;
+
+// CHECK: def I3_2_J2_6_K1_3
+// CHECK: int I = 2;
+// CHECK: int J = 6;
+// CHECK: int K = 3;
+
+// CHECK: def I3_3_J2_4_K1_1
+// CHECK: int I = 3;
+// CHECK: int J = 4;
+// CHECK: int K = 1;
+
+// CHECK: def I3_3_J2_4_K1_2
+// CHECK: int I = 3;
+// CHECK: int J = 4;
+// CHECK: int K = 2;
+
+// CHECK: def I3_3_J2_4_K1_3
+// CHECK: int I = 3;
+// CHECK: int J = 4;
+// CHECK: int K = 3;
+
+// CHECK: def I3_3_J2_5_K1_1
+// CHECK: int I = 3;
+// CHECK: int J = 5;
+// CHECK: int K = 1;
+
+// CHECK: def I3_3_J2_5_K1_2
+// CHECK: int I = 3;
+// CHECK: int J = 5;
+// CHECK: int K = 2;
+
+// CHECK: def I3_3_J2_5_K1_3
+// CHECK: int I = 3;
+// CHECK: int J = 5;
+// CHECK: int K = 3;
+
+// CHECK: def I3_3_J2_6_K1_1
+// CHECK: int I = 3;
+// CHECK: int J = 6;
+// CHECK: int K = 1;
+
+// CHECK: def I3_3_J2_6_K1_2
+// CHECK: int I = 3;
+// CHECK: int J = 6;
+// CHECK: int K = 2;
+
+// CHECK: def I3_3_J2_6_K1_3
+// CHECK: int I = 3;
+// CHECK: int J = 6;
+// CHECK: int K = 3;
+
+// CHECK: def I4_1_J3_4
+// CHECK: int I = 1;
+// CHECK: int J = 4;
+// CHECK: int K = 0;
+
+// CHECK: def I4_1_J3_5
+// CHECK: int I = 1;
+// CHECK: int J = 5;
+// CHECK: int K = 0;
+
+// CHECK: def I4_1_J3_6
+// CHECK: int I = 1;
+// CHECK: int J = 6;
+// CHECK: int K = 0;
+
+// CHECK: def I4_2_J3_4
+// CHECK: int I = 2;
+// CHECK: int J = 4;
+// CHECK: int K = 0;
+
+// CHECK: def I4_2_J3_5
+// CHECK: int I = 2;
+// CHECK: int J = 5;
+// CHECK: int K = 0;
+
+// CHECK: def I4_2_J3_6
+// CHECK: int I = 2;
+// CHECK: int J = 6;
+// CHECK: int K = 0;
+
+// CHECK: def I4_3_J3_4
+// CHECK: int I = 3;
+// CHECK: int J = 4;
+// CHECK: int K = 0;
+
+// CHECK: def I4_3_J3_5
+// CHECK: int I = 3;
+// CHECK: int J = 5;
+// CHECK: int K = 0;
+
+// CHECK: def I4_3_J3_6
+// CHECK: int I = 3;
+// CHECK: int J = 6;
+// CHECK: int K = 0;
diff --git a/utils/emacs/tablegen-mode.el b/utils/emacs/tablegen-mode.el
index 3853ce6..26b6639 100644
--- a/utils/emacs/tablegen-mode.el
+++ b/utils/emacs/tablegen-mode.el
@@ -13,7 +13,7 @@
(defvar tablegen-font-lock-keywords
(let ((kw (regexp-opt '("class" "defm" "def" "field" "include" "in"
- "let" "multiclass")
+ "let" "multiclass", "foreach")
'words))
(type-kw (regexp-opt '("bit" "bits" "code" "dag" "int" "list" "string")
'words))
diff --git a/utils/vim/tablegen.vim b/utils/vim/tablegen.vim
index 3043489..a9b0e4e 100644
--- a/utils/vim/tablegen.vim
+++ b/utils/vim/tablegen.vim
@@ -14,7 +14,7 @@ syntax sync minlines=100
syn case match
-syn keyword tgKeyword def let in code dag field include defm
+syn keyword tgKeyword def let in code dag field include defm foreach
syn keyword tgType class int string list bit bits multiclass
syn match tgNumber /\<\d\+\>/