mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 00:26:06 +00:00
TableGen: Add !foldl operation
Change-Id: I63d67bf6e0b315e2d3360e47e3b62c9517f38987 llvm-svn: 326790
This commit is contained in:
parent
616635022a
commit
d34f6843aa
@ -217,6 +217,15 @@ supported include:
|
|||||||
of a variable that will be substituted by members of 'b' in 'c'.
|
of a variable that will be substituted by members of 'b' in 'c'.
|
||||||
This operation is analogous to $(foreach) in GNU make.
|
This operation is analogous to $(foreach) in GNU make.
|
||||||
|
|
||||||
|
``!foldl(start, lst, a, b, expr)``
|
||||||
|
Perform a left-fold over 'lst' with the given starting value. 'a' and 'b'
|
||||||
|
are variable names which will be substituted in 'expr'. If you think of
|
||||||
|
expr as a function f(a,b), the fold will compute
|
||||||
|
'f(...f(f(start, lst[0]), lst[1]), ...), lst[n-1])' for a list of length n.
|
||||||
|
As usual, 'a' will be of the type of 'start', and 'b' will be of the type
|
||||||
|
of elements of 'lst'. These types need not be the same, but 'expr' must be
|
||||||
|
of the same type as 'start'.
|
||||||
|
|
||||||
``!head(a)``
|
``!head(a)``
|
||||||
The first element of list 'a.'
|
The first element of list 'a.'
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ wide variety of meanings:
|
|||||||
:!eq !if !head !tail !con
|
:!eq !if !head !tail !con
|
||||||
:!add !shl !sra !srl !and
|
:!add !shl !sra !srl !and
|
||||||
:!or !empty !subst !foreach !strconcat
|
:!or !empty !subst !foreach !strconcat
|
||||||
:!cast !listconcat !size
|
:!cast !listconcat !size !foldl
|
||||||
|
|
||||||
|
|
||||||
Syntax
|
Syntax
|
||||||
|
@ -316,6 +316,7 @@ protected:
|
|||||||
IK_TernOpInit,
|
IK_TernOpInit,
|
||||||
IK_UnOpInit,
|
IK_UnOpInit,
|
||||||
IK_LastOpInit,
|
IK_LastOpInit,
|
||||||
|
IK_FoldOpInit,
|
||||||
IK_StringInit,
|
IK_StringInit,
|
||||||
IK_VarInit,
|
IK_VarInit,
|
||||||
IK_VarListElementInit,
|
IK_VarListElementInit,
|
||||||
@ -913,6 +914,43 @@ public:
|
|||||||
std::string getAsString() const override;
|
std::string getAsString() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// !foldl (a, b, expr, start, lst) - Fold over a list.
|
||||||
|
class FoldOpInit : public TypedInit, public FoldingSetNode {
|
||||||
|
private:
|
||||||
|
Init *Start;
|
||||||
|
Init *List;
|
||||||
|
Init *A;
|
||||||
|
Init *B;
|
||||||
|
Init *Expr;
|
||||||
|
|
||||||
|
FoldOpInit(Init *Start, Init *List, Init *A, Init *B, Init *Expr, RecTy *Type)
|
||||||
|
: TypedInit(IK_FoldOpInit, Type), Start(Start), List(List), A(A), B(B),
|
||||||
|
Expr(Expr) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FoldOpInit(const FoldOpInit &) = delete;
|
||||||
|
FoldOpInit &operator=(const FoldOpInit &) = delete;
|
||||||
|
|
||||||
|
static bool classof(const Init *I) { return I->getKind() == IK_FoldOpInit; }
|
||||||
|
|
||||||
|
static FoldOpInit *get(Init *Start, Init *List, Init *A, Init *B, Init *Expr,
|
||||||
|
RecTy *Type);
|
||||||
|
|
||||||
|
void Profile(FoldingSetNodeID &ID) const;
|
||||||
|
|
||||||
|
// Fold - If possible, fold this to a simpler init. Return this if not
|
||||||
|
// possible to fold.
|
||||||
|
Init *Fold(Record *CurRec) const;
|
||||||
|
|
||||||
|
bool isComplete() const override { return false; }
|
||||||
|
|
||||||
|
Init *resolveReferences(Resolver &R) const override;
|
||||||
|
|
||||||
|
Init *getBit(unsigned Bit) const override;
|
||||||
|
|
||||||
|
std::string getAsString() const override;
|
||||||
|
};
|
||||||
|
|
||||||
/// 'Opcode' - Represent a reference to an entire variable object.
|
/// 'Opcode' - Represent a reference to an entire variable object.
|
||||||
class VarInit : public TypedInit {
|
class VarInit : public TypedInit {
|
||||||
Init *VarName;
|
Init *VarName;
|
||||||
|
@ -1160,6 +1160,77 @@ std::string TernOpInit::getAsString() const {
|
|||||||
RHS->getAsString() + ")";
|
RHS->getAsString() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *A, Init *B,
|
||||||
|
Init *Start, Init *List, Init *Expr,
|
||||||
|
RecTy *Type) {
|
||||||
|
ID.AddPointer(Start);
|
||||||
|
ID.AddPointer(List);
|
||||||
|
ID.AddPointer(A);
|
||||||
|
ID.AddPointer(B);
|
||||||
|
ID.AddPointer(Expr);
|
||||||
|
ID.AddPointer(Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
FoldOpInit *FoldOpInit::get(Init *Start, Init *List, Init *A, Init *B,
|
||||||
|
Init *Expr, RecTy *Type) {
|
||||||
|
static FoldingSet<FoldOpInit> ThePool;
|
||||||
|
|
||||||
|
FoldingSetNodeID ID;
|
||||||
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type);
|
||||||
|
|
||||||
|
void *IP = nullptr;
|
||||||
|
if (FoldOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
||||||
|
return I;
|
||||||
|
|
||||||
|
FoldOpInit *I = new (Allocator) FoldOpInit(Start, List, A, B, Expr, Type);
|
||||||
|
ThePool.InsertNode(I, IP);
|
||||||
|
return I;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FoldOpInit::Profile(FoldingSetNodeID &ID) const {
|
||||||
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
Init *FoldOpInit::Fold(Record *CurRec) const {
|
||||||
|
if (ListInit *LI = dyn_cast<ListInit>(List)) {
|
||||||
|
Init *Accum = Start;
|
||||||
|
for (Init *Elt : *LI) {
|
||||||
|
MapResolver R(CurRec);
|
||||||
|
R.set(A, Accum);
|
||||||
|
R.set(B, Elt);
|
||||||
|
Accum = Expr->resolveReferences(R);
|
||||||
|
}
|
||||||
|
return Accum;
|
||||||
|
}
|
||||||
|
return const_cast<FoldOpInit *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Init *FoldOpInit::resolveReferences(Resolver &R) const {
|
||||||
|
Init *NewStart = Start->resolveReferences(R);
|
||||||
|
Init *NewList = List->resolveReferences(R);
|
||||||
|
ShadowResolver SR(R);
|
||||||
|
SR.addShadow(A);
|
||||||
|
SR.addShadow(B);
|
||||||
|
Init *NewExpr = Expr->resolveReferences(SR);
|
||||||
|
|
||||||
|
if (Start == NewStart && List == NewList && Expr == NewExpr)
|
||||||
|
return const_cast<FoldOpInit *>(this);
|
||||||
|
|
||||||
|
return get(NewStart, NewList, A, B, NewExpr, getType())
|
||||||
|
->Fold(R.getCurrentRecord());
|
||||||
|
}
|
||||||
|
|
||||||
|
Init *FoldOpInit::getBit(unsigned Bit) const {
|
||||||
|
return VarBitInit::get(const_cast<FoldOpInit *>(this), Bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FoldOpInit::getAsString() const {
|
||||||
|
return (Twine("!foldl(") + Start->getAsString() + ", " + List->getAsString() +
|
||||||
|
", " + A->getAsUnquotedString() + ", " + B->getAsUnquotedString() +
|
||||||
|
", " + Expr->getAsString() + ")")
|
||||||
|
.str();
|
||||||
|
}
|
||||||
|
|
||||||
RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
|
RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
|
||||||
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
|
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
|
||||||
for (Record *Rec : RecordType->getClasses()) {
|
for (Record *Rec : RecordType->getClasses()) {
|
||||||
|
@ -480,6 +480,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
|
|||||||
.Case("cast", tgtok::XCast)
|
.Case("cast", tgtok::XCast)
|
||||||
.Case("empty", tgtok::XEmpty)
|
.Case("empty", tgtok::XEmpty)
|
||||||
.Case("subst", tgtok::XSubst)
|
.Case("subst", tgtok::XSubst)
|
||||||
|
.Case("foldl", tgtok::XFoldl)
|
||||||
.Case("foreach", tgtok::XForEach)
|
.Case("foreach", tgtok::XForEach)
|
||||||
.Case("listconcat", tgtok::XListConcat)
|
.Case("listconcat", tgtok::XListConcat)
|
||||||
.Case("strconcat", tgtok::XStrConcat)
|
.Case("strconcat", tgtok::XStrConcat)
|
||||||
|
@ -48,7 +48,7 @@ namespace tgtok {
|
|||||||
|
|
||||||
// !keywords.
|
// !keywords.
|
||||||
XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast,
|
XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast,
|
||||||
XSubst, XForEach, XHead, XTail, XSize, XEmpty, XIf, XEq,
|
XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq,
|
||||||
|
|
||||||
// Integer value.
|
// Integer value.
|
||||||
IntVal,
|
IntVal,
|
||||||
|
@ -1232,6 +1232,123 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
|
|||||||
return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec,
|
return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec,
|
||||||
CurMultiClass);
|
CurMultiClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case tgtok::XFoldl: {
|
||||||
|
// Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')'
|
||||||
|
Lex.Lex(); // eat the operation
|
||||||
|
if (Lex.getCode() != tgtok::l_paren) {
|
||||||
|
TokError("expected '(' after !foldl");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Lex.Lex(); // eat the '('
|
||||||
|
|
||||||
|
Init *StartUntyped = ParseValue(CurRec);
|
||||||
|
if (!StartUntyped)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
TypedInit *Start = dyn_cast<TypedInit>(StartUntyped);
|
||||||
|
if (!Start) {
|
||||||
|
TokError(Twine("could not get type of !foldl start: '") +
|
||||||
|
StartUntyped->getAsString() + "'");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.getCode() != tgtok::comma) {
|
||||||
|
TokError("expected ',' in !foldl");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Lex.Lex(); // eat the ','
|
||||||
|
|
||||||
|
Init *ListUntyped = ParseValue(CurRec);
|
||||||
|
if (!ListUntyped)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
TypedInit *List = dyn_cast<TypedInit>(ListUntyped);
|
||||||
|
if (!List) {
|
||||||
|
TokError(Twine("could not get type of !foldl list: '") +
|
||||||
|
ListUntyped->getAsString() + "'");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListRecTy *ListType = dyn_cast<ListRecTy>(List->getType());
|
||||||
|
if (!ListType) {
|
||||||
|
TokError(Twine("!foldl list must be a list, but is of type '") +
|
||||||
|
List->getType()->getAsString());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.getCode() != tgtok::comma) {
|
||||||
|
TokError("expected ',' in !foldl");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.Lex() != tgtok::Id) { // eat the ','
|
||||||
|
TokError("third argument of !foldl must be an identifier");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Init *A = StringInit::get(Lex.getCurStrVal());
|
||||||
|
if (CurRec->getValue(A)) {
|
||||||
|
TokError((Twine("left !foldl variable '") + A->getAsString() +
|
||||||
|
"' already defined")
|
||||||
|
.str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.Lex() != tgtok::comma) { // eat the id
|
||||||
|
TokError("expected ',' in !foldl");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.Lex() != tgtok::Id) { // eat the ','
|
||||||
|
TokError("fourth argument of !foldl must be an identifier");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Init *B = StringInit::get(Lex.getCurStrVal());
|
||||||
|
if (CurRec->getValue(B)) {
|
||||||
|
TokError((Twine("right !foldl variable '") + B->getAsString() +
|
||||||
|
"' already defined")
|
||||||
|
.str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.Lex() != tgtok::comma) { // eat the id
|
||||||
|
TokError("expected ',' in !foldl");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Lex.Lex(); // eat the ','
|
||||||
|
|
||||||
|
CurRec->addValue(RecordVal(A, Start->getType(), false));
|
||||||
|
CurRec->addValue(RecordVal(B, ListType->getElementType(), false));
|
||||||
|
Init *ExprUntyped = ParseValue(CurRec);
|
||||||
|
CurRec->removeValue(A);
|
||||||
|
CurRec->removeValue(B);
|
||||||
|
if (!ExprUntyped)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
TypedInit *Expr = dyn_cast<TypedInit>(ExprUntyped);
|
||||||
|
if (!Expr) {
|
||||||
|
TokError("could not get type of !foldl expression");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Expr->getType() != Start->getType()) {
|
||||||
|
TokError(Twine("!foldl expression must be of same type as start (") +
|
||||||
|
Start->getType()->getAsString() + "), but is of type " +
|
||||||
|
Expr->getType()->getAsString());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lex.getCode() != tgtok::r_paren) {
|
||||||
|
TokError("expected ')' in fold operator");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Lex.Lex(); // eat the ')'
|
||||||
|
|
||||||
|
return FoldOpInit::get(Start, List, A, B, Expr, Start->getType())
|
||||||
|
->Fold(CurRec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1590,6 +1707,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
|
|||||||
case tgtok::XListConcat:
|
case tgtok::XListConcat:
|
||||||
case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')'
|
case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')'
|
||||||
case tgtok::XIf:
|
case tgtok::XIf:
|
||||||
|
case tgtok::XFoldl:
|
||||||
case tgtok::XForEach:
|
case tgtok::XForEach:
|
||||||
case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')'
|
case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')'
|
||||||
return ParseOperation(CurRec, ItemType);
|
return ParseOperation(CurRec, ItemType);
|
||||||
@ -1697,7 +1815,7 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
|
|||||||
TypedInit *RHS = nullptr;
|
TypedInit *RHS = nullptr;
|
||||||
|
|
||||||
Lex.Lex(); // Eat the '#'.
|
Lex.Lex(); // Eat the '#'.
|
||||||
switch (Lex.getCode()) {
|
switch (Lex.getCode()) {
|
||||||
case tgtok::colon:
|
case tgtok::colon:
|
||||||
case tgtok::semi:
|
case tgtok::semi:
|
||||||
case tgtok::l_brace:
|
case tgtok::l_brace:
|
||||||
@ -2579,7 +2697,7 @@ Record *TGParser::InstantiateMulticlassDef(MultiClass &MC, Record *DefProto,
|
|||||||
// Ensure redefinition doesn't happen.
|
// Ensure redefinition doesn't happen.
|
||||||
if (Records.getDef(CurRec->getNameInitAsString())) {
|
if (Records.getDef(CurRec->getNameInitAsString())) {
|
||||||
Error(DefmPrefixRange.Start, "def '" + CurRec->getNameInitAsString() +
|
Error(DefmPrefixRange.Start, "def '" + CurRec->getNameInitAsString() +
|
||||||
"' already defined, instantiating defm with subdef '" +
|
"' already defined, instantiating defm with subdef '" +
|
||||||
DefProto->getNameInitAsString() + "'");
|
DefProto->getNameInitAsString() + "'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
71
llvm/test/TableGen/foldl.td
Normal file
71
llvm/test/TableGen/foldl.td
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// RUN: llvm-tblgen %s | FileCheck %s
|
||||||
|
// XFAIL: vg_leak
|
||||||
|
|
||||||
|
// CHECK: --- Defs ---
|
||||||
|
|
||||||
|
// CHECK: def A1 {
|
||||||
|
// CHECK: int ret = 0;
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def A2 {
|
||||||
|
// CHECK: int ret = 5;
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def A3 {
|
||||||
|
// CHECK: int ret = 10;
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def B1 {
|
||||||
|
// CHECK: list<string> ret = [];
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def B2 {
|
||||||
|
// CHECK: list<string> ret = [];
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def B3 {
|
||||||
|
// CHECK: list<string> ret = ["a"];
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def B4 {
|
||||||
|
// CHECK: list<string> ret = ["a", "b", "c", "d"];
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// CHECK: def E0 {
|
||||||
|
// CHECK: list<int> ret = [45, 45, 45, 45];
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
class Sum<list<int> lst> {
|
||||||
|
int ret = !foldl(0, lst, a, b, !add(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Flatten<list<list<string>> lst> {
|
||||||
|
list<string> ret = !foldl([]<string>, lst, a, b, !listconcat(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
def A1 : Sum<[]>;
|
||||||
|
def A2 : Sum<[5]>;
|
||||||
|
def A3 : Sum<[1, 2, 3, 4]>;
|
||||||
|
|
||||||
|
def B1 : Flatten<[]>;
|
||||||
|
def B2 : Flatten<[[]]>;
|
||||||
|
def B3 : Flatten<[["a"]]>;
|
||||||
|
def B4 : Flatten<[["a", "b"], [], ["c"], ["d"]]>;
|
||||||
|
|
||||||
|
// The variables a and b are declared both in the "inner" foldl and in the
|
||||||
|
// other foreach. The test checks that they don't "leak".
|
||||||
|
class C<list<int> lst> {
|
||||||
|
int ret = !foldl(0, lst, a, b, !add(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
class D<list<int> lst1, list<int> lst2> {
|
||||||
|
list<int> x = !foreach(a, lst1, C<lst2>.ret);
|
||||||
|
list<int> y = !foreach(b, lst1, C<lst2>.ret);
|
||||||
|
list<int> z = !listconcat(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
class E<list<int> lst2> {
|
||||||
|
list<int> ret = D<[0, 1], lst2>.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
def E0 : E<[10, 15, 20]>;
|
Loading…
x
Reference in New Issue
Block a user