Skip to content

Commit

Permalink
[TableGen] Support type aliases via new keyword deftype
Browse files Browse the repository at this point in the history
We can use `deftype` (not using `typedef` here to be consistent
with `def`, `defm`, `defset`, `defvar`, etc) to define type aliases.

Currently, only primitive types and type aliases are supported to be
the source type and `deftype` statements can only appear at the top
level.

Reviewers: fpetrogalli, Artem-B, nhaehnle, jroelofs

Reviewed By: jroelofs, nhaehnle, Artem-B

Pull Request: #79570
  • Loading branch information
wangpc-pp committed Feb 2, 2024
1 parent 438fe1d commit acf6811
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 4 deletions.
2 changes: 2 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Changes to building LLVM
Changes to TableGen
-------------------

- We can define type aliases via new keyword ``deftype``.

Changes to Interprocedural Optimizations
----------------------------------------

Expand Down
18 changes: 16 additions & 2 deletions llvm/docs/TableGen/ProgRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,8 @@ files.
.. productionlist::
TableGenFile: (`Statement` | `IncludeDirective`
:| `PreprocessorDirective`)*
Statement: `Assert` | `Class` | `Def` | `Defm` | `Defset` | `Defvar`
:| `Dump` | `Foreach` | `If` | `Let` | `MultiClass`
Statement: `Assert` | `Class` | `Def` | `Defm` | `Defset` | `Deftype`
:| `Defvar` | `Dump` | `Foreach` | `If` | `Let` | `MultiClass`

The following sections describe each of these top-level statements.

Expand Down Expand Up @@ -1215,6 +1215,20 @@ set.
Anonymous records created inside initialization expressions using the
``ClassID<...>`` syntax are not collected in the set.

``deftype`` --- define a type
--------------------------------

A ``deftype`` statement defines a type. The type can be used throughout the
statements that follow the definition.

.. productionlist::
Deftype: "deftype" `TokIdentifier` "=" `Type` ";"

The identifier on the left of the ``=`` is defined to be a type name
whose actual type is given by the type expression on the right of the ``=``.

Currently, only primitive types and type aliases are supported to be the source
type and `deftype` statements can only appear at the top level.

``defvar`` --- define a variable
--------------------------------
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/TableGen/TGLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ tgtok::TokKind TGLexer::LexIdentifier() {
.Case("foreach", tgtok::Foreach)
.Case("defm", tgtok::Defm)
.Case("defset", tgtok::Defset)
.Case("deftype", tgtok::Deftype)
.Case("multiclass", tgtok::MultiClass)
.Case("field", tgtok::Field)
.Case("let", tgtok::Let)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/TableGen/TGLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ enum TokKind {
Def,
Defm,
Defset,
Deftype,
Defvar,
Dump,
Foreach,
Expand Down
54 changes: 52 additions & 2 deletions llvm/lib/TableGen/TGParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,11 +1103,17 @@ RecTy *TGParser::ParseType() {
case tgtok::Dag:
Lex.Lex();
return DagRecTy::get(Records);
case tgtok::Id:
case tgtok::Id: {
auto I = TypeAliases.find(Lex.getCurStrVal());
if (I != TypeAliases.end()) {
Lex.Lex();
return I->second;
}
if (Record *R = ParseClassID())
return RecordRecTy::get(R);
TokError("unknown class name");
return nullptr;
}
case tgtok::Bits: {
if (Lex.Lex() != tgtok::less) { // Eat 'bits'
TokError("expected '<' after bits type");
Expand Down Expand Up @@ -3665,6 +3671,42 @@ bool TGParser::ParseDefset() {
return false;
}

/// ParseDeftype - Parse a defvar statement.
///
/// Deftype ::= DEFTYPE Id '=' Type ';'
///
bool TGParser::ParseDeftype() {
assert(Lex.getCode() == tgtok::Deftype);
Lex.Lex(); // Eat the 'deftype' token

if (Lex.getCode() != tgtok::Id)
return TokError("expected identifier");

const std::string TypeName = Lex.getCurStrVal();
if (TypeAliases.count(TypeName) || Records.getClass(TypeName))
return TokError("type of this name '" + TypeName + "' already exists");

Lex.Lex();
if (!consume(tgtok::equal))
return TokError("expected '='");

SMLoc Loc = Lex.getLoc();
RecTy *Type = ParseType();
if (!Type)
return true;

if (Type->getRecTyKind() == RecTy::RecordRecTyKind)
return Error(Loc, "cannot define type alias for class type '" +
Type->getAsString() + "'");

TypeAliases[TypeName] = Type;

if (!consume(tgtok::semi))
return TokError("expected ';'");

return false;
}

/// ParseDefvar - Parse a defvar statement.
///
/// Defvar ::= DEFVAR Id '=' Value ';'
Expand Down Expand Up @@ -3914,7 +3956,8 @@ bool TGParser::ParseClass() {
if (Lex.getCode() != tgtok::Id)
return TokError("expected class name after 'class' keyword");

Record *CurRec = Records.getClass(Lex.getCurStrVal());
const std::string &Name = Lex.getCurStrVal();
Record *CurRec = Records.getClass(Name);
if (CurRec) {
// If the body was previously defined, this is an error.
if (!CurRec->getValues().empty() ||
Expand All @@ -3931,6 +3974,10 @@ bool TGParser::ParseClass() {
CurRec = NewRec.get();
Records.addClass(std::move(NewRec));
}

if (TypeAliases.count(Name))
return TokError("there is already a defined type alias '" + Name + "'");

Lex.Lex(); // eat the name.

// A class definition introduces a new scope.
Expand Down Expand Up @@ -4265,6 +4312,7 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
/// Object ::= LETCommand '{' ObjectList '}'
/// Object ::= LETCommand Object
/// Object ::= Defset
/// Object ::= Deftype
/// Object ::= Defvar
/// Object ::= Assert
/// Object ::= Dump
Expand All @@ -4276,6 +4324,8 @@ bool TGParser::ParseObject(MultiClass *MC) {
case tgtok::Assert: return ParseAssert(MC);
case tgtok::Def: return ParseDef(MC);
case tgtok::Defm: return ParseDefm(MC);
case tgtok::Deftype:
return ParseDeftype();
case tgtok::Defvar: return ParseDefvar();
case tgtok::Dump:
return ParseDump(MC);
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/TableGen/TGParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class TGParser {
TGLexer Lex;
std::vector<SmallVector<LetRecord, 4>> LetStack;
std::map<std::string, std::unique_ptr<MultiClass>> MultiClasses;
std::map<std::string, RecTy *> TypeAliases;

/// Loops - Keep track of any foreach loops we are within.
///
Expand Down Expand Up @@ -264,6 +265,7 @@ class TGParser {
bool ParseDefm(MultiClass *CurMultiClass);
bool ParseDef(MultiClass *CurMultiClass);
bool ParseDefset();
bool ParseDeftype();
bool ParseDefvar(Record *CurRec = nullptr);
bool ParseDump(MultiClass *CurMultiClass, Record *CurRec = nullptr);
bool ParseForeach(MultiClass *CurMultiClass);
Expand Down
70 changes: 70 additions & 0 deletions llvm/test/TableGen/deftype.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: llvm-tblgen %s | FileCheck %s
// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
// RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s
// RUN: not llvm-tblgen -DERROR4 %s 2>&1 | FileCheck --check-prefix=ERROR4 %s
// RUN: not llvm-tblgen -DERROR5 %s 2>&1 | FileCheck --check-prefix=ERROR5 %s

class Class<int v> {
int value = v;
}

deftype StringAlias = string;
deftype CodeAlias = code;
deftype DagAlias = dag;
deftype Boolean = bit;
deftype Byte = bits<8>;
deftype Integer = int;
deftype IntList = list<int>;
deftype ByteList = list<Byte>;
deftype ClassList = list<Class>;
// The type can be another type alias.
deftype ClassListAlias = ClassList;

// CHECK: def test {
// CHECK-NEXT: string str = "string";
// CHECK-NEXT: string codeStr = "code";
// CHECK-NEXT: dag dagExpr = ("string" "code");
// CHECK-NEXT: bit bool = 0;
// CHECK-NEXT: bits<8> byte = { 0, 1, 1, 1, 1, 0, 1, 1 };
// CHECK-NEXT: int integer = 123;
// CHECK-NEXT: list<int> ints = [1, 2, 3];
// CHECK-NEXT: list<bits<8>> bytes = [{ 0, 0, 0, 0, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 1, 1 }];
// CHECK-NEXT: list<Class> defs = [anonymous_0, anonymous_1, anonymous_2];
// CHECK-NEXT: }
def test {
StringAlias str = "string";
CodeAlias codeStr = "code";
DagAlias dagExpr = (str codeStr);
Boolean bool = false;
Byte byte = 123;
Integer integer = 123;
IntList ints = [1, 2, 3];
ByteList bytes = [1, 2, 3];
ClassListAlias defs = [Class<1>, Class<2>, Class<3>];
}

#ifdef ERROR1
// ERROR1: [[@LINE+1]]:9: error: type of this name 'Byte' already exists
deftype Byte = bits<8>;
#endif

#ifdef ERROR2
// ERROR2: [[@LINE+1]]:9: error: type of this name 'Class' already exists
deftype Class = int;
#endif

#ifdef ERROR3
// ERROR3: [[@LINE+1]]:22: error: cannot define type alias for class type 'Class'
deftype ClassAlias = Class;
#endif

#ifdef ERROR4
// ERROR4: [[@LINE+1]]:7: error: there is already a defined type alias 'Byte'
class Byte; // incomplete class definition.
#endif

#ifdef ERROR5
// ERROR5: [[@LINE+1]]:7: error: there is already a defined type alias 'Byte'
class Byte {}
#endif

0 comments on commit acf6811

Please sign in to comment.