Index: docs/TableGen/LangIntro.rst =================================================================== --- docs/TableGen/LangIntro.rst +++ docs/TableGen/LangIntro.rst @@ -258,6 +258,22 @@ ``!if(a,b,c)`` 'b' if the result of 'int' or 'bit' operator 'a' is nonzero, 'c' otherwise. +``!ifs(condition1 : val1, condition2 : val2, ..., default : valn)`` + Instead of embedding !if inside !if which can get cumbersome, + one can use !ifs. !ifs 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, the default value 'valn' is returned. + + The default value can appear anywhere, but there must be + one and only one default value. + Below is an example to convert an integer 'x' into a string: + + !ifs(!lt(x,0) : "Negative", + !eq(x,0) : "zero", + !eq(x,1) : "one, + default : "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_IfsOpInit, IK_FoldOpInit, IK_IsAOpInit, IK_StringInit, @@ -912,6 +913,85 @@ std::string getAsString() const override; }; +/// !ifs(condition1: value1, condition2: value2, ... ,default: value) +/// Selects the first value for which corresponding condition is true. +/// Otherwise returns the default value +class IfsOpInit final : public TypedInit, public FoldingSetNode, + public TrailingObjects { + friend TrailingObjects; + unsigned NumCases; + Init *Default; + RecTy *RetType; + + IfsOpInit(unsigned NC, Init *D, RecTy *Type) + : TypedInit(IK_IfsOpInit, Type), + NumCases(NC), Default(D), RetType(Type) {} + + size_t numTrailingObjects(OverloadToken) const { + return 2*NumCases; + } + +public: + IfsOpInit(const IfsOpInit &) = delete; + IfsOpInit &operator=(const IfsOpInit &) = delete; + + static bool classof(const Init *I) { + return I->getKind() == IK_IfsOpInit; + } + + static IfsOpInit *get(ArrayRef C, ArrayRef V, + Init *D, RecTy *Type); + + void Profile(FoldingSetNodeID &ID) const; + + Init *getDefault() const { return Default; } + RecTy *getReturnType() const { return RetType; } + + unsigned getNumCases() const { return NumCases; } + + Init *getCase(unsigned Num) const { + assert(Num < NumCases && "Case number out of range!"); + return getTrailingObjects()[Num]; + } + + Init *getVal(unsigned Num) const { + assert(Num < NumCases && "Val number out of range!"); + return getTrailingObjects()[Num+NumCases]; + } + + ArrayRef getCases() const { + return makeArrayRef(getTrailingObjects(), NumCases); + } + + ArrayRef getVals() const { + return makeArrayRef(getTrailingObjects()+NumCases, NumCases); + } + + Init *Fold(Record *CurRec) const; + + Init *resolveReferences(Resolver &R) const override; + + bool isConcrete() 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 getCases().begin(); } + inline const_case_iterator arg_end () const { return getCases().end(); } + + inline size_t case_size () const { return NumCases; } + inline bool case_empty() const { return NumCases == 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 NumCases; } + inline bool val_empty() const { return NumCases == 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,121 @@ return const_cast(this); } +static void ProfileIfsOpInit(FoldingSetNodeID &ID, + ArrayRef CaseRange, + ArrayRef ValRange, + const Init *Default, const RecTy *RetType) { + assert(CaseRange.size() == ValRange.size() && + "Number of conditions and values must match (excl. default)!"); + ID.AddPointer(Default); + ID.AddPointer(RetType); + ArrayRef::iterator Case = CaseRange.begin(); + ArrayRef::iterator Val = ValRange.begin(); + + while (Case != CaseRange.end()) { + ID.AddPointer(*Case++); + ID.AddPointer(*Val++); + } +} + +void IfsOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileIfsOpInit(ID, + makeArrayRef(getTrailingObjects(), NumCases), + makeArrayRef(getTrailingObjects() + NumCases, NumCases), + Default, RetType); +} + +IfsOpInit * +IfsOpInit::get(ArrayRef CaseRange, ArrayRef ValRange, + Init *D, RecTy *Ty) { + assert(CaseRange.size() == ValRange.size() && + "Number of conditions and values must match (excl. default)!"); + + static FoldingSet ThePool; + FoldingSetNodeID ID; + ProfileIfsOpInit(ID, CaseRange, ValRange, D, Ty); + + void *IP = nullptr; + if (IfsOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = Allocator.Allocate(totalSizeToAlloc(2*CaseRange.size()), + alignof(BitsInit)); + IfsOpInit *I = new(Mem) IfsOpInit(CaseRange.size(), D, Ty); + + std::uninitialized_copy(CaseRange.begin(), CaseRange.end(), + I->getTrailingObjects()); + std::uninitialized_copy(ValRange.begin(), ValRange.end(), + I->getTrailingObjects()+CaseRange.size()); + ThePool.InsertNode(I, IP); + return I; +} + +Init *IfsOpInit::resolveReferences(Resolver &R) const { + SmallVector NewCases; + bool Changed = false; + for (const Init *Case : getCases()) { + Init *NewCase = Case->resolveReferences(R); + NewCases.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; + } + + Init *NewD = Default->resolveReferences(R); + if (NewD != Default || Changed) + return (IfsOpInit::get(NewCases, NewVals, NewD, + getReturnType()))->Fold(R.getCurrentRecord()); + + return const_cast(this); +} + +Init *IfsOpInit::Fold(Record *CurRec) const { + for ( unsigned i = 0; i < NumCases; ++i) { + Init *LHS = getCase(i); + Init *RHS = getVal(i); + if (IntInit *LHSi = dyn_cast_or_null( + LHS->convertInitializerTo(IntRecTy::get()))) { + if (LHSi->getValue()) + return RHS; + } else + return const_cast(this); + } + return Default; +} + +bool IfsOpInit::isConcrete() const { + if (!Default->isConcrete()) + return false; + for (const Init *Case : getCases()) { + if (!Case->isConcrete()) + return false; + } + for (const Init *Val : getVals()) { + if (!Val->isConcrete()) + return false; + } + return true; +} + +std::string IfsOpInit::getAsString() const { + std::string Result = "!ifs("; + for (unsigned i = 0; i < getNumCases(); i++) { + Result += getCase(i)->getAsString() + ": "; + Result += getVal(i)->getAsString() + ", "; + } + return Result + "default: " + Default->getAsString() + ")"; +} + +Init *IfsOpInit::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 @@ -47,11 +47,11 @@ // Keywords. Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List, - MultiClass, String, Defset, + MultiClass, String, Defset, Default, // !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, XIfs, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, // Integer value. Index: lib/TableGen/TGLexer.cpp =================================================================== --- lib/TableGen/TGLexer.cpp +++ lib/TableGen/TGLexer.cpp @@ -350,6 +350,7 @@ .Case("field", tgtok::Field) .Case("let", tgtok::Let) .Case("in", tgtok::In) + .Case("default", tgtok::Default) .Default(tgtok::Id); if (Kind == tgtok::Id) @@ -545,6 +546,7 @@ .Case("ge", tgtok::XGe) .Case("gt", tgtok::XGt) .Case("if", tgtok::XIf) + .Case("ifs", tgtok::XIfs) .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 *ParseOperationIfs(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::XIfs: + return ParseOperationIfs(CurRec, ItemType); + case tgtok::XFoldl: { // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' Lex.Lex(); // eat the operation @@ -1603,6 +1606,140 @@ return Type; } +Init *TGParser::ParseOperationIfs(Record *CurRec, RecTy *ItemType) { + Lex.Lex(); // eat the operation 'switch' + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !ifs operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + // Parse through odd number of Vals: '[[Case: Val,]+ default: Val)' + SmallVector Case; + SmallVector Val; + Init *Default = nullptr; + while (true) { + if (Lex.getCode() == tgtok::r_paren) { + Lex.Lex(); // eat the ')' + break; + } + if (Lex.getCode() == tgtok::comma) { + Lex.Lex(); // eat the possibly spurious comma or one following default + continue; + } + + if (Lex.getCode() == tgtok::Default) { + if (Default) { + TokError("multiple defaults defined in !switch"); + return nullptr; + } + Lex.Lex(); // eat the 'default' + + if (Lex.getCode() != tgtok::colon) { + TokError("expected ':' after 'default' in !switch operator"); + return nullptr; + } + Lex.Lex(); // eat the ':' + Default = ParseValue(CurRec, ItemType); + if (!Default) + return nullptr; + continue; + } + + Init *V = ParseValue(CurRec); + if (!V) + return nullptr; + Case.push_back(V); + + if (Lex.getCode() != tgtok::colon) { + TokError("expected ':' following a condition in !switch 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 !ifs operator"); + return nullptr; + } + } + + if (Case.size() < 1) { + TokError("there should be at least 1 'condition : value' to the !switch operator"); + return nullptr; + } + if (!Default){ + TokError("no default defined for the !switch 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 !ifs"); + return nullptr; + } + Type = RType; + } + } + } + + RecTy *DTy = nullptr; + if (TypedInit *Dt = dyn_cast(Default)) + DTy = Dt->getType(); + if (BitsInit *Dbits = dyn_cast(Default)) + DTy = BitsRecTy::get(Dbits->getNumBits()); + if (isa(Default)) + DTy = BitRecTy::get(); + + if (Type == nullptr) { + if (!isa(Default)) + Type = DTy; + } else { + if (!isa(Default)) { + RecTy *RType = resolveTypes(Type, DTy); + if (!RType) { + TokError(Twine("inconsistent types '") + Type->getAsString() + + "' and '" + DTy->getAsString() + "' for !ifs"); + return nullptr; + } + Type = RType; + } + } + + if (!Type) { + TokError("could not get type for !switch from its arguments"); + return nullptr; + } + + return IfsOpInit::get(Case, Val, Default, Type)->Fold(CurRec); +} + /// ParseSimpleValue - Parse a tblgen value. This returns null on error. /// /// SimpleValue ::= IDValue @@ -1621,6 +1758,7 @@ /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' /// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= IFS '(' [Value ':' Value,]+ DEFAULT ':' Value ')' /// Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { @@ -1933,6 +2071,7 @@ case tgtok::XListConcat: case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: + case tgtok::XIfs: case tgtok::XFoldl: case tgtok::XForEach: case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' Index: test/TableGen/ifs-empty-list-arg.td =================================================================== --- /dev/null +++ test/TableGen/ifs-empty-list-arg.td @@ -0,0 +1,7 @@ +// RUN: llvm-tblgen %s +// XFAIL: vg_leak + +class C { + list X = !ifs(cond: [1, 2, 3], default: []); + list Y = !ifs(cond: [], default: [4, 5, 6]); +} Index: test/TableGen/ifs-type.td =================================================================== --- /dev/null +++ test/TableGen/ifs-type.td @@ -0,0 +1,12 @@ +// 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 = !ifs(cc: b, + default: c); +} Index: test/TableGen/ifs-usage.td =================================================================== --- /dev/null +++ test/TableGen/ifs-usage.td @@ -0,0 +1,55 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +// Check that !ifs picks the first true value +// CHECK: class A +// CHECK-NEXT: string S = !ifs(!eq(A:x, 10): "ten", !eq(A:x, 11): "eleven", !eq(A:x, 10): "TEN", !gt(A:x, 9): "MoreThanNine", default: "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 = !ifs(!eq(x,10) : "ten", + !eq(x,11) : "eleven", + !eq(x,10) : "TEN", + !gt(x,9) : "MoreThanNine", + default : "unknown"); +} +def B1 : A<1>; +def B9 : A<9>; +def B10 : A<10>; +def B11 : A<11>; +def B12 : A<12>; + + +// Check that order of default does not matter, by putting default first in the above test +// CHECK: Bditto1 +// CHECK-NEXT: string S = "unknown" +// CHECK: Bditto10 +// CHECK-NEXT: string S = "ten"; +// CHECK: def Bditto11 +// CHECK-NEXT: string S = "eleven"; +// CHECK: def Bditto12 +// CHECK-NEXT: string S = "MoreThanNine"; +// CHECK: def Bditto9 +// CHECK-NEXT: string S = "unknown" +class Aditto { + string S = !ifs(default : "unknown", + !eq(x,10) : "ten", + !eq(x,11) : "eleven", + !eq(x,10) : "TEN", + !gt(x,9) : "MoreThanNine"); +} + +def Bditto1 : Aditto<1>; +def Bditto9 : Aditto<9>; +def Bditto10 : Aditto<10>; +def Bditto11 : Aditto<11>; +def Bditto12 : Aditto<12>; Index: test/TableGen/ifs.td =================================================================== --- /dev/null +++ test/TableGen/ifs.td @@ -0,0 +1,135 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +// Support for an `!ifs' operator as part of a `let' statement. +// CHECK: class C +// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, !ifs({ C:y{3} }: 1, { C:y{2} }: { C:x{0} }, { C:y{1} }: { C:x{1} }, { C:y{0} }: { C:x{2} }, default: ?){0}, !ifs({ C:x{2} }: { C:y{3}, C:y{2} }, { C:x{1} }: { C:y{2}, C:y{1} }, { C:x{0} }: { C:y{1}, C:y{0} }, default: ?){1}, !ifs({ C:x{2} }: { C:y{3}, C:y{2} }, { C:x{1} }: { C:y{2}, C:y{1} }, { C:x{0} }: { C:y{1}, C:y{0} }, default: ?){0}, !ifs({ C:x{2} }: { 0, 1, 0 }, default: { 1, 1, 0 }){2}, !ifs({ C:x{2} }: { 0, 1, 0 }, default: { 1, 1, 0 }){1}, !ifs({ C:x{2} }: { 0, 1, 0 }, default: { 1, 1, 0 }){0}, !ifs({ C:x{1} }: { C:y{3}, C:y{2} }, default: { 0, 1 }){1}, !ifs({ C:x{1} }: { C:y{3}, C:y{2} }, default: { 0, 1 }){0}, !ifs({ C:x{0} }: { C:y{3}, C:y{2}, C:y{1}, C:y{0} }, default: { C:z, C:y{2}, C:y{1}, C:y{0} }){3}, !ifs({ C:x{0} }: { C:y{3}, C:y{2}, C:y{1}, C:y{0} }, default: { C:z, C:y{2}, C:y{1}, C:y{0} }){2}, !ifs({ C:x{0} }: { C:y{3}, C:y{2}, C:y{1}, C:y{0} }, default: { C:z, C:y{2}, C:y{1}, C:y{0} }){1}, !ifs({ C:x{0} }: { C:y{3}, C:y{2}, C:y{1}, C:y{0} }, default: { C:z, C:y{2}, C:y{1}, C:y{0} }){0} }; + +class C x, bits<4> y, bit z> { + bits<16> n; + + let n{11} = !ifs(y{3}: 1, + y{2}: x{0}, + y{1}: x{1}, + y{0}: x{2}, + default :?); + let n{10-9}= !ifs(x{2}: y{3-2}, + x{1}: y{2-1}, + x{0}: y{1-0}, + default: ?); + let n{8-6} = !ifs(x{2}: 0b010, default: 0b110); + let n{5-4} = !ifs(x{1}: y{3-2},default: {0, 1}); + let n{3-0} = !ifs(x{0}: y{3-0}, default: {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 }; + +class S { + bits<2> val = !ifs(!eq(s, 8): {0, 0}, + !eq(s, 16): 0b01, + !eq(s, 32): 2, + !eq(s, 64): {1, 1}, + default: ?); +} + +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 }; + +// Make sure !ifs gets propagated across multiple layers of inheritance. +class getInt { + int ret = !ifs(c: 0, default: 1); +} +class I1 { + int i = getInt.ret; +} +class I2 : I1; + +// CHECK: def DI1 { // I1 +// CHECK-NEXT: int i = 0; +def DI1: I1<1>; + +// CHECK: def DI2 { // I1 I2 +// CHECK-NEXT: int i = 0; +def DI2: I2<1>; + +// Check that !ifs with operands of different subtypes can initialize a +// supertype variable. +// +// CHECK: def EXd1 { +// CHECK: E x = E1d; +// CHECK: } +// +// CHECK: def EXd2 { +// CHECK: E x = E2d; +// CHECK: } +class E {} +class E1 : E {} +class E2 : E {} + +class EX { + E x = !ifs(cc: b, default: c); +} + +def E1d : E1<0>; +def E2d : E2<0>; + +def EXd1 : EX<1, E1d, E2d>; +def EXd2 : EX<0, E1d, E2d>; + +// CHECK: def One +// CHECK-NEXT: list first = [1, 2, 3]; +// CHECK-NEXT: list rest = [1, 2, 3]; + +// CHECK: def OneB +// CHECK-NEXT: list vals = [1, 2, 3]; + +// CHECK: def Two +// CHECK-NEXT: list first = [1, 2, 3]; +// CHECK-NEXT: list rest = [4, 5, 6]; + +// CHECK: def TwoB +// CHECK-NEXT: list vals = [4, 5, 6]; + +class A> vals> { + list first = vals[0]; + list rest = !ifs(!empty(!tail(vals)): vals[0], + default : vals[1]); +} + +def One : A<[[1,2,3]]>; +def Two : A<[[1,2,3], [4,5,6]]>; + +class B v> { + list vals = v; +} + +class BB> vals> : B; +class BBB> vals> : BB; + +def OneB : BBB<[[1,2,3]]>; +def TwoB : BBB<[[1,2,3],[4,5,6]]>; Index: test/TableGen/ifsbit.td =================================================================== --- /dev/null +++ test/TableGen/ifsbit.td @@ -0,0 +1,13 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak +// CHECK: a = 6 +// CHECK: a = 5 + +class A { + int a = !ifs(b: 5, default: 6); + bit c = !ifs(b: 0, default: 1); + bits<1> d = !ifs(b: 0, default: 1); +} + +def X : A<0>; +def Y : A;