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