Index: llvm/docs/TableGen/ProgRef.rst =================================================================== --- llvm/docs/TableGen/ProgRef.rst +++ llvm/docs/TableGen/ProgRef.rst @@ -218,13 +218,13 @@ .. productionlist:: BangOperator: one of : !add !and !cast !con !dag - : !empty !eq !foldl !foreach !filter - : !ge !getdagop !gt !head !if - : !interleave !isa !le !listconcat !listsplat - : !lt !mul !ne !not !or - : !setdagop !shl !size !sra !srl - : !strconcat !sub !subst !substr !tail - : !xor + : !empty !eq !filter !find !foldl + : !foreach !ge !getdagop !gt !head + : !if !interleave !isa !le !listconcat + : !listsplat !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: @@ -1639,6 +1639,12 @@ if the value is 0, the element is not included in the new list. If the value is anything else, the element is included. +``!find(``\ *string1*\ ``,`` *string2*\ [``,`` *start*]\ ``)`` + This operator searches for *string2* in *string1* and produces its + position. The starting position of the search may be specified by *start*, + which can range between 0 and the length of *string1*; the default is 0. + If the string is not found, the result is -1. + ``!foldl(``\ *init*\ ``,`` *list*\ ``,`` *acc*\ ``,`` *var*\ ``,`` *expr*\ ``)`` This operator performs a left-fold over the items in *list*. The variable *acc* acts as the accumulator and is initialized to *init*. Index: llvm/include/llvm/TableGen/Record.h =================================================================== --- llvm/include/llvm/TableGen/Record.h +++ llvm/include/llvm/TableGen/Record.h @@ -862,7 +862,7 @@ /// !op (X, Y, Z) - Combine two inits. class TernOpInit : public OpInit, public FoldingSetNode { public: - enum TernaryOp : uint8_t { SUBST, FOREACH, FILTER, IF, DAG, SUBSTR }; + enum TernaryOp : uint8_t { SUBST, FOREACH, FILTER, IF, DAG, SUBSTR, FIND }; private: Init *LHS, *MHS, *RHS; Index: llvm/lib/TableGen/Record.cpp =================================================================== --- llvm/lib/TableGen/Record.cpp +++ llvm/lib/TableGen/Record.cpp @@ -1398,6 +1398,26 @@ } break; } + + case FIND: { + StringInit *LHSs = dyn_cast(LHS); + StringInit *MHSs = dyn_cast(MHS); + IntInit *RHSi = dyn_cast(RHS); + if (LHSs && MHSs && RHSi) { + int64_t SourceSize = LHSs->getValue().size(); + int64_t Start = RHSi->getValue(); + if (Start < 0 || Start > SourceSize) + PrintError(CurRec->getLoc(), + Twine("!find start position is out of range 0...") + + std::to_string(SourceSize) + ": " + + std::to_string(Start)); + auto I = LHSs->getValue().find(MHSs->getValue(), Start); + if (I == std::string::npos) + return IntInit::get(-1); + return IntInit::get(I); + } + break; + } } return const_cast(this); @@ -1443,6 +1463,7 @@ case IF: Result = "!if"; break; case SUBST: Result = "!subst"; break; case SUBSTR: Result = "!substr"; break; + case FIND: Result = "!find"; break; } return (Result + "(" + (UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) + Index: llvm/lib/TableGen/TGLexer.h =================================================================== --- llvm/lib/TableGen/TGLexer.h +++ llvm/lib/TableGen/TGLexer.h @@ -53,7 +53,7 @@ // Bang operators. XConcat, XADD, XSUB, XMUL, XNOT, XAND, XOR, XXOR, XSRA, XSRL, XSHL, - XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XCast, + 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, Index: llvm/lib/TableGen/TGLexer.cpp =================================================================== --- llvm/lib/TableGen/TGLexer.cpp +++ llvm/lib/TableGen/TGLexer.cpp @@ -591,6 +591,7 @@ .Case("strconcat", tgtok::XStrConcat) .Case("interleave", tgtok::XInterleave) .Case("substr", tgtok::XSubstr) + .Case("find", tgtok::XFind) .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. .Default(tgtok::Error); Index: llvm/lib/TableGen/TGParser.h =================================================================== --- llvm/lib/TableGen/TGParser.h +++ llvm/lib/TableGen/TGParser.h @@ -262,6 +262,7 @@ RecTy *ParseType(); Init *ParseOperation(Record *CurRec, RecTy *ItemType); Init *ParseOperationSubstr(Record *CurRec, RecTy *ItemType); + Init *ParseOperationFind(Record *CurRec, RecTy *ItemType); Init *ParseOperationForEachFilter(Record *CurRec, RecTy *ItemType); Init *ParseOperationCond(Record *CurRec, RecTy *ItemType); RecTy *ParseOperatorType(); Index: llvm/lib/TableGen/TGParser.cpp =================================================================== --- llvm/lib/TableGen/TGParser.cpp +++ llvm/lib/TableGen/TGParser.cpp @@ -1508,6 +1508,9 @@ case tgtok::XSubstr: return ParseOperationSubstr(CurRec, ItemType); + case tgtok::XFind: + return ParseOperationFind(CurRec, ItemType); + case tgtok::XCond: return ParseOperationCond(CurRec, ItemType); @@ -1756,6 +1759,94 @@ return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); } +/// Parse the !find operation. Return null on error. +/// +/// Substr ::= !find(string, string [, start-int]) => int +Init *TGParser::ParseOperationFind(Record *CurRec, RecTy *ItemType) { + TernOpInit::TernaryOp Code = TernOpInit::FIND; + RecTy *Type = IntRecTy::get(); + + Lex.Lex(); // eat the operation + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after !find operator"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) + return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !find operator"); + return nullptr; + } + + SMLoc MHSLoc = Lex.getLoc(); + Init *MHS = ParseValue(CurRec); + if (!MHS) + return nullptr; + + SMLoc RHSLoc = Lex.getLoc(); + Init *RHS; + if (consume(tgtok::comma)) { + RHSLoc = Lex.getLoc(); + RHS = ParseValue(CurRec); + if (!RHS) + return nullptr; + } else { + RHS = IntInit::get(0); + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !find operator"); + return nullptr; + } + + if (ItemType && !Type->typeIsConvertibleTo(ItemType)) { + Error(RHSLoc, Twine("expected value of type '") + + ItemType->getAsString() + "', got '" + + Type->getAsString() + "'"); + } + + TypedInit *LHSt = dyn_cast(LHS); + if (!LHSt && !isa(LHS)) { + TokError("could not determine type of the source string in !find"); + return nullptr; + } + if (LHSt && !isa(LHSt->getType())) { + TokError(Twine("expected string, got type '") + + LHSt->getType()->getAsString() + "'"); + return nullptr; + } + + TypedInit *MHSt = dyn_cast(MHS); + if (!MHSt && !isa(MHS)) { + TokError("could not determine type of the target string in !find"); + return nullptr; + } + if (MHSt && !isa(MHSt->getType())) { + Error(MHSLoc, Twine("expected string, got type '") + + MHSt->getType()->getAsString() + "'"); + return nullptr; + } + + if (RHS) { + TypedInit *RHSt = dyn_cast(RHS); + if (!RHSt && !isa(RHS)) { + TokError("could not determine type of the start position in !find"); + return nullptr; + } + if (RHSt && !isa(RHSt->getType())) { + TokError(Twine("expected int, got type '") + + RHSt->getType()->getAsString() + "'"); + return nullptr; + } + } + + return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); +} + /// Parse the !foreach and !filter operations. Return null on error. /// /// ForEach ::= !foreach(ID, list-or-dag, expr) => list @@ -2282,7 +2373,8 @@ case tgtok::XForEach: case tgtok::XFilter: case tgtok::XSubst: - case tgtok::XSubstr: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + case tgtok::XSubstr: + case tgtok::XFind: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' return ParseOperation(CurRec, ItemType); } } Index: llvm/test/TableGen/find.td =================================================================== --- /dev/null +++ llvm/test/TableGen/find.td @@ -0,0 +1,64 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s + +// This file contains tests for the !find bang operator. + +defvar Sentence = "This is the end of the world."; + +// CHECK: def Rec01 +// CHECK-NEXT: int FirstThe = 8 +// CHECK-NEXT: int SameThe = 8 +// CHECK-NEXT: int SecondThe = 19 +// CHECK-NEXT: int ThirdThe = -1 + +def Rec01 { + int FirstThe = !find(Sentence, "the"); + int SameThe = !find(Sentence, "the", FirstThe); + int SecondThe = !find(Sentence, "the", !add(FirstThe, 1)); + int ThirdThe = !find(Sentence, "the", !add(SecondThe, 1)); +} + +class C1 { + string Name = name; + bit IsJr = !ne(!find(name, "Jr"), -1); +} + +// CHECK: def Rec02 +// CHECK-NEXT: string Name = "Sally Smith" +// CHECK-NEXT: bit IsJr = 0 +// CHECK: def Rec03 +// CHECK-NEXT: string Name = "Fred Jones, Jr." +// CHECK-NEXT: bit IsJr = 1 + +def Rec02 : C1<"Sally Smith">; +def Rec03 : C1<"Fred Jones, Jr.">; + +// CHECK: def Rec04 +// CHECK-NEXT: int ThisPos = 0 +// CHECK-NEXT: int WorldPos = 23 +// CHECK-NEXT: int TooLong = -1 + +def Rec04 { + int ThisPos = !find(Sentence, "This"); + int WorldPos = !find(Sentence, "world."); + int TooLong = !find(Sentence, "world.country"); +} + +// CHECK: def Rec05 +// CHECK-NEXT: string Name = "Pat Snork" +// CHECK-NEXT: bit IsJr = 0 +// CHECK-NEXT: bit JrOrSnork = 1 + +def Rec05 : C1<"Pat Snork"> { + bit JrOrSnork = !or(IsJr, !ne(!find(Name, "Snork"), -1)); +} + +#ifdef ERROR1 + +// ERROR1: !find start position is out of range 0...29: 100 + +def Rec06 { + int Test1 = !find(Sentence, "the", 100); +} +#endif +