Index: docs/TableGen/LangIntro.rst =================================================================== --- docs/TableGen/LangIntro.rst +++ docs/TableGen/LangIntro.rst @@ -258,6 +258,20 @@ ``!if(a,b,c)`` 'b' if the result of 'int' or 'bit' operator 'a' is nonzero, 'c' otherwise. +``!cond(condition_1 : val1, condition_2 : val2, ..., condition_n : valn)`` + Instead of embedding !if inside !if which can get cumbersome, + one can use !cond. !cond returns 'val1' if the result of 'int' or 'bit' + operator 'condition1' is nonzero. Otherwise, it checks 'condition2'. + If 'condition2' is nonzero, returns 'val2', and so on. + If all conditions are zero, it reports an error. + + Below is an example to convert an integer 'x' into a string: + + !cond(!lt(x,0) : "Negative", + !eq(x,0) : "Zero", + !eq(x,1) : "One, + 1 : "MoreThanOne") + ``!eq(a,b)`` 'bit 1' if string a is equal to string b, 0 otherwise. This only operates on string, int and bit objects. Use !cast to compare other types of Index: include/llvm/TableGen/Record.h =================================================================== --- include/llvm/TableGen/Record.h +++ include/llvm/TableGen/Record.h @@ -316,6 +316,7 @@ IK_TernOpInit, IK_UnOpInit, IK_LastOpInit, + IK_CondOpInit, IK_FoldOpInit, IK_IsAOpInit, IK_StringInit, @@ -912,6 +913,83 @@ std::string getAsString() const override; }; +/// !cond(condition_1: value1, ... , condition_n: value) +/// Selects the first value for which condition is true. +/// Otherwise reports an error. +class CondOpInit final : public TypedInit, public FoldingSetNode, + public TrailingObjects { + unsigned NumConds; + RecTy *ValType; + + CondOpInit(unsigned NC, RecTy *Type) + : TypedInit(IK_CondOpInit, Type), + NumConds(NC), ValType(Type) {} + + size_t numTrailingObjects(OverloadToken) const { + return 2*NumConds; + } + +public: + CondOpInit(const CondOpInit &) = delete; + CondOpInit &operator=(const CondOpInit &) = delete; + + static bool classof(const Init *I) { + return I->getKind() == IK_CondOpInit; + } + + static CondOpInit *get(ArrayRef C, ArrayRef V, + RecTy *Type); + + void Profile(FoldingSetNodeID &ID) const; + + RecTy *getValType() const { return ValType; } + + unsigned getNumConds() const { return NumConds; } + + Init *getCond(unsigned Num) const { + assert(Num < NumConds && "Condition number out of range!"); + return getTrailingObjects()[Num]; + } + + Init *getVal(unsigned Num) const { + assert(Num < NumConds && "Val number out of range!"); + return getTrailingObjects()[Num+NumConds]; + } + + ArrayRef getConds() const { + return makeArrayRef(getTrailingObjects(), NumConds); + } + + ArrayRef getVals() const { + return makeArrayRef(getTrailingObjects()+NumConds, NumConds); + } + + Init *Fold(Record *CurRec) const; + + Init *resolveReferences(Resolver &R) const override; + + bool isConcrete() const override; + bool isComplete() const override; + std::string getAsString() const override; + + using const_case_iterator = SmallVectorImpl::const_iterator; + using const_val_iterator = SmallVectorImpl::const_iterator; + + inline const_case_iterator arg_begin() const { return getConds().begin(); } + inline const_case_iterator arg_end () const { return getConds().end(); } + + inline size_t case_size () const { return NumConds; } + inline bool case_empty() const { return NumConds == 0; } + + inline const_val_iterator name_begin() const { return getVals().begin();} + inline const_val_iterator name_end () const { return getVals().end(); } + + inline size_t val_size () const { return NumConds; } + inline bool val_empty() const { return NumConds == 0; } + + Init *getBit(unsigned Bit) const override; +}; + /// !foldl (a, b, expr, start, lst) - Fold over a list. class FoldOpInit : public TypedInit, public FoldingSetNode { private: Index: lib/TableGen/Record.cpp =================================================================== --- lib/TableGen/Record.cpp +++ lib/TableGen/Record.cpp @@ -1694,6 +1694,137 @@ return const_cast(this); } +static void ProfileCondOpInit(FoldingSetNodeID &ID, + ArrayRef CondRange, + ArrayRef ValRange, + const RecTy *ValType) { + assert(CondRange.size() == ValRange.size() && + "Number of conditions and values must match!"); + ID.AddPointer(ValType); + ArrayRef::iterator Case = CondRange.begin(); + ArrayRef::iterator Val = ValRange.begin(); + + while (Case != CondRange.end()) { + ID.AddPointer(*Case++); + ID.AddPointer(*Val++); + } +} + +void CondOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileCondOpInit(ID, + makeArrayRef(getTrailingObjects(), NumConds), + makeArrayRef(getTrailingObjects() + NumConds, NumConds), + ValType); +} + +CondOpInit * +CondOpInit::get(ArrayRef CondRange, + ArrayRef ValRange, RecTy *Ty) { + assert(CondRange.size() == ValRange.size() && + "Number of conditions and values must match!"); + + static FoldingSet ThePool; + FoldingSetNodeID ID; + ProfileCondOpInit(ID, CondRange, ValRange, Ty); + + void *IP = nullptr; + if (CondOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = Allocator.Allocate(totalSizeToAlloc(2*CondRange.size()), + alignof(BitsInit)); + CondOpInit *I = new(Mem) CondOpInit(CondRange.size(), Ty); + + std::uninitialized_copy(CondRange.begin(), CondRange.end(), + I->getTrailingObjects()); + std::uninitialized_copy(ValRange.begin(), ValRange.end(), + I->getTrailingObjects()+CondRange.size()); + ThePool.InsertNode(I, IP); + return I; +} + +Init *CondOpInit::resolveReferences(Resolver &R) const { + SmallVector NewConds; + bool Changed = false; + for (const Init *Case : getConds()) { + Init *NewCase = Case->resolveReferences(R); + NewConds.push_back(NewCase); + Changed |= NewCase != Case; + } + + SmallVector NewVals; + for (const Init *Val : getVals()) { + Init *NewVal = Val->resolveReferences(R); + NewVals.push_back(NewVal); + Changed |= NewVal != Val; + } + + if (Changed) + return (CondOpInit::get(NewConds, NewVals, + getValType()))->Fold(R.getCurrentRecord()); + + return const_cast(this); +} + +Init *CondOpInit::Fold(Record *CurRec) const { + for ( unsigned i = 0; i < NumConds; ++i) { + Init *Cond = getCond(i); + Init *Val = getVal(i); + + if (IntInit *CondI = dyn_cast_or_null( + Cond->convertInitializerTo(IntRecTy::get()))) { + if (CondI->getValue()) + return Val->convertInitializerTo(getValType()); + } else + return const_cast(this); + } + + PrintFatalError(CurRec->getLoc(), + CurRec->getName() + + " does not have any true condition in:" + + this->getAsString()); + return nullptr; +} + +bool CondOpInit::isConcrete() const { + for (const Init *Case : getConds()) + if (!Case->isConcrete()) + return false; + + for (const Init *Val : getVals()) + if (!Val->isConcrete()) + return false; + + return true; +} + +bool CondOpInit::isComplete() const { + for (const Init *Case : getConds()) + if (!Case->isComplete()) + return false; + + for (const Init *Val : getVals()) + if (!Val->isConcrete()) + return false; + + return true; +} + +std::string CondOpInit::getAsString() const { + std::string Result = "!cond("; + for (unsigned i = 0; i < getNumConds(); i++) { + Result += getCond(i)->getAsString() + ": "; + Result += getVal(i)->getAsString(); + if (i != getNumConds()-1) + Result += ", "; + } + return Result + ")"; +} + +Init *CondOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast(this), Bit); +} + static void ProfileDagInit(FoldingSetNodeID &ID, Init *V, StringInit *VN, ArrayRef ArgRange, ArrayRef NameRange) { Index: lib/TableGen/TGLexer.h =================================================================== --- lib/TableGen/TGLexer.h +++ lib/TableGen/TGLexer.h @@ -51,7 +51,7 @@ // !keywords. XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast, - XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq, XIsA, XDag, + XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, // Integer value. Index: lib/TableGen/TGLexer.cpp =================================================================== --- lib/TableGen/TGLexer.cpp +++ lib/TableGen/TGLexer.cpp @@ -545,6 +545,7 @@ .Case("ge", tgtok::XGe) .Case("gt", tgtok::XGt) .Case("if", tgtok::XIf) + .Case("cond", tgtok::XCond) .Case("isa", tgtok::XIsA) .Case("head", tgtok::XHead) .Case("tail", tgtok::XTail) Index: lib/TableGen/TGParser.h =================================================================== --- lib/TableGen/TGParser.h +++ lib/TableGen/TGParser.h @@ -194,6 +194,7 @@ bool ParseRangePiece(SmallVectorImpl &Ranges); RecTy *ParseType(); Init *ParseOperation(Record *CurRec, RecTy *ItemType); + Init *ParseOperationCond(Record *CurRec, RecTy *ItemType); RecTy *ParseOperatorType(); Init *ParseObjectName(MultiClass *CurMultiClass); Record *ParseClassID(); Index: lib/TableGen/TGParser.cpp =================================================================== --- lib/TableGen/TGParser.cpp +++ lib/TableGen/TGParser.cpp @@ -1445,6 +1445,9 @@ return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); } + case tgtok::XCond: + return ParseOperationCond(CurRec, ItemType); + case tgtok::XFoldl: { // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' Lex.Lex(); // eat the operation @@ -1603,6 +1606,91 @@ return Type; } +Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) { + Lex.Lex(); // eat the operation 'cond' + + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + // Parse through '[Case: Val,]+' + SmallVector Case; + SmallVector Val; + while (true) { + if (Lex.getCode() == tgtok::r_paren) { + Lex.Lex(); // eat the ')' + break; + } + + Init *V = ParseValue(CurRec); + if (!V) + return nullptr; + Case.push_back(V); + + if (Lex.getCode() != tgtok::colon) { + TokError("expected ':' following a condition in !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the ':' + + V = ParseValue(CurRec, ItemType); + if (!V) + return nullptr; + Val.push_back(V); + + if (Lex.getCode() == tgtok::r_paren) { + Lex.Lex(); // eat the ')' + break; + } + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' or ')' following a value in !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + } + + if (Case.size() < 1) { + TokError("there should be at least 1 'condition : value' in the !cond operator"); + return nullptr; + } + + // resolve type + RecTy *Type = nullptr; + for (Init *V : Val) { + RecTy *VTy = nullptr; + if (TypedInit *Vt = dyn_cast(V)) + VTy = Vt->getType(); + if (BitsInit *Vbits = dyn_cast(V)) + VTy = BitsRecTy::get(Vbits->getNumBits()); + if (isa(V)) + VTy = BitRecTy::get(); + + if (Type == nullptr) { + if (!isa(V)) + Type = VTy; + } else { + if (!isa(V)) { + RecTy *RType = resolveTypes(Type, VTy); + if (!RType) { + TokError(Twine("inconsistent types '") + Type->getAsString() + + "' and '" + VTy->getAsString() + "' for !cond"); + return nullptr; + } + Type = RType; + } + } + } + + if (!Type) { + TokError("could not determine type for !cond from its arguments"); + return nullptr; + } + return CondOpInit::get(Case, Val, Type)->Fold(CurRec); +} + /// ParseSimpleValue - Parse a tblgen value. This returns null on error. /// /// SimpleValue ::= IDValue @@ -1621,6 +1709,7 @@ /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' /// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')' /// Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { @@ -1933,6 +2022,7 @@ case tgtok::XListConcat: case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: + case tgtok::XCond: case tgtok::XFoldl: case tgtok::XForEach: case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' Index: test/TableGen/cond-bitlist.td =================================================================== --- /dev/null +++ test/TableGen/cond-bitlist.td @@ -0,0 +1,27 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +class S { + bits<2> val = !cond(!eq(s, 8): {0, 0}, + !eq(s, 16): 0b01, + !eq(s, 32): 2, + !eq(s, 64): {1, 1}, + 1 : ?); +} + +def D8 : S<8>; +def D16 : S<16>; +def D32 : S<32>; +def D64 : S<64>; +def D128: S<128>; +// CHECK: def D128 +// CHECK-NEXT: bits<2> val = { ?, ? }; +// CHECK: def D16 +// CHECK-NEXT: bits<2> val = { 0, 1 }; +// CHECK: def D32 +// CHECK-NEXT: bits<2> val = { 1, 0 }; +// CHECK: def D64 +// CHECK-NEXT: bits<2> val = { 1, 1 }; +// CHECK: def D8 +// CHECK-NEXT: bits<2> val = { 0, 0 }; + Index: test/TableGen/cond-default.td =================================================================== --- /dev/null +++ test/TableGen/cond-default.td @@ -0,0 +1,11 @@ +// Check that not specifying a valid condition results in error + +// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +class C { + string s = !cond(!lt(x,0) : "negative", !gt(x,0) : "positive"); +} + +def Zero : C<0>; +//CHECK: error: Zero does not have any true condition in:!cond(0: "negative", 0: "positive") Index: test/TableGen/cond-empty-list-arg.td =================================================================== --- /dev/null +++ test/TableGen/cond-empty-list-arg.td @@ -0,0 +1,8 @@ +// RUN: llvm-tblgen %s +// XFAIL: vg_leak + +class C { + bit true = 1; + list X = !cond(cond: [1, 2, 3], true : []); + list Y = !cond(cond: [], true : [4, 5, 6]); +} Index: test/TableGen/cond-inheritance.td =================================================================== --- /dev/null +++ test/TableGen/cond-inheritance.td @@ -0,0 +1,22 @@ +// Make sure !cond gets propagated across multiple layers of inheritance. +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +class getInt { + int ret = !cond(c: 0, 1 : 1); +} + +class I1 { + int i = getInt.ret; +} + +class I2 : I1; + +def DI1: I1<1>; +// CHECK: def DI1 { // I1 +// CHECK-NEXT: int i = 0; + +// CHECK: def DI2 { // I1 I2 +// CHECK-NEXT: int i = 0; +def DI2: I2<1>; + Index: test/TableGen/cond-let.td =================================================================== --- /dev/null +++ test/TableGen/cond-let.td @@ -0,0 +1,36 @@ +// Check support for `!cond' operator as part of a `let' statement. +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + + +class C x, bits<4> y, bit z> { + bits<16> n; + + let n{11} = !cond(y{3}: 1, + y{2}: x{0}, + y{1}: x{1}, + y{0}: x{2}, + {1} :?); + let n{10-9}= !cond(x{2}: y{3-2}, + x{1}: y{2-1}, + x{1}: y{1-0}, + {1} : ?); + let n{8-6} = !cond(x{2}: 0b010, 1 : 0b110); + let n{5-4} = !cond(x{1}: y{3-2}, 1 : {0, 1}); + let n{3-0} = !cond(x{0}: y{3-0}, 1 : {z, y{2}, y{1}, y{0}}); +} + + +def C1 : C<{1, 0, 1}, {0, 1, 0, 1}, 0>; +def C2 : C<{0, 1, 0}, {1, 0, 1, 0}, 1>; +def C3 : C<{0, 0, 0}, {1, 0, 1, 0}, 0>; +def C4 : C<{0, 0, 0}, {0, 0, 0, 0}, 0>; + +// CHECK: def C1 +// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }; +// CHECK: def C2 +// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0 }; +// CHECK: def C3 +// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, ?, ?, 1, 1, 0, 0, 1, 0, 0, 1, 0 }; +// CHECK: def C4 +// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, ?, ?, ?, 1, 1, 0, 0, 1, 0, 0, 0, 0 }; Index: test/TableGen/cond-list.td =================================================================== --- /dev/null +++ test/TableGen/cond-list.td @@ -0,0 +1,38 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + + +class A> vals> { + list first = vals[0]; + list rest = !cond(!empty(!tail(vals)): vals[0], + 1 : vals[1]); +} + +def A_OneEl : A<[[1,2,3]]>; +// CHECK: def A_OneEl { // A +// CHECK-NEXT: list first = [1, 2, 3]; +// CHECK-NEXT: list rest = [1, 2, 3]; +// CHECK-NEXT: } + +def A_TwoEl : A<[[1,2,3], [4,5,6]]>; +// CHECK: def A_TwoEl { // A +// CHECK-NEXT: list first = [1, 2, 3]; +// CHECK-NEXT: list rest = [4, 5, 6]; +// CHECK-NEXT: } + + +class B v> { + list vals = v; +} +class BB> vals> : B; +class BBB> vals> : BB; + +def B_OneEl : BBB<[[1,2,3]]>; +// CHECK: def B_OneEl { // B BB BBB +// CHECK-NEXT: list vals = [1, 2, 3]; +// CHECK-NEXT: } + +def B_TwoEl : BBB<[[1,2,3],[4,5,6]]>; +// CHECK: def B_TwoEl { // B BB BBB +// CHECK-NEXT: list vals = [4, 5, 6]; +// CHECK-NEXT: } Index: test/TableGen/cond-subclass.td =================================================================== --- /dev/null +++ test/TableGen/cond-subclass.td @@ -0,0 +1,27 @@ +// Check that !cond with operands of different subtypes can +// initialize a supertype variable. +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +class E {} +class E1 : E {} +class E2 : E {} + +class EX { + E x = !cond(cc: b, 1 : c); +} + +def E1d : E1<0>; +def E2d : E2<0>; + +def EXd1 : EX<1, E1d, E2d>; +def EXd2 : EX<0, E1d, E2d>; + +// CHECK: def EXd1 { +// CHECK: E x = E1d; +// CHECK: } +// +// CHECK: def EXd2 { +// CHECK: E x = E2d; +// CHECK: } + Index: test/TableGen/cond-type.td =================================================================== --- /dev/null +++ test/TableGen/cond-type.td @@ -0,0 +1,11 @@ +// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +class A {} +class B : A {} +class C : A {} + +// CHECK: Value 'x' of type 'C' is incompatible with initializer '{{.*}}' of type 'A' +class X { + C x = !cond(cc: b, 1 : c); +} Index: test/TableGen/cond-usage.td =================================================================== --- /dev/null +++ test/TableGen/cond-usage.td @@ -0,0 +1,29 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +// Check that !cond picks the first true value +// CHECK: class A +// CHECK-NEXT: string S = !cond(!eq(A:x, 10): "ten", !eq(A:x, 11): "eleven", !eq(A:x, 10): "TEN", !gt(A:x, 9): "MoreThanNine", 1: "unknown"); +// CHECK: B1 +// CHECK-NEXT: string S = "unknown" +// CHECK: B10 +// CHECK-NEXT: string S = "ten"; +// CHECK: def B11 +// CHECK-NEXT: string S = "eleven"; +// CHECK: def B12 +// CHECK-NEXT: string S = "MoreThanNine"; +// CHECK: def B9 +// CHECK-NEXT: string S = "unknown" + +class A { + string S = !cond(!eq(x,10) : "ten", + !eq(x,11) : "eleven", + !eq(x,10) : "TEN", + !gt(x,9) : "MoreThanNine", + !eq(1,1) : "unknown"); +} +def B1 : A<1>; +def B9 : A<9>; +def B10 : A<10>; +def B11 : A<11>; +def B12 : A<12>; Index: test/TableGen/condsbit.td =================================================================== --- /dev/null +++ test/TableGen/condsbit.td @@ -0,0 +1,15 @@ +// check that !cond works well with bit conditional values +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak +// CHECK: a = 6 +// CHECK: a = 5 + +class A { + bit true = 1; + int a = !cond(b: 5, true : 6); + bit c = !cond(b: 0, true : 1); + bits<1> d = !cond(b: 0, true : 1); +} + +def X : A<0>; +def Y : A;