1.. Licensed to the Apache Software Foundation (ASF) under one 2 or more contributor license agreements. See the NOTICE file 3 distributed with this work for additional information 4 regarding copyright ownership. The ASF licenses this file 5 to you under the Apache License, Version 2.0 (the 6 "License"); you may not use this file except in compliance 7 with the License. You may obtain a copy of the License at 8 9.. http://www.apache.org/licenses/LICENSE-2.0 10 11.. Unless required by applicable law or agreed to in writing, 12 software distributed under the License is distributed on an 13 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 KIND, either express or implied. See the License for the 15 specific language governing permissions and limitations 16 under the License. 17 18==================== 19Expressions in Relay 20==================== 21 22The Relay IR is a pure, expression-oriented language. The below sections 23describe the different expressions in Relay and give details of their semantics. 24 25Dataflow and Control Fragments 26============================== 27 28For the purposes of comparing Relay to traditional computational graph-based IRs, it 29can be useful to consider Relay expressions in terms of dataflow and control fragments. 30Each portion of a Relay program containing expressions that only affect the dataflow can 31be viewed as a traditional computation graph when writing and expressing transformations. 32 33The dataflow fragment covers the set of Relay expressions that do not involve 34control flow. That is, any portion of a program containing only the following 35constructs corresponds to a pure computation graph: 36 37- `Variables`_ 38- Tuple `Construction`_ and `Projection`_ 39- `Let Bindings`_ 40- `Graph Bindings`_ 41- Calls to `Operators`_ and `ADT Constructors`_ 42 43Control flow expressions allow the graph topology to change 44based on the value of previously executed expressions. The control 45fragment in Relay includes the following constructs: 46 47- `If-Then-Else`_ Expressions 48- `ADT Matching`_ Expressions 49- Recursive Calls in Functions 50 51From the point of view of a computation graph, a function is a subgraph and a function call inlines the subgraph, substituting its arguments for the free variables in the subgraph with corresponding names. 52Thus, if a function's body uses only dataflow constructs, 53a call to that function is in the dataflow fragment; conversely, if the 54function's body contains control flow, a call to that function is not part of the dataflow fragment. 55 56Variables 57========= 58 59Inspired by LLVM, Relay explicitly distinguishes between local and 60global variables both in the AST and in the text format. In the text format, 61global and local variables are distinguished by prefixes, or *sigils*. 62Global variables are prefixed with :code:`@` and local variables with :code:`%`. 63 64This explicit distinction makes certain optimizations easier to implement. 65For example, inlining a global definition requires no analysis: simply 66substituting the definition suffices. 67 68Global Variable 69~~~~~~~~~~~~~~~~~~ 70 71Global identifiers are prefixed by the :code:`@` sigil, such as ":code:`@global`". 72A global identifier always references a globally visible definition contained in the 73globally visible environment, known as the `module <Module and Global Functions_>`__. 74Global identifiers must be unique. 75 76See :py:class:`~tvm.relay.expr.GlobalVar` for its implementation 77and documentation. 78 79Local Variable 80~~~~~~~~~~~~~~ 81 82Local identifiers are prefixed by the :code:`%` sigil, 83such as ":code:`%local`". A local identifier always references 84a function argument or a variable bound in a :code:`let` expression, 85and will be scoped to the function where it appears or the :code:`let` 86expression where it is bound, respectively. 87 88In the below code segment, notice that :code:`%a` is defined twice. This is 89permitted, as in most functional languages; in the scope of the second 90:code:`let` expression, the name :code:`%a` is "shadowed," meaning all 91references to :code:`%a` in the inner scope refer to the later definition, while 92references to :code:`%a` in the outer scope continue to refer to 93the first one. 94 95.. code-block:: python 96 97 let %a = 1; 98 let %b = 2 * %a; // %b = 2 99 let %a = %a + %a; // %a = 2. %a is shadowed 100 %a + %b // has value 2 + 2 = 4 101 102(Note that in Relay's implementation, each definition of a local variable 103creates a new :py:class:`~tvm.relay.expr.Var`, so a shadowed local variable, 104despite having the same name as one in an outer scope, will be a different 105object. This allows for comparing local variables by pointer identity with the 106knowledge that the same local variable object corresponds to a different binding site.) 107 108See :py:class:`~tvm.relay.expr.Var` for its implementation 109and documentation. 110 111Functions 112========= 113 114Functions in Relay act similarly to procedures or functions in 115other programming languages and serve to generalize the concept 116of a named subgraph. 117 118Functions are first class in Relay, which means they are expressions just like variables, constants, and tuples. 119Additionally, functions in Relay are higher-order, which means that a function can be passed as an argument to a 120function or returned by a function, as function expressions evaluate to closures (see the `Closures`_ subsection), 121which are values like tensors and tuples. 122 123See :py:class:`~tvm.relay.expr.Function` for the definition and documentation of function nodes. 124 125Syntax 126~~~~~~ 127 128A definition minimally consists of the keyword :code:`fn`, an empty set of 129parameters, and a body expression (:py:class:`~tvm.relay.expr.Expr`) 130contained by curly braces. 131 132.. code-block:: python 133 134 fn() { body } 135 136A definition may contain any number of parameters. For example, a 137simple function that invokes the :code:`add` operator: 138 139.. code-block:: python 140 141 fn(%x, %y) { add(%x, %y) } 142 143Notice that within the function's body, the parameters are local 144variables, just like those bound in a :code:`let` expression. 145 146One may also annotate explicit types on functions. 147For example, we can restrict the above function to only work 148on certain types: 149 150.. code-block:: python 151 152 fn(%x : Tensor[(10, 10), float32], %y : Tensor[(10, 10), float32]) 153 -> Tensor[(10, 10), float32] { 154 add(%x, %y) 155 } 156 157The above function only takes arguments of type :code:`Tensor[(10, 10), float32]` and returns a value of 158type :code:`Tensor[(10, 10), float32]`. A function parameter is just a local 159variable (:py:class:`~tvm.relay.expr.LocalVar`) optionally annotated with a type, written as :code:`%x : T`. 160 161When the type information is omitted, Relay attempts to infer the most general type 162for the users. This property is known as generalization: for a definition without 163explicit annotations, Relay attempts to assign the most general type to the 164parameters and return type based on the function body and call sites. 165 166A recursive function expression can be defined using a :code:`let` binding, 167as here: 168 169.. code-block:: python 170 171 let %fact = fn(%x : Tensor[(10, 10), float32]) -> Tensor[(10, 10), float32] { 172 if (%x == Constant(0, (10, 10), float32)) { 173 Constant(1, (10, 10), float32) 174 } else { 175 %x * %fact(%x - Constant(1, (10, 10), float32)) 176 } 177 }; 178 %fact(Constant(10, (10, 10), float32)) 179 180Closures 181~~~~~~~~ 182 183A function expression evaluates to a closure. Closures 184are values that are represented as a pair of a local environment 185(storing the values for all variables defined outside the scope 186of the function's body) and the function itself. 187 188For example, in the below example, the final result will be 189a tensor of zero values because the closure for :code:`%f` stores the value of 190:code:`%x` at the pointer where :code:`%f` was defined. 191 192.. code-block:: python 193 194 let %g = fn() { 195 let %x = Constant(0, (10, 10), float32); 196 // %x is a free variable in the below function 197 fn(%y) { %y * %x } 198 }; 199 // the %x in %g's body is not in scope anymore 200 // %f is a closure where %x maps to Constant(0, (10, 10), float32) 201 let %f = %g(); 202 let %x = Constant(1, (10, 10), float32); 203 %f(%x) // evaluates to Constant(0, (10, 10), float32) 204 205Polymorphism and Type Relations 206~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 207 208*Note: type parameter syntax is not yet supported in the text format.* 209 210A function may also be given a set of type parameters, which can be 211substituted for specific types at call sites. Functions with 212type parameters are *type polymorphic*; their return type or the types 213of arguments they will accept can vary based on the type arguments 214given at call sites. 215 216Type parameters are classified by *kind* and can 217only appear in parts of the type signature where their kind is appropriate 218(e.g., type parameters of kind :code:`Shape` can only appear where a shape 219would be expected in a tensor type); for a full discussion, 220see :ref:`the documentation on type parameters <type-parameter>`. 221 222For example, one can define a polymorphic identity function for 223any Relay type as follows: 224 225.. code-block:: python 226 227 fn<t : Type>(%x : t) -> t { 228 %x 229 } 230 231The below definition is also polymorphic, but restricts its 232arguments to tensor types: 233 234.. code-block:: python 235 236 fn<s : Shape, bt : BaseType>(%x : Tensor[s, bt]) { 237 %x 238 } 239 240Notice that the return type is omitted and will be inferred. 241 242*Note: "where" syntax is not yet supported in the text format.* 243 244A function may also be subject to one or more type relations, such as in 245the following: 246 247.. code-block:: python 248 249 fn(%x, %y) where Broadcast { add(%x, %y) } 250 251In the above definition, the types of :code:`%x` and :code:`%y` and the return type 252are subject to the :code:`Broadcast` relation, meaning all three must be tensors 253and their shapes follow the elementwise broadcast relation. As with 254operators, the definitions of relations are not transparent to Relay 255and they are instead implemented externally in either C++ or Python. 256 257As in the case of :code:`Broadcast`, relations are used to express complicated 258constraints on types (especially tensor shapes). 259All function relations must hold at all call sites; 260type checking is thus treated as a constraint-solving problem. 261For more detail on type relations and their implementations, 262please see :ref:`their section in the documentation on Relay's type system <type-relation>`. 263 264Operators 265========= 266 267An operator is a primitive operation, such as :code:`add` or :code:`conv2d`, not defined in the Relay 268language. Operators are declared in the global operator 269registry in C++. Many common operators are backed by TVM's 270Tensor Operator Inventory (`TOPI <https://github.com/apache/incubator-tvm/tree/master/topi>`__). 271 272To register an operator a user must provide an implementation 273of the operator, its type, and any other desired metadata. 274The operator registry is a column-based store where 275operators are keys, so any metadata (which might be referenced 276by optimization passes) may be registered as a new column. 277 278From the perspective of Relay's type system, an operator is a function, 279so operators may be called like any other function and have function 280types. In particular, operator types are registered using a single 281type relation (see :ref:`the documentation on type relations <type-relation>`), typically a relation 282specialized to that operator. For example, the :code:`add` operator 283is registered with the :code:`Broadcast` relation, indicating that the 284arguments of :code:`add` must be tensors and that the return type 285is a tensor whose shape depends on those of its arguments. 286 287Operators are rendered without a sigil (e.g :code:`conv2d`, :code:`flatten`) 288when pretty-printing Relay programs. 289Operators are explicitly contained in the program and are uniquely 290identifiable by pointer. 291 292Note that common arithmetic operators such as :code:`add` and :code:`multiply` 293may be written using the corresponding arithmetic operators in the text format 294(e.g., :code:`+` or :code:`*`) as syntactic sugar. 295 296See :py:class:`~tvm.relay.op.Op` for the definition and documentation 297of operator nodes, demonstrating the infrastructure for registering 298operator metadata. The other files in :py:class:`~tvm.relay.op` give 299handles for generating a call to various pre-registered operators. 300The :ref:`tutorial on adding operators to Relay <relay-add-op>` shows how to add further 301operators into the language. 302 303ADT Constructors 304================ 305 306Algebraic data types (ADTs) in Relay are described in detail in a 307:ref:`separate overview<adt-overview>` and their integration into 308the type system is described :ref:`here<adt-typing>`. 309 310In this section, we will simply note that ADT constructors are given 311a function type and should be used inside call nodes like a function 312or operator. An ADT constructor is defined by giving the name of 313the ADT it constructs (a global type variable) and the types of the 314expected arguments for the constructor. 315 316If the ADT definition includes type variables, those type variables 317may appear in the constructor. Constructors cannot include any other 318type variables. 319 320Let us suppose that :code:`D` is an ADT that takes type parameters 321:code:`a` and :code:`b`. If :code:`C1` is a constructor for :code:`D` 322and expects two arguments, one of type :code:`a` and one of type :code:`b`, then 323:code:`C1` has the following type signature: 324:code:`fun<a, b>(a, b) -> D[a, b]`. (See either the ADT overview 325or the discussion of ADT typing for an explanation of the type call 326in the return type.) 327If another constructor for :code:`D`, :code:`C2`, takes no arguments, 328then it has the following type signature: :code:`fun<a, b>() -> D[a, b]`; 329the type parameters will always appear in the return type. 330 331Once called, a constructor produces an ADT instance, which is a 332container that stores the values of the arguments to the constructor 333as well as the name ("tag") of the constructor. The tag will be used 334for deconstructing the instances and retrieving the values when 335`ADT Matching`_. 336 337See :py:class:`~tvm.relay.adt.Constructor` for the definition and documentation. 338 339Call 340==== 341 342Expressions with function types in Relay are "callable," 343meaning that they can be invoked via a function call. These consist of 344any expression that evaluates to a closure (i.e., function expressions 345or global functions) and Relay operators. 346 347The syntax of calls follows that used in C-like languages, demonstrated in the 348example below: 349 350.. code-block:: python 351 352 let %c = 1; 353 let %f = fn(%x : Tensor[(), float32], %y : Tensor[(), float32]) { %x + %y + %c }; 354 %f(10, 11) 355 356When a closure is called (see `Closures`_), 357the closure's body is evaluated in the stored environment 358(i.e., using the stored values for free variables) with 359local variable bindings added for each argument; the final value 360obtained by evaluating the body is the call's return value. 361Thus, in the above example, the call evaluates to 22. 362In the case of operators, the implementation is opaque to Relay, 363so the result is left up to the registered TVM implementation. 364 365*Note: type parameters are not yet supported in the text format.* 366 367A type-polymorphic function can also include type arguments at a call 368site. The type arguments are substituted for type parameters when 369type checking. If a function is type-polymorphic and type arguments are not 370given, type inference will attempt to infer type arguments if possible. 371The following code gives examples of explicit and inferred type arguments: 372 373.. code-block:: python 374 375 // %f : fn<a : Type, b : Type, c : Type>(a, b) -> c 376 let %x1 = %f<Tensor[(), bool], Tensor[(), bool], Tensor[(), bool)]>(True, False); 377 // %x1 is of type Tensor[(), bool] 378 let %x2 : () = %f(%x1, %x1) 379 // the type arguments in the second call are inferred to be <Tensor[(), bool], Tensor[(), bool], ()> 380 381Note that all type relations in the function type must hold at each 382call site. Specifically, this means that the relation will be checked 383against the specific types of the arguments at a given call site. This 384is also a form of polymorphism, since there may be multiple valid 385assignments of argument types and a return type so long as the relation 386is satisfied. 387 388For example, if we have a function :code:`%f` that takes tensor arguments 389and has the :code:`Broadcast` relation, then there are many different 390shapes that the arguments in the below call could have that would satisfy 391the type annotation: 392 393.. code-block:: python 394 395 let %x : Tensor[(100, 100, 100), float32] = %f(%a, %b); 396 %x 397 398See :py:class:`~tvm.relay.expr.Call` for its definition and documentation. 399 400.. _module-description: 401 402Module and Global Functions 403=========================== 404 405Relay keeps a global data structure known as a "module" (often called an "environment" in other 406functional programming languages) to keep track of the definitions of global functions. 407In particular, the module keeps a globally accessible mapping of global variables to the 408function expressions they denote. The utility of the module is that it allows global functions 409to recursively refer to themselves or any other global function (e.g., as in mutual recursion). 410 411Note Relay's module is analogous to data structures for keeping track of subgraphs in computation 412graph-based IRs. 413 414Global functions in Relay behave identically to the function expressions defined in `Functions`_, 415but have syntactic sugar in the text format to enter their definitions into the module. Namely, 416a global function definition includes a global identifier and is allowed to recursively refer to 417that identifier in the body, as in the following example: 418 419.. code-block:: python 420 421 def @ackermann(%m : Tensor[(), int32], %n : Tensor[(), int32]) -> Tensor[(), int32] { 422 if (%m == 0) { 423 %n + 1 424 } else if (%m > 0 && %n == 0) { 425 @ackermann(%m - 1, 1) 426 } else { 427 @ackermann(%m - 1, @ackermann(%m, %n - 1)) 428 } 429 } 430 431This definition would result in a module entry mapping the identifier :code:`@ackermann` to a function expression 432with the parameters, return type, and body above. Any reference to the identifier :code:`@ackermann` elsewhere in the 433code could then look up the identifier in the module and replace the function definition as needed. 434 435See :py:class:`~tvm.relay.Module` for the definition and documentation of a module. 436 437Constant 438======== 439 440This node represents a constant tensor value 441(see :py:mod:`~tvm.relay.Value` for more details). 442A constant is represented as a :py:class:`~tvm.NDArray`, 443allowing Relay to utilize TVM operators for constant evaluation. 444 445This node can also represent scalar constants, since 446scalars are tensors with a shape of :code:`()`. In the text format, numerical 447and boolean literals are thus syntactic sugar for constants encoding a 448tensor type with a rank-zero shape. 449 450See :py:class:`~tvm.relay.expr.Constant` for its definition and documentation. 451 452Tuples 453====== 454 455Construction 456~~~~~~~~~~~~ 457 458The tuple node builds a finite (that is, of statically known size) sequence of heterogeneous data. 459These tuples match Python's closely, and their fixed length allows for efficient projection of their 460members. 461 462.. code-block:: python 463 464 fn(%a : Tensor[(10, 10), float32], %b : float32, %c : Tensor[(100, 100), float32]) { 465 let %tup = (%a, %b); // type: (Tensor[(10, 10), float32], float32) 466 ((%tup.0 + %tup.1), %c) // type: (Tensor[(10, 10), float32], Tensor[(100, 100), float32]) 467 } 468 469See :py:class:`~tvm.relay.expr.Tuple` for its definition and documentation. 470 471Projection 472~~~~~~~~~~ 473 474A tuple must be indexed by an integer constant in order to extract a 475particular member of the tuple. Projections are 0-indexed. 476 477For example, the below projection evaluates to :code:`%b`: 478 479.. code-block:: python 480 481 (%a, %b, %c).1 482 483See :py:class:`~tvm.relay.expr.TupleGetItem` for its definition and documentation. 484 485Let Bindings 486============ 487 488A :code:`let` binding is an immutable local variable binding, 489allowing the user to bind an expression to a name. 490 491A :code:`let` binding contains a local variable, 492an optional type annotation, a value, and a body expression 493that may reference the bound identifier. If a type annotation 494on the bound variable is omitted, Relay attempts to infer the 495most general type permitted for the variable. 496 497The bound variable in a :code:`let` expression is only in scope 498in its body, except when the variable defines a function expression. 499When a :code:`let` expression creates a function, the variable is also 500in scope in its value to allow for recursively defined functions 501(see the previous subsection). 502 503The value of a :code:`let` binding is the value of the final expression 504after evaluating the bindings it depends on. For example, in the 505following example the entire expression evaluates to a tensor 506of shape :code:`(10, 10)` where all elements are 2: 507 508.. code-block:: python 509 510 let %x : Tensor[(10, 10), float32] = Constant(1, (10, 10), float32); 511 %x + %x 512 513A sequence of :code:`let` bindings can be considered as a dataflow graph, 514where the bindings are a series of sub-graphs connected 515by bound variables. Since these binding sequences are 516pure, a pair of bindings where neither depends on the other can be safely reordered. 517For example, the first and second :code:`let` bindings below 518may be evaluated in either order because neither has a dataflow 519dependency on the other: 520 521.. code-block:: python 522 523 let %x = %a + %b; 524 let %y = %c + %d; 525 %x * %y 526 527See :py:class:`~tvm.relay.expr.Let` for its definition and documentation. 528 529Graph Bindings 530============== 531 532A :code:`let` binding creates a named variable that is bound to the given value 533and scoped to the subsequent expression. By contrast, a graph binding allows for 534explicitly constructing dataflow graphs in a Relay program by binding an expression 535(graph node) directly to a temporary variable, which is not scoped. Each reference 536to the variable corresponds to an edge in the dataflow graph. This has the 537semantics of substituting the expression wherever the variable appears, even though 538the graph node will only be evaluated once by the compiled program. 539 540These bindings allow for a style of programming that corresponds to that already 541employed by NNVM and other dataflow graph-based input formats. The fact that the variables 542are not scoped offers some flexibility in evaluation order compared to :code:`let` 543bindings, though this can also introduce some ambiguity in programs (the 544:ref:`developer introduction to the Relay IR<relay-dev-intro>` includes more detailed discussion 545of this nuance). 546 547*Note: Graph bindings are not currently parsed by the text format.* 548 549In Relay's text format, a graph binding can be written as below (note the lack of a 550:code:`let` keyword and a semicolon): 551 552.. code-block:: python 553 554 %1 = %a + %b 555 %2 = %1 + %1 556 %2 * %2 557 558Unlike a let binding, a graph binding is not represented as an AST node in Relay, but rather as a meta-variable referencing its AST node value. 559For example, a program like the above could be constructed in Relay's 560Python front-end by setting *Python variables* equal to the corresponding Relay AST node and 561using the variables repeatedly, as below (a C++ program using the corresponding API bindings 562could accomplish the same thing): 563 564.. code-block:: python 565 566 sum1 = relay.add(a, b) 567 sum2 = relay.add(sum1, sum1) 568 relay.multiply(sum2, sum2) 569 570For development purposes and to enable certain optimizations, Relay includes passes to 571convert between dataflow graphs defined using graph bindings and programs with :code:`let` 572bindings in A-normal form, employed by many compiler optimizations from the functional 573programming community (see `"A-Normalization: Why and How" by 574Matt Might <http://matt.might.net/articles/a-normalization/>`__ for an introduction 575to A-normal form). 576 577If-Then-Else 578============ 579 580Relay has a simple if-then-else expression that allows programs to branch 581on a single value of type :code:`bool`, i.e., a zero-rank 582tensor of booleans (:code:`Tensor[(), bool]`). 583 584.. code-block:: python 585 586 if (%t == %u) { 587 %t 588 } else { 589 %u 590 } 591 592Since if-then-else branches are expressions, they may appear inline 593wherever any other expression may be expected, like invocations of 594the ternary operator in C-like languages. The if-then-else expression 595evaluates to the value of the "then" branch if the condition value 596evaluates to :code:`True` and evaluates to the value of the "else" branch if the 597condition value evaluates to :code:`False`. 598 599See :py:class:`~tvm.relay.expr.If` for its definition and documentation. 600 601ADT Matching 602============ 603 604Instances of algebraic data types (ADTs), as discussed in the 605:ref:`ADT overview<adt-overview>`, are containers that store the 606arguments passed to the constructor used to create them, tagged by 607the constructor name. 608 609Match expressions in Relay allow for retrieving the values stored in 610an ADT instance ("deconstructing" it) based on their constructor tag. 611A match expression behaves similarly to a C-style :code:`switch` statement, 612branching on the different possible constructors for the type of the 613value being deconstructed. As the ADT overview details, match 614expressions are capable of more general pattern-matching than simply 615splitting by constructors: any ADT instance nested inside an instance 616(e.g., a list of lists) can be deconstructed at the same time as 617the outer instance, while the different fields of the instance can be 618bound to variables. (See :ref:`this section<adt-pattern>` for a detailed 619description of ADT pattern-matching.) 620 621A match expression is defined using the 622input value (an expression) and a list of clauses, each of which 623consists of a pattern and an expression. When executed, the *first* 624clause whose pattern matches the structure of the queried value is 625executed; the clause expression is evaluated and returned. 626 627For example, suppose we have an ADT for natural numbers: 628 629.. code-block:: python 630 631 data Nat { 632 Z : () -> Nat # zero 633 S : (Nat) -> Nat # successor (+1) to a nat 634 } 635 636Then the following function subtracts one from a passed nat: 637 638.. code-block:: python 639 640 fn(%v: Nat[]) -> Nat[] { 641 match(%v) { 642 case Z() { Z() } 643 case S(%n) { %n } # the variable %n is bound in the scope of this clause 644 } 645 } 646 647The following function subtracts two from its argument if it is at least 648two and returns the argument otherwise, using a nested constructor pattern: 649 650.. code-block:: python 651 652 fn(%v : Nat[]) -> Nat[] { 653 match(%v) { 654 case S(S(%n)) { %n } 655 # wildcard pattern: matches all cases not matched already 656 case _ { %v } 657 } 658 } 659 660As aforementioned, the ordering of match clauses is relevant. 661In the below example, the first clause will always match so 662those below it can never run: 663 664.. code-block:: python 665 666 fn(%v : Nat[]) -> Nat[] { 667 match(%v) { 668 case _ { %v } 669 case S(S(%n)) { S(%n) } 670 case S(%n) { %n } 671 case Z() { S(Z()) } 672 } 673 } 674 675See :py:class:`~tvm.relay.adt.Match` for its definition and documentation. 676 677TempExprs 678========= 679 680Program transformations (passes) in Relay may require inserting temporary 681state into the program AST to guide further transformations. The 682:code:`TempExpr` node is provided as a utility to developers for this purpose; 683nodes inheriting from :code:`TempExpr` cannot appear directly in user-provided 684code but may be inserted in a pass. Any :code:`TempExpr` created in a pass 685should ideally be eliminated before the pass is complete, as a 686:code:`TempExpr` only stores internal state and has no semantics of its own. 687 688For an example of :code:`TempExpr` being used in a pass, 689see :code:`src/relay/pass/alter_op_layout.cc`, which uses :code:`TempExpr` nodes 690to store information about operator layouts as the pass tries to rearrange operator 691calls. 692 693See :py:class:`~tvm.relay.expr.TempExpr` for its definition and documentation. 694