From 44c484668275a76dc0906c5de4402c0a50fa2372 Mon Sep 17 00:00:00 2001 From: Nicolai Haehnle Date: Mon, 5 Mar 2018 15:21:04 +0000 Subject: [PATCH] TableGen: Reimplement !foreach using the resolving mechanism Summary: This changes the syntax of !foreach so that the first "parameter" is a new syntactic variable: !foreach(x, lst, expr) will define the variable x within the scope of expr, and evaluation of the !foreach will substitute elements of the given list (or dag) for x in expr. Aside from leading to a nicer syntax, this allows more complex expressions where x is deeply nested, or even constant expressions in which x does not occur at all. !foreach is currently not actually used anywhere in trunk, but I plan to use it in the AMDGPU backend. If out-of-tree targets are using it, they can adjust to the new syntax very easily. Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805 Reviewers: arsenm, craig.topper, tra, MartinO Subscribers: wdng, llvm-commits, tpr Differential Revision: https://reviews.llvm.org/D43651 llvm-svn: 326705 --- docs/TableGen/LangIntro.rst | 6 +- include/llvm/TableGen/Record.h | 41 +++++++++ lib/TableGen/Record.cpp | 137 +++++++++++++++---------------- lib/TableGen/TGParser.cpp | 123 ++++++++++++++++++++++----- test/TableGen/MultiPat.td | 20 ++--- test/TableGen/TargetInstrSpec.td | 20 ++--- test/TableGen/foreach-eval.td | 28 ++----- test/TableGen/foreach-leak.td | 25 ++++++ test/TableGen/foreach.td | 26 +++--- 9 files changed, 274 insertions(+), 152 deletions(-) create mode 100644 test/TableGen/foreach-leak.td diff --git a/docs/TableGen/LangIntro.rst b/docs/TableGen/LangIntro.rst index 534597b041b..69e5c1426e4 100644 --- a/docs/TableGen/LangIntro.rst +++ b/docs/TableGen/LangIntro.rst @@ -192,9 +192,9 @@ supported include: for 'a' in 'c.' This operation is analogous to $(subst) in GNU make. ``!foreach(a, b, c)`` - For each member of dag or list 'b' apply operator 'c.' 'a' is a dummy - variable that should be declared as a member variable of an instantiated - class. This operation is analogous to $(foreach) in GNU make. + For each member of dag or list 'b' apply operator 'c'. 'a' is the name + of a variable that will be substituted by members of 'b' in 'c'. + This operation is analogous to $(foreach) in GNU make. ``!head(a)`` The first element of list 'a.' diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h index 970cc8f9b8e..323f7f06768 100644 --- a/include/llvm/TableGen/Record.h +++ b/include/llvm/TableGen/Record.h @@ -17,6 +17,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" @@ -1030,6 +1031,9 @@ public: static FieldInit *get(Init *R, StringInit *FN); + Init *getRecord() const { return Rec; } + StringInit *getFieldName() const { return FieldName; } + Init *getBit(unsigned Bit) const override; Init *resolveReferences(Resolver &R) const override; @@ -1632,6 +1636,26 @@ public: virtual bool keepUnsetBits() const { return false; } }; +/// Resolve arbitrary mappings. +class MapResolver final : public Resolver { + struct MappedValue { + Init *V; + bool Resolved; + + MappedValue() : V(nullptr), Resolved(false) {} + MappedValue(Init *V, bool Resolved) : V(V), Resolved(Resolved) {} + }; + + DenseMap Map; + +public: + explicit MapResolver(Record *CurRec = nullptr) : Resolver(CurRec) {} + + void set(Init *Key, Init *Value) { Map[Key] = {Value, false}; } + + Init *resolve(Init *VarName) override; +}; + /// Resolve all variables from a record except for unset variables. class RecordResolver final : public Resolver { DenseMap Cache; @@ -1664,6 +1688,23 @@ public: } }; +/// Delegate resolving to a sub-resolver, but shadow some variable names. +class ShadowResolver final : public Resolver { + Resolver &R; + DenseSet Shadowed; + +public: + explicit ShadowResolver(Resolver &R) : Resolver(R.getCurrentRecord()), R(R) {} + + void addShadow(Init *Key) { Shadowed.insert(Key); } + + Init *resolve(Init *VarName) override { + if (Shadowed.count(VarName)) + return nullptr; + return R.resolve(VarName); + } +}; + } // end namespace llvm #endif // LLVM_TABLEGEN_RECORD_H diff --git a/lib/TableGen/Record.cpp b/lib/TableGen/Record.cpp index c08ae97dc57..990ef928f66 100644 --- a/lib/TableGen/Record.cpp +++ b/lib/TableGen/Record.cpp @@ -912,83 +912,57 @@ void TernOpInit::Profile(FoldingSetNodeID &ID) const { ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType()); } -// Evaluates operation RHSo after replacing all operands matching LHS with Arg. -static Init *EvaluateOperation(OpInit *RHSo, Init *LHS, Init *Arg, - Record *CurRec, MultiClass *CurMultiClass) { +static Init *ForeachApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) { + MapResolver R(CurRec); + R.set(LHS, MHSe); + return RHS->resolveReferences(R); +} - SmallVector NewOperands; - NewOperands.reserve(RHSo->getNumOperands()); - for (unsigned i = 0, e = RHSo->getNumOperands(); i < e; ++i) { - if (auto *RHSoo = dyn_cast(RHSo->getOperand(i))) { - if (Init *Result = - EvaluateOperation(RHSoo, LHS, Arg, CurRec, CurMultiClass)) - NewOperands.push_back(Result); - else - NewOperands.push_back(RHSoo); - } else if (LHS->getAsString() == RHSo->getOperand(i)->getAsString()) { - NewOperands.push_back(Arg); - } else { - NewOperands.push_back(RHSo->getOperand(i)); - } +static Init *ForeachDagApply(Init *LHS, DagInit *MHSd, Init *RHS, + Record *CurRec) { + bool Change = false; + Init *Val = ForeachApply(LHS, MHSd->getOperator(), RHS, CurRec); + if (Val != MHSd->getOperator()) + Change = true; + + SmallVector, 8> NewArgs; + for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) { + Init *Arg = MHSd->getArg(i); + Init *NewArg; + StringInit *ArgName = MHSd->getArgName(i); + + if (DagInit *Argd = dyn_cast(Arg)) + NewArg = ForeachDagApply(LHS, Argd, RHS, CurRec); + else + NewArg = ForeachApply(LHS, Arg, RHS, CurRec); + + NewArgs.push_back(std::make_pair(NewArg, ArgName)); + if (Arg != NewArg) + Change = true; } - // Now run the operator and use its result as the new leaf - const OpInit *NewOp = RHSo->clone(NewOperands); - Init *NewVal = NewOp->Fold(CurRec, CurMultiClass); - return (NewVal != NewOp) ? NewVal : nullptr; + if (Change) + return DagInit::get(Val, nullptr, NewArgs); + return MHSd; } // Applies RHS to all elements of MHS, using LHS as a temp variable. static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, - Record *CurRec, MultiClass *CurMultiClass) { - OpInit *RHSo = dyn_cast(RHS); + Record *CurRec) { + if (DagInit *MHSd = dyn_cast(MHS)) + return ForeachDagApply(LHS, MHSd, RHS, CurRec); - if (!RHSo) - PrintFatalError(CurRec->getLoc(), "!foreach requires an operator\n"); - - TypedInit *LHSt = dyn_cast(LHS); - - if (!LHSt) - PrintFatalError(CurRec->getLoc(), "!foreach requires typed variable\n"); - - DagInit *MHSd = dyn_cast(MHS); - if (MHSd) { - Init *Val = MHSd->getOperator(); - if (Init *Result = EvaluateOperation(RHSo, LHS, Val, CurRec, CurMultiClass)) - Val = Result; - - SmallVector, 8> args; - for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) { - Init *Arg = MHSd->getArg(i); - StringInit *ArgName = MHSd->getArgName(i); - // If this is a dag, recurse - if (isa(Arg)) { - if (Init *Result = - ForeachHelper(LHS, Arg, RHSo, Type, CurRec, CurMultiClass)) - Arg = Result; - } else if (Init *Result = - EvaluateOperation(RHSo, LHS, Arg, CurRec, CurMultiClass)) { - Arg = Result; - } - - // TODO: Process arg names - args.push_back(std::make_pair(Arg, ArgName)); - } - - return DagInit::get(Val, nullptr, args); - } - - ListInit *MHSl = dyn_cast(MHS); - ListRecTy *ListType = dyn_cast(Type); - if (MHSl && ListType) { + if (ListInit *MHSl = dyn_cast(MHS)) { SmallVector NewList(MHSl->begin(), MHSl->end()); - for (Init *&Arg : NewList) { - if (Init *Result = - EvaluateOperation(RHSo, LHS, Arg, CurRec, CurMultiClass)) - Arg = Result; + + for (Init *&Item : NewList) { + Init *NewItem = ForeachApply(LHS, Item, RHS, CurRec); + if (NewItem != Item) + Item = NewItem; } - return ListInit::get(NewList, ListType->getElementType()); + return ListInit::get(NewList, cast(Type)->getElementType()); } + return nullptr; } @@ -1038,8 +1012,7 @@ Init *TernOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) const { } case FOREACH: { - if (Init *Result = - ForeachHelper(LHS, MHS, RHS, getType(), CurRec, CurMultiClass)) + if (Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), CurRec)) return Result; break; } @@ -1081,7 +1054,15 @@ Init *TernOpInit::resolveReferences(Resolver &R) const { } Init *mhs = MHS->resolveReferences(R); - Init *rhs = RHS->resolveReferences(R); + Init *rhs; + + if (getOpcode() == FOREACH) { + ShadowResolver SR(R); + SR.addShadow(lhs); + rhs = RHS->resolveReferences(SR); + } else { + rhs = RHS->resolveReferences(R); + } if (LHS != lhs || MHS != mhs || RHS != rhs) return (TernOpInit::get(getOpcode(), lhs, mhs, rhs, getType())) @@ -1821,6 +1802,24 @@ Init *llvm::QualifyName(Record &CurRec, MultiClass *CurMultiClass, return NewName; } +Init *MapResolver::resolve(Init *VarName) { + auto It = Map.find(VarName); + if (It == Map.end()) + return nullptr; + + Init *I = It->second.V; + + if (!It->second.Resolved && Map.size() > 1) { + // Resolve mutual references among the mapped variables, but prevent + // infinite recursion. + Map.erase(It); + I = I->resolveReferences(*this); + Map[VarName] = {I, true}; + } + + return I; +} + Init *RecordResolver::resolve(Init *VarName) { Init *Val = Cache.lookup(VarName); if (Val) diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp index 3ef65938282..b42580c9cda 100644 --- a/lib/TableGen/TGParser.cpp +++ b/lib/TableGen/TGParser.cpp @@ -985,8 +985,109 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { return nullptr; } + case tgtok::XForEach: { // Value ::= !foreach '(' Id ',' Value ',' Value ')' + SMLoc OpLoc = Lex.getLoc(); + Lex.Lex(); // eat the operation + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !foreach"); + return nullptr; + } + + if (Lex.Lex() != tgtok::Id) { // eat the '(' + TokError("first argument of !foreach must be an identifier"); + return nullptr; + } + + Init *LHS = StringInit::get(Lex.getCurStrVal()); + + if (CurRec->getValue(LHS)) { + TokError((Twine("iteration variable '") + LHS->getAsString() + + "' already defined") + .str()); + return nullptr; + } + + if (Lex.Lex() != tgtok::comma) { // eat the id + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + Init *MHS = ParseValue(CurRec); + if (!MHS) + return nullptr; + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + TypedInit *MHSt = dyn_cast(MHS); + if (!MHSt) { + TokError("could not get type of !foreach input"); + return nullptr; + } + + RecTy *InEltType = nullptr; + RecTy *OutEltType = nullptr; + bool IsDAG = false; + + if (ListRecTy *InListTy = dyn_cast(MHSt->getType())) { + InEltType = InListTy->getElementType(); + if (ItemType) { + if (ListRecTy *OutListTy = dyn_cast(ItemType)) { + OutEltType = OutListTy->getElementType(); + } else { + Error(OpLoc, + "expected value of type '" + Twine(ItemType->getAsString()) + + "', but got !foreach of list type"); + return nullptr; + } + } + } else if (DagRecTy *InDagTy = dyn_cast(MHSt->getType())) { + InEltType = InDagTy; + if (ItemType && !isa(ItemType)) { + Error(OpLoc, + "expected value of type '" + Twine(ItemType->getAsString()) + + "', but got !foreach of dag type"); + return nullptr; + } + IsDAG = true; + } else { + TokError("!foreach must have list or dag input"); + return nullptr; + } + + CurRec->addValue(RecordVal(LHS, InEltType, false)); + Init *RHS = ParseValue(CurRec, OutEltType); + CurRec->removeValue(LHS); + if (!RHS) + return nullptr; + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in binary operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + RecTy *OutType; + if (IsDAG) { + OutType = InEltType; + } else { + TypedInit *RHSt = dyn_cast(RHS); + if (!RHSt) { + TokError("could not get type of !foreach result"); + return nullptr; + } + OutType = RHSt->getType()->getListTy(); + } + + return (TernOpInit::get(TernOpInit::FOREACH, LHS, MHS, RHS, OutType)) + ->Fold(CurRec, CurMultiClass); + } + case tgtok::XIf: - case tgtok::XForEach: case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' TernOpInit::TernaryOp Code; RecTy *Type = nullptr; @@ -998,9 +1099,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { case tgtok::XIf: Code = TernOpInit::IF; break; - case tgtok::XForEach: - Code = TernOpInit::FOREACH; - break; case tgtok::XSubst: Code = TernOpInit::SUBST; break; @@ -1081,23 +1179,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { } break; } - case tgtok::XForEach: { - TypedInit *MHSt = dyn_cast(MHS); - if (!MHSt) { - TokError("could not get type for !foreach"); - return nullptr; - } - Type = MHSt->getType(); - if (isa(Type)) { - TypedInit *RHSt = dyn_cast(RHS); - if (!RHSt) { - TokError("could not get type of !foreach list elements"); - return nullptr; - } - Type = RHSt->getType()->getListTy(); - } - break; - } case tgtok::XSubst: { TypedInit *RHSt = dyn_cast(RHS); if (!RHSt) { diff --git a/test/TableGen/MultiPat.td b/test/TableGen/MultiPat.td index b3792777b6b..b216896fb8e 100644 --- a/test/TableGen/MultiPat.td +++ b/test/TableGen/MultiPat.td @@ -73,14 +73,6 @@ def VR128 : RegisterClass<[v2i64, v2f64], def REGCLASS : RegisterClass<[], []>; def MNEMONIC; -class decls { - // Dummy for foreach - dag pattern; - int operand; -} - -def Decls : decls; - // Define intrinsics def int_x86_sse2_add_ps : Intrinsic<"addps">; def int_x86_sse2_add_pd : Intrinsic<"addpd">; @@ -92,16 +84,16 @@ class MakePat patterns> : Pat; class Base opcode, dag opnds, dag iopnds, string asmstr, Intrinsic intr, list> patterns> : Inst, - MakePat, + MakePat; + !subst(MNEMONIC, set, operand)))))>; multiclass arith opcode, string asmstr, string intr, list> patterns> { def PS : Base; -class decls { - // Dummy for foreach - dag pattern; - int operand; -} - -def Decls : decls; - // Define intrinsics def int_x86_sse2_add_ps : Intrinsic<"addps">; def int_x86_sse2_add_pd : Intrinsic<"addpd">; @@ -85,17 +77,17 @@ def INTRINSIC : Intrinsic<"Dummy">; multiclass arith opcode, string asmstr, string intr, list patterns> { def PS : Inst(!subst("SUFFIX", "_ps", intr)), - !subst(REGCLASS, VR128, Decls.operand))))>; + !subst(REGCLASS, VR128, operand))))>; def PD : Inst(!subst("SUFFIX", "_pd", intr)), - !subst(REGCLASS, VR128, Decls.operand))))>; + !subst(REGCLASS, VR128, operand))))>; } defm ADD : arith<0x58, "add", "int_x86_sse2_addSUFFIX", diff --git a/test/TableGen/foreach-eval.td b/test/TableGen/foreach-eval.td index 5bef51e49d5..227475f9df4 100644 --- a/test/TableGen/foreach-eval.td +++ b/test/TableGen/foreach-eval.td @@ -10,11 +10,9 @@ def d3; def d4; class D { - int tmp; dag r1 = !foreach(tmp, d, !subst(d1, d0, !subst(d2, d0, !subst(d3, d0, !subst(d4, d0, tmp))))); - dag tmp2; list dl = [d]; list r2 = !foreach(tmp2, dl, !foreach(tmp, tmp2, !subst(d1, d0, @@ -29,10 +27,8 @@ class D { def d : D <(d0 d1, d2, d3, d4)>; class I i> { - int tmp; list r1 = !foreach(tmp, i, !add(3, !add(4, tmp))); - list tmp2; list> li = [i]; list> r2 = !foreach(tmp2, li, !foreach(tmp, tmp2, !add(3, !add(4, tmp)))); @@ -43,26 +39,20 @@ class I i> { // CHECK: list> r2 = [{{[[]}}8, 9, 10]]; def i : I<[1,2,3]>; -class Tmp { - dag t0; - int t1; -} -def tmp: Tmp; - class J0 pattern> { list Pattern = pattern; } class J1 - : J0<[!foreach(tmp.t1, pattern, !subst(d1, d0, - !subst(d2, d0, - !subst(d3, d0, - !subst(d4, d0, tmp.t1)))))]>; + : J0<[!foreach(tmp, pattern, !subst(d1, d0, + !subst(d2, d0, + !subst(d3, d0, + !subst(d4, d0, tmp)))))]>; class J2 patterns> - : J0; + : J0; // CHECK-LABEL: def j1 // CHECK: list Pattern = [(d0 d0:$dst, (d0 d0:$src1))]; def j1 : J1< (d1 d2:$dst, (d3 d4:$src1))>; diff --git a/test/TableGen/foreach-leak.td b/test/TableGen/foreach-leak.td new file mode 100644 index 00000000000..68867e1081b --- /dev/null +++ b/test/TableGen/foreach-leak.td @@ -0,0 +1,25 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +// CHECK: --- Defs --- + +// CHECK: def C0 { +// CHECK: list> ret = {{\[}}[1, 2, 3], [1, 2, 3]]; +// CHECK: } + +// The variable name 'a' is used both in the "inner" and in the "outer" foreach. +// The test ensure that the inner declaration of 'a' properly shadows the outer +// one. +class A lst> { + list ret = !foreach(a, lst, !add(a, 1)); +} + +class B lst1, list lst2> { + list> ret = !foreach(a, lst1, A.ret); +} + +class C lst2> { + list> ret = B<[0, 1], lst2>.ret; +} + +def C0 : C<[0, 1, 2]>; diff --git a/test/TableGen/foreach.td b/test/TableGen/foreach.td index 33b6eb5bfa8..fcc3e8bfcfd 100644 --- a/test/TableGen/foreach.td +++ b/test/TableGen/foreach.td @@ -12,26 +12,22 @@ // CHECK: list x = ["0", "1", "2"]; // CHECK: } +// CHECK: def DY { +// CHECK: list y = [5, 7]; +// CHECK: } + // CHECK: Jr // CHECK: Sr -// Variables for foreach -class decls { - string name; - int num; -} - -def Decls : decls; - class A names> { list Names = names; } -class B names> : A; +class B names> : A; -class C names> : A; +class C names> : A; -class D names> : A; +class D names> : A; class Names { list values = ["Ken Griffey", "Seymour Cray"]; @@ -45,7 +41,13 @@ def Smiths : D<["NAME", "Jane Smith"]>; def Unprocessed : D; class X a> { - list x = !foreach(Decls.num, a, !cast(Decls.num)); + list x = !foreach(num, a, !cast(num)); } def DX : X<[0, 1, 2]>; + +class Y a> { + list y = !foreach(num, a, !add(!add(4, num), !add(1, num))); +} + +def DY: Y<[0, 1]>;