diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -223,10 +223,10 @@ : !div !empty !eq !filter !find : !foldl !foreach !ge !getdagop !gt : !head !if !interleave !isa !le - : !listconcat !listsplat !logtwo !lt !mul - : !ne !not !or !setdagop !shl - : !size !sra !srl !strconcat !sub - : !subst !substr !tail !xor + : !listconcat !listremove !listsplat !logtwo !lt + : !mul !ne !not !or !setdagop + : !shl !size !sra !srl !strconcat + : !sub !subst !substr !tail !xor The ``!cond`` operator has a slightly different syntax compared to other bang operators, so it is defined separately: @@ -1740,6 +1740,10 @@ This operator concatenates the list arguments *list1*, *list2*, etc., and produces the resulting list. The lists must have the same element type. +``!listremove(``\ *list1*\ ``,`` *list2*\ ``)`` + This operator returns a copy of *list1* removing all elements that also occur in + *list2*. The lists must have the same element type. + ``!listsplat(``\ *value*\ ``,`` *count*\ ``)`` This operator produces a list of length *count* whose elements are all equal to the *value*. For example, ``!listsplat(42, 3)`` results in diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -847,6 +847,7 @@ SRL, LISTCONCAT, LISTSPLAT, + LISTREMOVE, STRCONCAT, INTERLEAVE, CONCAT, @@ -900,6 +901,8 @@ Init *getLHS() const { return LHS; } Init *getRHS() const { return RHS; } + std::optional CompareInit(unsigned Opc, Init *LHS, Init *RHS) const; + // Fold - If possible, fold this to a simpler init. Return this if not // possible to fold. Init *Fold(Record *CurRec) const; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -1037,7 +1037,83 @@ return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType()); } -Init *BinOpInit::Fold(Record *CurRec) const { +std::optional BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) const { + // First see if we have two bit, bits, or int. + IntInit *LHSi = dyn_cast_or_null( + LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + IntInit *RHSi = dyn_cast_or_null( + RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + + if (LHSi && RHSi) { + bool Result; + switch (Opc) { + case EQ: + Result = LHSi->getValue() == RHSi->getValue(); + break; + case NE: + Result = LHSi->getValue() != RHSi->getValue(); + break; + case LE: + Result = LHSi->getValue() <= RHSi->getValue(); + break; + case LT: + Result = LHSi->getValue() < RHSi->getValue(); + break; + case GE: + Result = LHSi->getValue() >= RHSi->getValue(); + break; + case GT: + Result = LHSi->getValue() > RHSi->getValue(); + break; + default: + llvm_unreachable("unhandled comparison"); + } + return Result; + } + + // Next try strings. + StringInit *LHSs = dyn_cast(LHS); + StringInit *RHSs = dyn_cast(RHS); + + if (LHSs && RHSs) { + bool Result; + switch (Opc) { + case EQ: + Result = LHSs->getValue() == RHSs->getValue(); + break; + case NE: + Result = LHSs->getValue() != RHSs->getValue(); + break; + case LE: + Result = LHSs->getValue() <= RHSs->getValue(); + break; + case LT: + Result = LHSs->getValue() < RHSs->getValue(); + break; + case GE: + Result = LHSs->getValue() >= RHSs->getValue(); + break; + case GT: + Result = LHSs->getValue() > RHSs->getValue(); + break; + default: + llvm_unreachable("unhandled comparison"); + } + return Result; + } + + // Finally, !eq and !ne can be used with records. + if (Opc == EQ || Opc == NE) { + DefInit *LHSd = dyn_cast(LHS); + DefInit *RHSd = dyn_cast(RHS); + if (LHSd && RHSd) + return (Opc == EQ) ? LHSd == RHSd : LHSd != RHSd; + } + + return std::nullopt; +} + + Init *BinOpInit::Fold(Record *CurRec) const { switch (getOpcode()) { case CONCAT: { DagInit *LHSs = dyn_cast(LHS); @@ -1091,6 +1167,28 @@ } break; } + case LISTREMOVE: { + ListInit *LHSs = dyn_cast(LHS); + ListInit *RHSs = dyn_cast(RHS); + if (LHSs && RHSs) { + SmallVector Args; + for (Init *EltLHS : *LHSs) { + bool Found = false; + for (Init *EltRHS : *RHSs) { + if (std::optional Result = CompareInit(EQ, EltLHS, EltRHS)) { + if (*Result) { + Found = true; + break; + } + } + } + if (!Found) + Args.push_back(EltLHS); + } + return ListInit::get(Args, LHSs->getElementType()); + } + break; + } case STRCONCAT: { StringInit *LHSs = dyn_cast(LHS); StringInit *RHSs = dyn_cast(RHS); @@ -1118,53 +1216,8 @@ case LT: case GE: case GT: { - // First see if we have two bit, bits, or int. - IntInit *LHSi = dyn_cast_or_null( - LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); - IntInit *RHSi = dyn_cast_or_null( - RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); - - if (LHSi && RHSi) { - bool Result; - switch (getOpcode()) { - case EQ: Result = LHSi->getValue() == RHSi->getValue(); break; - case NE: Result = LHSi->getValue() != RHSi->getValue(); break; - case LE: Result = LHSi->getValue() <= RHSi->getValue(); break; - case LT: Result = LHSi->getValue() < RHSi->getValue(); break; - case GE: Result = LHSi->getValue() >= RHSi->getValue(); break; - case GT: Result = LHSi->getValue() > RHSi->getValue(); break; - default: llvm_unreachable("unhandled comparison"); - } - return BitInit::get(getRecordKeeper(), Result); - } - - // Next try strings. - StringInit *LHSs = dyn_cast(LHS); - StringInit *RHSs = dyn_cast(RHS); - - if (LHSs && RHSs) { - bool Result; - switch (getOpcode()) { - case EQ: Result = LHSs->getValue() == RHSs->getValue(); break; - case NE: Result = LHSs->getValue() != RHSs->getValue(); break; - case LE: Result = LHSs->getValue() <= RHSs->getValue(); break; - case LT: Result = LHSs->getValue() < RHSs->getValue(); break; - case GE: Result = LHSs->getValue() >= RHSs->getValue(); break; - case GT: Result = LHSs->getValue() > RHSs->getValue(); break; - default: llvm_unreachable("unhandled comparison"); - } - return BitInit::get(getRecordKeeper(), Result); - } - - // Finally, !eq and !ne can be used with records. - if (getOpcode() == EQ || getOpcode() == NE) { - DefInit *LHSd = dyn_cast(LHS); - DefInit *RHSd = dyn_cast(RHS); - if (LHSd && RHSd) - return BitInit::get(getRecordKeeper(), - (getOpcode() == EQ) ? LHSd == RHSd : LHSd != RHSd); - } - + if (std::optional Result = CompareInit(getOpcode(), LHS, RHS)) + return BitInit::get(getRecordKeeper(), *Result); break; } case SETDAGOP: { @@ -1260,6 +1313,7 @@ case GT: Result = "!gt"; break; case LISTCONCAT: Result = "!listconcat"; break; case LISTSPLAT: Result = "!listsplat"; break; + case LISTREMOVE: Result = "!listremove"; break; case STRCONCAT: Result = "!strconcat"; break; case INTERLEAVE: Result = "!interleave"; break; case SETDAGOP: Result = "!setdagop"; break; diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -56,7 +56,7 @@ XSHL, XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, XCast, XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, - XExists, + XExists, XListRemove, // Boolean literals. TrueVal, FalseVal, diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -584,6 +584,7 @@ .Case("filter", tgtok::XFilter) .Case("listconcat", tgtok::XListConcat) .Case("listsplat", tgtok::XListSplat) + .Case("listremove", tgtok::XListRemove) .Case("strconcat", tgtok::XStrConcat) .Case("interleave", tgtok::XInterleave) .Case("substr", tgtok::XSubstr) diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1179,6 +1179,7 @@ case tgtok::XGt: case tgtok::XListConcat: case tgtok::XListSplat: + case tgtok::XListRemove: case tgtok::XStrConcat: case tgtok::XInterleave: case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')' @@ -1208,6 +1209,7 @@ case tgtok::XGt: Code = BinOpInit::GT; break; case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break; case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break; + case tgtok::XListRemove: Code = BinOpInit::LISTREMOVE; break; case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break; case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break; case tgtok::XSetDagOp: Code = BinOpInit::SETDAGOP; break; @@ -1246,12 +1248,16 @@ // ArgType for the comparison operators is not yet known. break; case tgtok::XListConcat: - // We don't know the list type until we parse the first argument + // We don't know the list type until we parse the first argument. ArgType = ItemType; break; case tgtok::XListSplat: // Can't do any typechecking until we parse the first argument. break; + case tgtok::XListRemove: + // We don't know the list type until we parse the first argument. + ArgType = ItemType; + break; case tgtok::XStrConcat: Type = StringRecTy::get(Records); ArgType = StringRecTy::get(Records); @@ -1329,6 +1335,13 @@ } ArgType = nullptr; // Broken invariant: types not identical. break; + case BinOpInit::LISTREMOVE: + if (!isa(ArgType)) { + Error(InitLoc, Twine("expected a list, got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + break; case BinOpInit::EQ: case BinOpInit::NE: if (!ArgType->typeIsConvertibleTo(IntRecTy::get(Records)) && @@ -1423,6 +1436,9 @@ // listsplat returns a list of type of the *first* argument. if (Code == BinOpInit::LISTSPLAT) Type = cast(InitList.front())->getType()->getListTy(); + // listremove returns a list with type of the argument. + if (Code == BinOpInit::LISTREMOVE) + Type = ArgType; // We allow multiple operands to associative operators like !strconcat as // shorthand for nesting them. @@ -2154,6 +2170,7 @@ /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' /// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= LISTSPLATTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTREMOVETOK '(' Value ',' Value ')' /// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= COND '(' [Value ':' Value,]+ ')' /// @@ -2453,6 +2470,7 @@ case tgtok::XGt: case tgtok::XListConcat: case tgtok::XListSplat: + case tgtok::XListRemove: case tgtok::XStrConcat: case tgtok::XInterleave: case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')' diff --git a/llvm/test/TableGen/listremove.td b/llvm/test/TableGen/listremove.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/listremove.td @@ -0,0 +1,17 @@ +// RUN: llvm-tblgen %s | FileCheck %s + +// CHECK: class X { +// CHECK: list T0 = ["foo", "bar"]; +// CHECK: list T1 = ["foo", "bar"]; +// CHECK: list T2 = ["bar"]; +// CHECK: list T3 = ["foo"]; +// CHECK: list T4 = []; +// CHECK: } + +class X { + list T0 = !listremove(["foo", "bar"], []); + list T1 = !listremove(["foo", "bar"], ["baz"]); + list T2 = !listremove(["foo", "bar"], ["foo"]); + list T3 = !listremove(["foo", "bar"], ["bar", "bar"]); + list T4 = !listremove(["foo", "bar"], ["bar", "foo"]); +} diff --git a/llvm/utils/kate/llvm-tablegen.xml b/llvm/utils/kate/llvm-tablegen.xml --- a/llvm/utils/kate/llvm-tablegen.xml +++ b/llvm/utils/kate/llvm-tablegen.xml @@ -31,6 +31,7 @@ !strconcat !cast !listconcat + !listreplace !listsplat !size !foldl