From dc5d6632b0c25cc5c057325c517f28575452d602 Mon Sep 17 00:00:00 2001 From: "Paul C. Anagnostopoulos" Date: Sun, 18 Oct 2020 10:47:07 -0400 Subject: [PATCH] [TableGen] Enhance !empty and !size to handle strings and DAGs. Fix bug in the type checking for !empty, !head, !size, !tail. --- llvm/docs/TableGen/ProgRef.rst | 14 +++++---- llvm/lib/TableGen/Record.cpp | 6 ++++ llvm/lib/TableGen/TGParser.cpp | 64 +++++++++++++++++++------------------ llvm/test/TableGen/empty.td | 71 ++++++++++++++++++++++++++++++++++++++++++ llvm/test/TableGen/size.td | 69 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+), 37 deletions(-) create mode 100644 llvm/test/TableGen/empty.td diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst index a70f54c..fa9989d 100644 --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -274,7 +274,7 @@ wide range of records conveniently and compactly. the programmer's intention. ``bits<``\ *n*\ ``>`` - The ``bits`` type is a fixed-size integer of arbitrary length *n* that + The ``bits`` type is a fixed-sized integer of arbitrary length *n* that is treated as separate bits. These bits can be accessed individually. A field of this type is useful for representing an instruction operation code, register number, or address mode/register/displacement. The bits of @@ -1242,8 +1242,8 @@ a name is to tag an operator or argument in a DAG with a particular meaning, or to associate an argument in one DAG with a like-named argument in another DAG. -The following bang operators manipulate DAGs: ``!con``, ``!dag``, ``!foreach``, -``!getop``, ``!setop``. +The following bang operators are useful for working with DAGs: +``!con``, ``!dag``, ``!empty``, ``!foreach``, ``!getop``, ``!setop``, ``!size``. Defvar in a record body ----------------------- @@ -1509,8 +1509,9 @@ and non-0 as true. Example: ``!dag(op, [a1, a2, ?], ["name1", "name2", "name3"])`` results in ``(op a1:$name1, a2:$name2, ?:$name3)``. -``!empty(``\ *list*\ ``)`` - This operator produces 1 if the *list* is empty; 0 otherwise. +``!empty(``\ *a*\ ``)`` + This operator produces 1 if the string, list, or DAG *a* is empty; 0 otherwise. + A dag is empty if it has no arguments; the operator does not count. ``!eq(`` *a*\ `,` *b*\ ``)`` This operator produces 1 if *a* is equal to *b*; 0 otherwise. @@ -1631,7 +1632,8 @@ and non-0 as true. is undefined for shift counts outside 0...63. ``!size(``\ *a*\ ``)`` - This operator produces the number of elements in the list *a*. + This operator produces the size of the string, list, or dag *a*. + The size of a DAG is the number of arguments; the operator does not count. ``!sra(``\ *a*\ ``,`` *count*\ ``)`` This operator shifts *a* right arithmetically by *count* bits and produces the resulting diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index f191638..d404a42 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -786,11 +786,17 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { case SIZE: if (ListInit *LHSl = dyn_cast(LHS)) return IntInit::get(LHSl->size()); + if (DagInit *LHSd = dyn_cast(LHS)) + return IntInit::get(LHSd->arg_size()); + if (StringInit *LHSs = dyn_cast(LHS)) + return IntInit::get(LHSs->getValue().size()); break; case EMPTY: if (ListInit *LHSl = dyn_cast(LHS)) return IntInit::get(LHSl->empty()); + if (DagInit *LHSd = dyn_cast(LHS)) + return IntInit::get(LHSd->arg_empty()); if (StringInit *LHSs = dyn_cast(LHS)) return IntInit::get(LHSs->getValue().empty()); break; diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 98443c9..83a2ba1 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -986,56 +986,58 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { Init *LHS = ParseValue(CurRec); if (!LHS) return nullptr; - if (Code == UnOpInit::HEAD || - Code == UnOpInit::TAIL || - Code == UnOpInit::EMPTY) { + if (Code == UnOpInit::EMPTY || Code == UnOpInit::SIZE) { ListInit *LHSl = dyn_cast(LHS); StringInit *LHSs = dyn_cast(LHS); + DagInit *LHSd = dyn_cast(LHS); TypedInit *LHSt = dyn_cast(LHS); - if (!LHSl && !LHSs && !LHSt) { - TokError("expected list or string type argument in unary operator"); + if (!LHSl && !LHSs && !LHSd && !LHSt) { + TokError("expected string, list, or dag type argument in unary operator"); return nullptr; } if (LHSt) { ListRecTy *LType = dyn_cast(LHSt->getType()); StringRecTy *SType = dyn_cast(LHSt->getType()); - if (!LType && !SType) { - TokError("expected list or string type argument in unary operator"); + DagRecTy *DType = dyn_cast(LHSt->getType()); + if (!LType && !SType && !DType) { + TokError("expected string, list, or dag type argument in unary operator"); return nullptr; } } + } - if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL || - Code == UnOpInit::SIZE) { - if (!LHSl && !LHSt) { + if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL) { + ListInit *LHSl = dyn_cast(LHS); + TypedInit *LHSt = dyn_cast(LHS); + if (!LHSl && !LHSt) { + TokError("expected list type argument in unary operator"); + return nullptr; + } + if (LHSt) { + ListRecTy *LType = dyn_cast(LHSt->getType()); + if (!LType) { TokError("expected list type argument in unary operator"); return nullptr; } } - if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL) { - if (LHSl && LHSl->empty()) { - TokError("empty list argument in unary operator"); + if (LHSl && LHSl->empty()) { + TokError("empty list argument in unary operator"); + return nullptr; + } + if (LHSl) { + Init *Item = LHSl->getElement(0); + TypedInit *Itemt = dyn_cast(Item); + if (!Itemt) { + TokError("untyped list element in unary operator"); return nullptr; } - if (LHSl) { - Init *Item = LHSl->getElement(0); - TypedInit *Itemt = dyn_cast(Item); - if (!Itemt) { - TokError("untyped list element in unary operator"); - return nullptr; - } - Type = (Code == UnOpInit::HEAD) ? Itemt->getType() - : ListRecTy::get(Itemt->getType()); - } else { - assert(LHSt && "expected list type argument in unary operator"); - ListRecTy *LType = dyn_cast(LHSt->getType()); - if (!LType) { - TokError("expected list type argument in unary operator"); - return nullptr; - } - Type = (Code == UnOpInit::HEAD) ? LType->getElementType() : LType; - } + Type = (Code == UnOpInit::HEAD) ? Itemt->getType() + : ListRecTy::get(Itemt->getType()); + } else { + assert(LHSt && "expected list type argument in unary operator"); + ListRecTy *LType = dyn_cast(LHSt->getType()); + Type = (Code == UnOpInit::HEAD) ? LType->getElementType() : LType; } } diff --git a/llvm/test/TableGen/empty.td b/llvm/test/TableGen/empty.td new file mode 100644 index 0000000..f751c27 --- /dev/null +++ b/llvm/test/TableGen/empty.td @@ -0,0 +1,71 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +defvar LongList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +defvar EmptyStr = ""; + +// Test !empty(dag). + +// CHECK: def Drec1 { +// CHECK: bit Empty = 1; +// CHECK: bit NotEmpty = 0; +// CHECK: def Drec2 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; +// CHECK: def Drec3 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; + +class D { + bit Empty = !empty(ADag); + bit NotEmpty = !not(!empty(ADag)); +} + +def op; + +def Drec1 : D<(op)>; +def Drec2 : D<(op "string")>; +def Drec3 : D<(op "string", 42)>; + +// Test !empty(list). + +// CHECK: def Lrec1 { +// CHECK: bit Empty = 1; +// CHECK: bit NotEmpty = 0; +// CHECK: def Lrec2 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; +// CHECK: def Lrec3 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; + +class L Ints> { + bit Empty = !empty(Ints); + bit NotEmpty = !not(!empty(Ints)); +} + +def Lrec1 : L<[]>; +def Lrec2 : L<[1]>; +def Lrec3 : L; + +// Test !empty(string). + +// CHECK: def Srec1 { +// CHECK: bit Empty = 1; +// CHECK: bit NotEmpty = 0; +// CHECK: def Srec2 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; +// CHECK: def Srec3 { +// CHECK: bit Empty = 0; +// CHECK: bit NotEmpty = 1; + +class S { + bit Empty = !empty(Str); + bit NotEmpty = !not(!empty(Str)); +} + +def Srec1 : S; +def Srec2 : S<"a">; +def Srec3 : S<"ab">; + diff --git a/llvm/test/TableGen/size.td b/llvm/test/TableGen/size.td index f7061e1..5a1e661 100644 --- a/llvm/test/TableGen/size.td +++ b/llvm/test/TableGen/size.td @@ -1,6 +1,8 @@ // RUN: llvm-tblgen %s | FileCheck %s // XFAIL: vg_leak +// Test !size of lists. + // CHECK: --- Defs --- // CHECK: def A1 { @@ -32,3 +34,70 @@ def A2 : A<[1, 1, 2]>; def B1 : B<[]>; def B2 : B<["a", "b"]>; + +// Test !size of DAGs. + +// CHECK: def D0 { +// CHECK: int Val = 0; +// CHECK: } + +// CHECK: def D1 { +// CHECK: int Val = 1; +// CHECK: } + +// CHECK: def D2 { +// CHECK: int Val = 2; +// CHECK: } + +// CHECK: def D3 { +// CHECK: int Val = 3; +// CHECK: } + +// CHECK: def D4 { +// CHECK: int Val = 4; +// CHECK: } + +class D { + int Val = !size(D); +} + +def op; + +def D0 : D<(op)>; +def D1 : D<(op "string")>; +def D2 : D<(op "string", 42)>; +def D3 : D<(op "string", 42, (op "sub-dag"))>; +def D4 : D<(op "string", 42, (op "sub-dag"), D0.Val)>; + +// Test !size of strings. + +// CHECK: def S0 { +// CHECK: int Val = 0; +// CHECK: } + +// CHECK: def S1 { +// CHECK: int Val = 1; +// CHECK: } + +// CHECK: def S2 { +// CHECK: int Val = 2; +// CHECK: } + +// CHECK: def S3 { +// CHECK: int Val = 3; +// CHECK: } + +// CHECK: def S4 { +// CHECK: int Val = 29; +// CHECK: } + +class S { + int Val = !size(S); +} + +def S0 : S<"">; +def S1 : S<"a">; +def S2 : S<"ab">; +def S3 : S<"abc">; +def S4 : S<"This is the end of the world!">; + -- 2.7.4