diff --git a/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.ipynb b/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.ipynb new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.ipynb @@ -0,0 +1,775 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b1123484", + "metadata": {}, + "source": [ + "## Intro to TableGen Part 1: Classes and Defs" + ] + }, + { + "cell_type": "markdown", + "id": "15a8d57a", + "metadata": {}, + "source": [ + "**Note:** The content in this notebook is adapted from https://llvm.org/docs/TableGen/index.html.\n", + "\n", + "TableGen is a language used in LLVM to automate the generation of certain types of code. Usually repetitive code that has a common structure. TableGen is used to generate \"records\" that are then processed by a \"backend\" into domain specific code.\n", + "\n", + "The compiler for TableGen is the binary `llvm-tblgen`. This contains the logic to convert TableGen source into records that can then be passed to a TableGen backend.\n", + "\n", + "TableGen allows you to define Classes and Defs (instances of classes) but it doesn't encode what to do with that structure. That's what the \"backend\" does. The \"backend\" converts this structure into something useful. For example, C++ code.\n", + "\n", + "These backends are included in the `llvm-tblgen` binary and you can choose which one to run using a command line option. If you don't choose a backend you get a dump of the structure, and this is what this notebook uses." + ] + }, + { + "cell_type": "markdown", + "id": "4c53f250", + "metadata": {}, + "source": [ + "This tutorial will focus on the language itself only. The only thing you need to know now is that in addition to `llvm-tblgen` you will see other `*-tblgen` like `clang-tblgen`. The difference between them is the backends they include." + ] + }, + { + "cell_type": "markdown", + "id": "dd31a31a", + "metadata": {}, + "source": [ + "The default output from `llvm-tblgen` looks like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "88331eb7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "------------- Defs -----------------\n" + ] + } + ], + "source": [ + "// Empty source file" + ] + }, + { + "cell_type": "markdown", + "id": "58f4b3e8", + "metadata": {}, + "source": [ + "No source means no classes and no defs. Let's add a class." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "249f27bf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + "}\n", + "------------- Defs -----------------\n" + ] + } + ], + "source": [ + "class C {}" + ] + }, + { + "cell_type": "markdown", + "id": "e92f5c84", + "metadata": {}, + "source": [ + "Followed shortly by defs (or definitions if you prefer)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2a101610", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + "}\n" + ] + } + ], + "source": [ + "def X: C;" + ] + }, + { + "cell_type": "markdown", + "id": "6b1b79a5", + "metadata": {}, + "source": [ + "A def creates an instance of a class. Typically, the main loop of a TableGen backend will look for all defs that are instances of a certain class.\n", + "\n", + "For example if I am generating register information I would look for all defs that are instances of RegisterInfo in the example below." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "964e9ba5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class RegisterInfo {\n", + "}\n", + "------------- Defs -----------------\n", + "def X0 {\t// RegisterInfo\n", + "}\n", + "def X1 {\t// RegisterInfo\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class RegisterInfo {}\n", + "def X0: RegisterInfo {}\n", + "def X1: RegisterInfo {}" + ] + }, + { + "cell_type": "markdown", + "id": "b2ef33b7", + "metadata": {}, + "source": [ + "Of course we need some attributes in these classes and defs, which brings me to types.\n", + "\n", + "TableGen is statically typed (https://llvm.org/docs/TableGen/ProgRef.html#types) with error checking if you try to assign the wrong things. Below are a few of the builtin types." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "232a8bdf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int a = ?;\n", + " bit b = 0;\n", + " string s = \"Hello\";\n", + "}\n", + "------------- Defs -----------------\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a;\n", + " bit b = 0;\n", + " string s = \"Hello\";\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "c2c6203b", + "metadata": {}, + "source": [ + "Note that you do not have to give a member a default value." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9f8149cb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int a = ?;\n", + " bit b = 0;\n", + " string s = \"Hello\";\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + " int a = ?;\n", + " bit b = 0;\n", + " string s = \"Hello\";\n", + "}\n" + ] + } + ], + "source": [ + "def X: C {}" + ] + }, + { + "cell_type": "markdown", + "id": "79ead3c0", + "metadata": {}, + "source": [ + "When you make an instance of a class using `def`, that instance gets all the members of the class. Their values will be as set in the class.\n", + "\n", + "In the case of `a` it also keeps the undefined value. Any backend using that would have to check for that case." + ] + }, + { + "cell_type": "markdown", + "id": "65eb1236", + "metadata": {}, + "source": [ + "If we want to override those values we can use `let` (https://llvm.org/docs/TableGen/ProgRef.html#let-override-fields-in-classes-or-records). This can be done in a couple of ways. The first is where you mark the scope of the let using `in {}`.\n", + "\n", + "Below, I am saying that within the `{}` after the `let`, all `a` should have the value 5." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "296a5d9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + " int a = 5;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "let a=5 in {\n", + " def X: C {}\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "89b5cdd9", + "metadata": {}, + "source": [ + "You can also use `let` within a def. Limiting its scope to the `{}` of the def itself." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "826a5449", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + " int a = 5;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "def X: C {\n", + " let a=5;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "ad57c118", + "metadata": {}, + "source": [ + "As you'd expect, it is an error to try to `let` a name that hasn't been defined or to give it a value of the incorrect type." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ddc86443", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":5:9: error: Field 'a' of type 'int' is incompatible with value '\"Hello\"' of type 'string'\n", + " let a=\"Hello\";\n", + " ^\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "def X: C {\n", + " let a=\"Hello\";\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2748d14c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":5:11: error: Value 'b' unknown!\n", + " let b=5;\n", + " ^\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "def X: C {\n", + " let b=5;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "a8b89546", + "metadata": {}, + "source": [ + "If you have multiple let, the outer scope is applied first then down to the narrowest." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "92e3bb40", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int a = 9;\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + " int a = 6;\n", + "}\n", + "def Y {\t// C\n", + " int a = 5;\n", + "}\n", + "def Z {\t// C\n", + " int a = 3;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int a=9;\n", + "}\n", + "let a=5 in {\n", + " let a=6 in {\n", + " def X: C {}\n", + " }\n", + " def Y: C {}\n", + " def Z: C { let a=3; }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "777c9390", + "metadata": {}, + "source": [ + "The first let is at what we call the \"top level\". That means the outer most scope in terms of the source code. A bit like a global variable in a C file.\n", + "\n", + "That is applied first and changes `a` from `9` to `5`. `X` is within a further let that changes `a` from `5` to `6`. `Y` however is outside that so its `a` remains `5`.\n", + "\n", + "`Z` takes it a step further with a `let` inside the def. This overrides all the others to make `a` `3` within `Z`.\n", + "\n", + "That example is overly complex, but only to demonstrate the feature. A practial example is shown below. What if we are describing registers and only some of those registers are 64 bits and the majority are 32 bits?" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "626b2c19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class Register {\n", + " int size = 4;\n", + "}\n", + "------------- Defs -----------------\n", + "def W0 {\t// Register\n", + " int size = 4;\n", + "}\n", + "def X0 {\t// Register\n", + " int size = 8;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class Register {\n", + " int size=4;\n", + "}\n", + "let size=8 in {\n", + " def X0: Register {}\n", + " // Repeats 31 times...\n", + "}\n", + "def W0: Register {}\n", + "// Repeats 31 times..." + ] + }, + { + "cell_type": "markdown", + "id": "6b90974b", + "metadata": {}, + "source": [ + "(for anyone curious that's AArch64's register naming)\n", + "\n", + "We start by setting a default value of `size` which is 4 (4x8=32 bits). Then using a top level `let` we override that value and set it to 8 for all the 64 bit registers." + ] + }, + { + "cell_type": "markdown", + "id": "a15296c4", + "metadata": {}, + "source": [ + "In addition to the built in types, class members can be user defined types as well." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "eaa4b9ec", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class Inner {\n", + "}\n", + "class Outer {\n", + " Inner i = ?;\n", + "}\n", + "------------- Defs -----------------\n" + ] + } + ], + "source": [ + "%reset\n", + "class Inner {}\n", + "class Outer {\n", + " Inner i;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "1faa1c3c", + "metadata": {}, + "source": [ + "Of course that raises the question, how do we construct an instance of `Inner` to use as the value? Using a `def` of course!" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "243afa46", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class Inner {\n", + "}\n", + "class Outer {\n", + " Inner i = AnInner;\n", + "}\n", + "------------- Defs -----------------\n", + "def AnInner {\t// Inner\n", + "}\n", + "def AnOuter {\t// Outer\n", + " Inner i = AnInner;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class Inner {}\n", + "def AnInner: Inner {}\n", + "class Outer {\n", + " Inner i = AnInner;\n", + "}\n", + "def AnOuter: Outer;" + ] + }, + { + "cell_type": "markdown", + "id": "49ef7b63", + "metadata": {}, + "source": [ + "But what if I wanted to make that a parameter instead of a making a definition up front? This is where class template arguments come in.\n", + "\n", + "I'm going to switch back to builtin types just to make the example clearer." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "888f1d04", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class C {\n", + " int c = C:a;\n", + " int d = C:b;\n", + "}\n", + "------------- Defs -----------------\n", + "def X {\t// C\n", + " int c = 0;\n", + " int d = 1;\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int c = a;\n", + " int d = b;\n", + "}\n", + "def X: C<0, 1> {}" + ] + }, + { + "cell_type": "markdown", + "id": "071294c5", + "metadata": {}, + "source": [ + "This is going to look familiar if you have written C++. Here it is in C++ using a templated class.\n", + "```\n", + "template\n", + "class C {\n", + " int c = a;\n", + " int d = b;\n", + "};\n", + "C<0, 1> X;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "ecd8c692", + "metadata": {}, + "source": [ + "`C` now requires 2 template arguments and type checking applies as it would for a class member." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "eb5cb0a9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":5:8: error: Value not specified for template argument 'C:b' (#1) of parent class 'C'\n", + "def X: C<0> {}\n", + " ^\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int c = a;\n", + " int d = b;\n", + "}\n", + "def X: C<0> {}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "480b263f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":5:8: error: Value specified for template argument 'C:b' (#1) is of type string; expected type int: \"hello\"\n", + "def X: C<0, \"hello\"> {}\n", + " ^\n" + ] + } + ], + "source": [ + "%reset\n", + "class C {\n", + " int c = a;\n", + " int d = b;\n", + "}\n", + "def X: C<0, \"hello\"> {}" + ] + }, + { + "cell_type": "markdown", + "id": "fd25ec74", + "metadata": {}, + "source": [ + "Using this you can enforce a structure on the user of these classes. In our register example I could enforce that you give a size value for example.\n", + "\n", + "You can combine these class template arguments with class members that aren't derived from template arguments to implement optional arguments.\n", + "\n", + "The code below makes the size argument mandatory but the alias optional. Something along the lines of `Register(int size, const char* alias=nullptr);` if it were C++." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "e6d4b0e5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "------------- Classes -----------------\n", + "class Register {\n", + " int size = Register:_size;\n", + " string alias = \"\";\n", + "}\n", + "------------- Defs -----------------\n", + "def X0 {\t// Register\n", + " int size = 8;\n", + " string alias = \"\";\n", + "}\n", + "def X29 {\t// Register\n", + " int size = 8;\n", + " string alias = \"frame pointer\";\n", + "}\n" + ] + } + ], + "source": [ + "%reset\n", + "class Register {\n", + " int size = _size;\n", + " string alias = \"\";\n", + "}\n", + "def X0: Register<8> {}\n", + "def X29: Register<8> {\n", + " let alias=\"frame pointer\";\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "b07af3ed", + "metadata": {}, + "source": [ + "**Note:** Unlike a c++ constructor you can't reuse the name between the template argument and the class member.\n", + "Here I have added `_` to the template argument but there's no required style." + ] + }, + { + "cell_type": "markdown", + "id": "0810ae4a", + "metadata": {}, + "source": [ + "This concludes part 1 of this tutorial. To learn more advanced features, see the following parts which are in this same folder." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "LLVM TableGen", + "language": "tablegen", + "name": "tablegen" + }, + "language_info": { + "file_extension": ".td", + "mimetype": "text/x-tablegen", + "name": "tablegen", + "pygments_lexer": "text" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.md b/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.md new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_tutorial_part_1.md @@ -0,0 +1,431 @@ +## Intro to TableGen Part 1: Classes and Defs + +**Note:** The content in this notebook is adapted from https://llvm.org/docs/TableGen/index.html. + +TableGen is a language used in LLVM to automate the generation of certain types of code. Usually repetitive code that has a common structure. TableGen is used to generate "records" that are then processed by a "backend" into domain specific code. + +The compiler for TableGen is the binary `llvm-tblgen`. This contains the logic to convert TableGen source into records that can then be passed to a TableGen backend. + +TableGen allows you to define Classes and Defs (instances of classes) but it doesn't encode what to do with that structure. That's what the "backend" does. The "backend" converts this structure into something useful. For example, C++ code. + +These backends are included in the `llvm-tblgen` binary and you can choose which one to run using a command line option. If you don't choose a backend you get a dump of the structure, and this is what this notebook uses. + +This tutorial will focus on the language itself only. The only thing you need to know now is that in addition to `llvm-tblgen` you will see other `*-tblgen` like `clang-tblgen`. The difference between them is the backends they include. + +The default output from `llvm-tblgen` looks like this: + + +```tablegen +// Empty source file +``` + + ------------- Classes ----------------- + ------------- Defs ----------------- + + +No source means no classes and no defs. Let's add a class. + + +```tablegen +class C {} +``` + + ------------- Classes ----------------- + class C { + } + ------------- Defs ----------------- + + +Followed shortly by defs (or definitions if you prefer). + + +```tablegen +def X: C; +``` + + ------------- Classes ----------------- + class C { + } + ------------- Defs ----------------- + def X { // C + } + + +A def creates an instance of a class. Typically, the main loop of a TableGen backend will look for all defs that are instances of a certain class. + +For example if I am generating register information I would look for all defs that are instances of RegisterInfo in the example below. + + +```tablegen +%reset +class RegisterInfo {} +def X0: RegisterInfo {} +def X1: RegisterInfo {} +``` + + ------------- Classes ----------------- + class RegisterInfo { + } + ------------- Defs ----------------- + def X0 { // RegisterInfo + } + def X1 { // RegisterInfo + } + + +Of course we need some attributes in these classes and defs, which brings me to types. + +TableGen is statically typed (https://llvm.org/docs/TableGen/ProgRef.html#types) with error checking if you try to assign the wrong things. Below are a few of the builtin types. + + +```tablegen +%reset +class C { + int a; + bit b = 0; + string s = "Hello"; +} +``` + + ------------- Classes ----------------- + class C { + int a = ?; + bit b = 0; + string s = "Hello"; + } + ------------- Defs ----------------- + + +Note that you do not have to give a member a default value. + + +```tablegen +def X: C {} +``` + + ------------- Classes ----------------- + class C { + int a = ?; + bit b = 0; + string s = "Hello"; + } + ------------- Defs ----------------- + def X { // C + int a = ?; + bit b = 0; + string s = "Hello"; + } + + +When you make an instance of a class using `def`, that instance gets all the members of the class. Their values will be as set in the class. + +In the case of `a` it also keeps the undefined value. Any backend using that would have to check for that case. + +If we want to override those values we can use `let` (https://llvm.org/docs/TableGen/ProgRef.html#let-override-fields-in-classes-or-records). This can be done in a couple of ways. The first is where you mark the scope of the let using `in {}`. + +Below, I am saying that within the `{}` after the `let`, all `a` should have the value 5. + + +```tablegen +%reset +class C { + int a = 9; +} +let a=5 in { + def X: C {} +} +``` + + ------------- Classes ----------------- + class C { + int a = 9; + } + ------------- Defs ----------------- + def X { // C + int a = 5; + } + + +You can also use `let` within a def. Limiting its scope to the `{}` of the def itself. + + +```tablegen +%reset +class C { + int a = 9; +} +def X: C { + let a=5; +} +``` + + ------------- Classes ----------------- + class C { + int a = 9; + } + ------------- Defs ----------------- + def X { // C + int a = 5; + } + + +As you'd expect, it is an error to try to `let` a name that hasn't been defined or to give it a value of the incorrect type. + + +```tablegen +%reset +class C { + int a = 9; +} +def X: C { + let a="Hello"; +} +``` + + :5:9: error: Field 'a' of type 'int' is incompatible with value '"Hello"' of type 'string' + let a="Hello"; + ^ + + + +```tablegen +%reset +class C { + int a = 9; +} +def X: C { + let b=5; +} +``` + + :5:11: error: Value 'b' unknown! + let b=5; + ^ + + +If you have multiple let, the outer scope is applied first then down to the narrowest. + + +```tablegen +%reset +class C { + int a=9; +} +let a=5 in { + let a=6 in { + def X: C {} + } + def Y: C {} + def Z: C { let a=3; } +} +``` + + ------------- Classes ----------------- + class C { + int a = 9; + } + ------------- Defs ----------------- + def X { // C + int a = 6; + } + def Y { // C + int a = 5; + } + def Z { // C + int a = 3; + } + + +The first let is at what we call the "top level". That means the outer most scope in terms of the source code. A bit like a global variable in a C file. + +That is applied first and changes `a` from `9` to `5`. `X` is within a further let that changes `a` from `5` to `6`. `Y` however is outside that so its `a` remains `5`. + +`Z` takes it a step further with a `let` inside the def. This overrides all the others to make `a` `3` within `Z`. + +That example is overly complex, but only to demonstrate the feature. A practial example is shown below. What if we are describing registers and only some of those registers are 64 bits and the majority are 32 bits? + + +```tablegen +%reset +class Register { + int size=4; +} +let size=8 in { + def X0: Register {} + // Repeats 31 times... +} +def W0: Register {} +// Repeats 31 times... +``` + + ------------- Classes ----------------- + class Register { + int size = 4; + } + ------------- Defs ----------------- + def W0 { // Register + int size = 4; + } + def X0 { // Register + int size = 8; + } + + +(for anyone curious that's AArch64's register naming) + +We start by setting a default value of `size` which is 4 (4x8=32 bits). Then using a top level `let` we override that value and set it to 8 for all the 64 bit registers. + +In addition to the built in types, class members can be user defined types as well. + + +```tablegen +%reset +class Inner {} +class Outer { + Inner i; +} +``` + + ------------- Classes ----------------- + class Inner { + } + class Outer { + Inner i = ?; + } + ------------- Defs ----------------- + + +Of course that raises the question, how do we construct an instance of `Inner` to use as the value? Using a `def` of course! + + +```tablegen +%reset +class Inner {} +def AnInner: Inner {} +class Outer { + Inner i = AnInner; +} +def AnOuter: Outer; +``` + + ------------- Classes ----------------- + class Inner { + } + class Outer { + Inner i = AnInner; + } + ------------- Defs ----------------- + def AnInner { // Inner + } + def AnOuter { // Outer + Inner i = AnInner; + } + + +But what if I wanted to make that a parameter instead of a making a definition up front? This is where class template arguments come in. + +I'm going to switch back to builtin types just to make the example clearer. + + +```tablegen +%reset +class C { + int c = a; + int d = b; +} +def X: C<0, 1> {} +``` + + ------------- Classes ----------------- + class C { + int c = C:a; + int d = C:b; + } + ------------- Defs ----------------- + def X { // C + int c = 0; + int d = 1; + } + + +This is going to look familiar if you have written C++. Here it is in C++ using a templated class. +``` +template +class C { + int c = a; + int d = b; +}; +C<0, 1> X; +``` + +`C` now requires 2 template arguments and type checking applies as it would for a class member. + + +```tablegen +%reset +class C { + int c = a; + int d = b; +} +def X: C<0> {} +``` + + :5:8: error: Value not specified for template argument 'C:b' (#1) of parent class 'C' + def X: C<0> {} + ^ + + + +```tablegen +%reset +class C { + int c = a; + int d = b; +} +def X: C<0, "hello"> {} +``` + + :5:8: error: Value specified for template argument 'C:b' (#1) is of type string; expected type int: "hello" + def X: C<0, "hello"> {} + ^ + + +Using this you can enforce a structure on the user of these classes. In our register example I could enforce that you give a size value for example. + +You can combine these class template arguments with class members that aren't derived from template arguments to implement optional arguments. + +The code below makes the size argument mandatory but the alias optional. Something along the lines of `Register(int size, const char* alias=nullptr);` if it were C++. + + +```tablegen +%reset +class Register { + int size = _size; + string alias = ""; +} +def X0: Register<8> {} +def X29: Register<8> { + let alias="frame pointer"; +} +``` + + ------------- Classes ----------------- + class Register { + int size = Register:_size; + string alias = ""; + } + ------------- Defs ----------------- + def X0 { // Register + int size = 8; + string alias = ""; + } + def X29 { // Register + int size = 8; + string alias = "frame pointer"; + } + + +**Note:** Unlike a c++ constructor you can't reuse the name between the template argument and the class member. +Here I have added `_` to the template argument but there's no required style. + +This concludes part 1 of this tutorial. To learn more advanced features, see the following parts which are in this same folder.