Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] TVMScript Metaprogramming #79

Merged
merged 12 commits into from
Aug 4, 2022
398 changes: 398 additions & 0 deletions rfcs/0079-tvmscript-metaprogramming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,398 @@
- Feature Name: tvmscript-metaprogramming
- Start Date: 2022-06-16
- RFC PR: [apache/tvm-rfcs#79](https://github.com/apache/tvm-rfcs/pull/79)
- GitHub Issue: [apache/tvm#0000](https://github.com/apache/tvm/issues/0000)
- Co-Authors: Yaxing Cai ([**@cyx-6**](https://github.com/cyx-6), main implementation), Lite Ye
([**@yelite**](https://github.com/yelite)), Yong Wu
([**@yongwww**](https://github.com/yongwww)), Yuchen Jin
([**@YuchenJin**](https://github.com/YuchenJin)), Eric Lunderberg
([**@Lunderberg**](https://github.com/Lunderberg)), Masahiro Masuda
([**@masahi**](https://github.com/masahi)), Junru Shao
([**@junrushao1994**](https://github.com/junrushao1994), main designer)

# Summary
[summary]: #summary

This RFC proposes a new TVMScript parser infrastructure, supporting extensive
metaprogramming and syntactic sugars. The new infrastructure is IR-agnostic,
treating TIR just as one of dialects. Additionally, the new infrastructure will
provide better tooling around Python ecosystem (pylint, mypy, etc.).

# Motivation
[motivation]: #motivation

**What is TVMScript**.
Check [Blitz Course to TensorIR](https://tvm.apache.org/docs/tutorial/tensor_ir_blitz_course.html) and
[TVMScript Unified Printer RFC](https://github.com/apache/tvm-rfcs/pull/74/files#diff-6965a40ad8df7618ae68e11c88f924542a506c74a931cc3011ae9f99989b5f51R20-R26)
for an introduction into TVMScript.

**What is metaprogramming.** In the context of TVMScript, metaprogramming means
a programmable way to control IR generation. For example, in
https://github.com/apache/tvm/pull/11097, a metaprogramming feature was added
to the TVMScript parser, allows users to programmably control the shapes of the
input buffers of a `PrimFunc`.

### Limitation of current design

The current parser lacks capability on generic metaprogramming that allows user
to have more control on IR construction. This makes it challenging to support
operators like NMS (non-maximum suppression, which is crucial to object
detection model). There is an implementation of NMS at
[python/tvm/topi/cuda/nms.py#L367-L386](https://github.com/apache/tvm/blob/d0650bad66d0ff89a01347537021bc442a98c223/python/tvm/topi/cuda/nms.py#L367-L386).
The implementation of NMS-like operators requires rank-polymorphism and the
ability to interleave host program with TVMScript, which is difficult to be
implemented under the current design.

TVMScript also needs reasonable support on Python tooling. Currently it doesn’t
play nicely with pylint and mypy. For example,
[test_meta_schedule_postproc_rewrite_tensorize.py](https://github.com/apache/tvm/blob/d0650bad66d0ff89a01347537021bc442a98c223/tests/python/unittest/test_meta_schedule_postproc_rewrite_tensorize.py)
has 100+ warnings from pylint within only 500 hundred lines of code. This
creates confusion to the user and leaves an impression that TVMScript isn’t a
mature product and not production-ready. Even though it’s something that can be
incrementally improved under the current design, we believe it’s easier to get
an ideal result if we have a design with the tooling support in mind.

The current design also lacks of unified approach for different IRs. At
[https://github.com/tlc-pack/relax/tree/relax/python/tvm/script/relax](https://github.com/tlc-pack/relax/tree/relax/python/tvm/script/relax),
a mature implementation of TVMScript parser is maintained for Relax. But it’s
hard to extend if we want to support more IRs for TVM unity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think talking about more IRs without context might sound a bit scary to some readers...perhaps channeling the spirit of YAGNI we should avoid over-generalizing here. I think having 1 parser for Relax and TIR is quite reasonable; beyond that we might consider waiting til we have more details.

Copy link
Member

@junrushao junrushao Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @areusch for your feedback! The technique itself could sound like scary or over-generalized, but the fact is it's a better implementation with better maintainability:

  1. It's extremely simple to implement. Concretely speaking, at the time of writing, the core parser is 176 lines of code, which now is working for existing cases without problem. Compared with the current monolithic parser which has 1370 lines of code, it's significantly shorter.
  2. It's good for tooling, particularly, unittesting, documentation and ease to add more sugars. Currently the monolithic parser is extremely hard to be unittested, because it's impossible to split a specific chunk of parsing logic out, and thus all tests are end-to-end tests, for example, this 1k-line "unit"-test is not quite "unit". With our new design, every part of the logic can be tested separately, and almost all sugars don't need to go into changing the parser, but instead adding more functions on the IRBuilder side just suffice.
  3. The new design provides much better clarity and readability.


To conclude, with this RFC, we want to:

1. Add more metaprogramming features to TVMScript, making it easier for TVM
developers to write complicated operators.
2. Improve tooling and documentation of TVMScript, reducing the friction for an
tkonolige marked this conversation as resolved.
Show resolved Hide resolved
average machine learning practitioner to use TVMScript.
3. Modularize and infrastructuralize the TVMScript parser, lowering the cost to
implement parser for new IR.


# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

## Metaprogramming features to support

### (F1) Template Metaprogramming

Users should be able to use variables from outer scope in the TVMScript
function/class. The parsed result should be identical to function/class with
the variable replaced by its value. For instance,

```python
@T.prim_func
def matmul(
A: T.Buffer[(128, 128)],
) -> None:
...

def gen_matmul(n, m) -> None:
@T.prim_func
def f(A: T.Buffer[(n, m)]):
...
return f

f = gen_matmul(n=128, m=128) # `f` should be identical to `matmul`
```

This is already partially supported by https://github.com/apache/tvm/pull/11097
for using `PrimExpr` captured by outer function. With the new parser, we want
to support this feature in more places and with more variable types.

### (F2) Rank-polymorphism

Users should be able to write a single function to handle different ranks of
input buffers (different numbers of dimensions). For example, user should be
able to write a generic function to do broadcast add,

```python
def broadcast_add(a, b, c):
@T.prim_func
def f(
A: T.BufferFrom(a),
B: T.BufferFrom(b),
C: T.BufferFrom(c),
) -> None:
for i, i_a, i_b in T.some_broadcast_method(A.shape, B.shape):
with T.block():
C[*i] = A[*i_a] + B[*i_b]

broadcast_add(
a = Buffer((128, 1), "float32"),
b = Buffer((1, 128), "float32"),
c = Buffer((128, 128), "float32"),
)
```

### (F3) Sugar: TE Compute in TIR

Users should be able to replace boilerplate code with a function call, which’s
expanded to large chunk of code during parsing. For example, we may want to use
TE’s compute-like syntax to replace nested loop,

```python
@T.prim_func
def te_compute_sugar(
A: T.Buffer[(128, 128)],
B: T.Buffer[(128, 128)],
) -> None:
...
C = T.compute((128, 128), lambda i, j: A[i, j] + B[i, j])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious if you may want to apply rewrite rules here, how would that look? how big should we go here? is it worth including this as "metaprogramming" coupled with the parser, or should we simply make it possible to "sew" fragments of TVMScript together, whether they are hand-written or machine-generated?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious if you may want to apply rewrite rules here, how would that look? how big should we go here?
Just to make sure I understand the question correctly - by rewrite rule, you mean rewriting the python AST?

If so, the answer is no. To implement such a feature, there is absolutely no need to change the core parser at all. We only need to add a method in the IRBuilder:

def compute(shape, f_compute) -> None: ...

which will internally call other methods the IRBuilder offers, for example in our case is:

# note it can be implemented in either C++ or python without the parser to do anything
C = T.alloc_buffer(*shape)
with T.grid(*shape) as loop_vars:
  with T.block("some_name"):
    T.buffer_store(C, shape, f_compute(*loop_vars))

...

## expands to ====>

@T.prim_func
def te_compute_expanded(
A: T.Buffer[(128, 128)],
B: T.Buffer[(128, 128)],
) -> None:
...
for i in range(128):
for j in range(128):
with T.block("..."):
C[i, j] = A[i, j] + B[i, j]
...
```

### (F4) Interleave host program and TVMScript program to customize metaprogramming

As an escape hatch from writing code to be parsed by the TVMScript
parser, users should be able to write imperative code to construct IR nodes
tkonolige marked this conversation as resolved.
Show resolved Hide resolved
directly and embed it inside regular TVMScript. Those code will be evaluated
by the Python interpreter when parsing. This gives users the ultimate tool when
TVMScript isn’t expressible enough for their use cases. For example, at
[python/tvm/topi/vision/nms.py#L380-L431](https://github.com/apache/tvm/blob/3cb4597ed48360e3f3d80161d1c03f833072d28e/python/tvm/topi/vision/nms.py#L380-L431),
there are blocks of repetitive code on computing the coordinates of the four
corners of bounding box. This can be simplified as:

```python
# Before, without IRBuilder interleaving
@T.prim_func
def nms(...):
...
for i in range(batch_size):
...
a_l = min(
output[batch_idx, box_a_idx, box_start_idx],
output[batch_idx, box_a_idx, box_start_idx + 2],
)
a_t = min(
output[batch_idx, box_a_idx, box_start_idx + 1],
output[batch_idx, box_a_idx, box_start_idx + 3],
)
a_r = max(
output[batch_idx, box_a_idx, box_start_idx],
output[batch_idx, box_a_idx, box_start_idx + 2],
)
a_b = max(
output[batch_idx, box_a_idx, box_start_idx + 1],
output[batch_idx, box_a_idx, box_start_idx + 3],
)
...
for k in range(j):
check_iou = ...
...
if check_iou > 0:
# b_l: left, b_t: top, b_r: right, b_b: bottom
b_l = min(
output[batch_idx, box_b_idx, box_start_idx],
output[batch_idx, box_b_idx, box_start_idx + 2],
)
b_t = min(
output[batch_idx, box_b_idx, box_start_idx + 1],
output[batch_idx, box_b_idx, box_start_idx + 3],
)
b_r = max(
output[batch_idx, box_b_idx, box_start_idx],
output[batch_idx, box_b_idx, box_start_idx + 2],
)
b_b = max(
output[batch_idx, box_b_idx, box_start_idx + 1],
output[batch_idx, box_b_idx, box_start_idx + 3],
)
...

# With IRBuilder interleaving:

from tvm.script import tir as T

def get_box_coordinates(output, batch_idx, box_idx, box_start_idx):
"""a method executed by python interpreter"""
box_l = T.min(
output[batch_idx, box_idx, box_start_idx],
output[batch_idx, box_idx, box_start_idx + 2],
) # type(box_l) is PrimExpr
... # Repeat for other coordinates
return box_l, box_t, box_r, box_b

@T.prim_func(capture=[get_box_coordinates])
def nms(...):
...
for i in range(batch_size):
...
a_l, a_t, a_r, a_b = get_box_coordinates(output, batch_idx, box_a_idx, box_start_idx)
...
for k in range(j):
check_iou = ...
...
if check_iou > 0:
b_l, b_t, b_r, b_b = get_box_coordinates(output, batch_idx, box_b_idx, box_start_idx)
...
```

# Reference-level explanation
tkonolige marked this conversation as resolved.
Show resolved Hide resolved
[reference-level-explanation]: #reference-level-explanation

## IRBuilder as Core

As the foundation of IR construction, we will provide a set of APIs called
IRBuilder to let user construct IR imperatively. IRBuilder will be used by the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a separate IRBuilder from tvm.tir.ir_builder. I'd suggest we change the name to not confuse users. Better yet, we could unify it with tvm.tir.ir_builder so that we have only two ways of writing TIR (TVMScript, tvm.tir.ir_builder) instead of three (TVMScript, tvm.tir.ir_builder, IRBuilder).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IRBuilder module will be tvm.script.builder and the entry point will be named as Builder, to avoid confusion with the old tvm.tir.ir_builder.

Also as in our private discussion,

in the long run, we will deprecate tvm.tir.ir_builder, which is already less maintained to support latest TIR features, and migrate to TVMScript’s IRBuilder

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the long run, we will deprecate tvm.tir.ir_builder, which is already less maintained to support latest TIR features, and migrate to TVMScript’s IRBuilder

Can you add this to the RFC?

parser, as well as by users directly as described in the feature F4. IRBuilder
allows user to write code in a style that’s similar to TVMScript, while it’s
being executed as host program. For example,

```python
from tvm.script.builder import Builder, def_, def_many
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the feature set of tvm.script.builder compare to tvm.tir.ir_builder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tvm.script.builder is designed to support all features of TIR and it has a minimal core part that's IR agnostic. This will scale the development of IRBuilder API in other IR (Relax). The new IRBuilder also has all implementation in C++.

from tvm.script import tir as T

with Builder() as b:
with T.prim_func():
T.func_name("main")
buffer_a = T.Buffer((128, 128, 128), "float32")
buffer_b = T.Buffer((128, 128, 128), "float32")
arg_a = T.arg("A", buffer_a)
arg_b = T.arg("B", buffer_b)
with T.grid(128, 128, 128) as (i, j, k):
def_many(["i", "j", "k"], [i, j, k])
with T.block(name="block"):
vi = def_("vi", T.axis.spatial(128, i))
vj = def_("vj", T.axis.spatial(128, j))
vk = def_("vk", T.axis.reduce(128, k))

f = b.get() # f is a PrimFunc
```

produces similar result to
yelite marked this conversation as resolved.
Show resolved Hide resolved

```python
@T.prim_func
def main(
A: T.Buffer(shape=(128, 128, 128), dtype="float32"),
B: T.Buffer(shape=(128, 128, 128), dtype="float32"),
) -> None:
for i, j, k in T.grid(128, 128, 128):
with T.block("block"):
vi = T.axis.S(128, i)
vj = T.axis.S(128, j)
vk = T.axis.R(128, k)
```

The implementation of IRBuilder will be in C++ so that it can be used in an
environment without Python. Python binding will be created to expose IRBuilder
Comment on lines +295 to +296
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good motivation for having IRBuilder in C++ is that it could be used with other languages like rust.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and this complement to the design of Doc and DocPrinter in the printer RFC https://github.com/apache/tvm-rfcs/blob/main/rfcs/0074-tvmscript-unified-printer.md. We will have the flexibility to parse from and print to a different language if use cases emerge.

to TVMScript parser.

## Parse-time evaluation

To support metaprogramming, the TVMScript parser needs to evaluate the
expression using Python interpreter. All metaprogramming
features we discussed above can be implemented through this parse-time
evaluation. Using the same `gen_matmul` example from F1,

```python
def gen_matmul(n, m) -> None:
@T.prim_func
def f(A: T.Buffer[(n, m)]):
...
return f
```

What parser does here is to:

1. Collect the environment inside `gen_matmul`, getting a dictionary
1. All primitive types will be captured automatically, while advanced types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you list the types that are supported for capture?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it's updated. It will auto capture int, str, float and None.

(like function) needs to be explicitly declared in the decorator to be
captured (for example, `@T.prim_func(capture=[get_box_coordinates])`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more consistent to capture all values explicitly. It avoids the problem of users accidentally capturing a variable without knowing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an ideal world we would want to capture everything implicitly, to match the closure behavior in Python. However, due to the usage of eval and exec, we want to limit the auto capture to those types that's absolutely safe to mitigate security concern.

We will provide detailed error message if user tries to use a variable that should have been explicitly captured, to compensate the potential errors caused by this inconsistency.

2. Call the corresponding function from IRBuilder, as the parser visits the AST
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, what you are saying here is that the parser will not construct AST nodes directly and it will instead use the new IRBuilder infrastructure to construct the AST?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the IRBuilder is responsible for constructing the IR graph and the parser can be considered as a thin layer built on the IRBuilder.

I updated the RFC to better clarify this.

of function `f`.
1. When visiting the function argument, call `eval` on its type annotation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing to watch out for is that type annotations can be str to avoid circular imports here

Copy link
Member

@junrushao junrushao Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's definitely a good point in general. In our particular case, our type system is rather restrictive to those only TVM IR could represent (mostly tir.IterVar, tir.Var, tir.Buffer, etc), so it shouldn't be a concern here

with the environment captured in the first step.
2. `T.Buffer[(n, m)]` gets evaluated to a value with type `tir.Buffer`.
3. Call the IRBuilder API `T.arg("A", buffer)` to add an arg to the function
that’s being constructed

Another example,

```python
for *i in T.grid(*A.shape):
...
```

The parser will:

1. Evaluate the expression `T.grid(*A.shape)` by the step described above.
`T.grid` returns a value that is nearly equivalent to `List[Var]`.
2. Call `exec` on a specially constructed statement `*i = __tvm_rhs_var__`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exec is an incredibly powerful function and we should be careful using it. Is there a reason we can't bind the results directly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it'd be helpful to note here that using exec means folks would need to essentially strip this code out from places that were productionizing TVM, as exec is generally a security no-no and requires special review.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good question. The original RFC indeed lacks some discussion on the necessity and security implication behind the usage of eval and exec. I updated the RFC to include a section for it.

To answer this comment directly, the left hand side of assignment can get very complicated, like

a, (b, *c, _), d = rhs

It's much easier to use exec, compared to writing our own small Python interpreter. Since the tokens on the LHS comes from user's code without modification, and the auto capturing behavior is limited, the usage of exec here does not create additional security risk for user.

using exec means folks would need to essentially strip this code out from places that were productionizing TVM

I think it's a little bit far-fetched. I agree that the usage of eval and exec needs strong rationale and careful security review. Also using them for untrusted input is a big security hole. But there are still legitimate use cases. In our case the input comes from user's code directly, which should be considered safe. There is a similar usage of exec in torch.jit https://github.com/pytorch/pytorch/blob/master/torch/jit/frontend.py#L465 (constructing a function from template with user's input).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting discussion and both sides seem to have valid points - eval and exec can be very useful but they are susceptible to misuse. If they are irreplaceable, can we provide more guideline (e.g., good/bad use-cases) to avoid the misuses?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be quite challenging to have a comprehensive guideline over the usage of eval and exec. In our particular case, the guideline is to always limit the input of eval and exec to fragments of user's code (or the equivalent snippet, like <lhs> = __tvm_rhs_var__). By doing this, we eliminate the possibility that unknown code is executed without user's awareness.

Another effect of using eval is that the timing of the evaluation changes, an extreme example would be (although not something people will write in TVMScript) :

@T.prim_func
def f():
    ...
    if False:
        nuke_home_directory()
    ...

User would expect:

  1. Nothing from f will run if f is not called by other code
  2. nuke_home_directory will not be called because it's dead code

To minimize the surprise to users, we ask user to explicitly capture those function before they can be evaluated in the parser, to make it clear that the timing of evaluation is different than ordinary Python code.

with `locals` that maps `__tvm_rhs_var__` to the value evaluated in step 1.
3. Collect the value of `i` from the `locals` dictionary
4. Call the IRBuilder API `def_many(["i"], [i])`

By running `eval` and `exec` provided by the Python interpreter, we can implement
yelite marked this conversation as resolved.
Show resolved Hide resolved
language features which are difficult to implement manually, and also make sure
TVMScript has the same semantics on expression compared to regular Python code.

## Parser Registration

The logic of how to process a particular Python syntax node is registered
through decorator. For example,
Comment on lines +402 to +403
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why we want to register parsers for specific fragments instead of calling them explicitly. Using a registration mechanism makes it a lot harder to understand the codebase. Registrations can be split over multiple files and you have to go on a wild chase through the codebase to find what you are looking for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed sometimes registration results in code being split over many files and make it hard to trace how things are connected together. In our case though, all registered parsing function for a particular IR will be centralized in a single file. Another benefit of not calling them directly is that it's easier to test individual part of the parser.


```python
@dispatch.register(token="tir", type_name="For")
def visit_for(self: Parser, node: doc.For) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused at the type signature here. Shouldn't the visitor return an AST fragment so that the visit statements can be use recursively?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser is a thin wrapper of the IRBuilder API and doesn't directly construct IR graph. IRBuilder uses thread local store for builder state so there is no need to return things from visit methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why the granularity for registering parsers is at the individual token level and not at the language level. Your example here is

@dispatch.register(token="tir", type_name="For")
 def visit_for(self: Parser, node: doc.For) -> None:
    ...

But I'd expect

@dispatch.register(language="tir")
class TIRParser:
    def parse_for(...):
        ...
    def parse_with(...):
        ...

I don't understand why you would need the extension point of the language to be on a per token basis.

...
```

handles the transformation of Python `For` node. The `token="tir"` in the
decorator means that the handler is for TIR. `self: Parser` has all the
infrastructural API and maintains a stack of `token` to determine which
function to dispatch to. This makes embedding different IR possible (for
example, embedding TIR in Relax). The folder structure will look like

```
python/tvm/script/
└── parser
   ├── core
   │   └── ... # Parser infrastructure
   ├── tir # TIR dialect
   │   └── ...
└── relax # Hypothetical Relax dialect (not part of our RFC)
└── ...
```


# Drawbacks
[drawbacks]: #drawbacks

N/A

# Rationale and alternatives
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are definitely different approaches for doing meta programming like lisp macro vs c macros. Could you add a little discussion on that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the design constraint is that TVMScript should not deviate from Python. All features proposed in this RFC follows Python's semantics and doesn't create foreign syntax or language features. Neither lisp macro or C macro is relevant to the design here.

I updated the RFC to include a discussion on metaprogramming features from Taichi and Triton per your other feedback.

[rationale-and-alternatives]: #rationale-and-alternatives

N/A

# Prior art
[prior-art]: #prior-art
tkonolige marked this conversation as resolved.
Show resolved Hide resolved

- Hybrid Script: [https://tvm.apache.org/docs/reference/langref/hybrid_script.html](https://tvm.apache.org/docs/reference/langref/hybrid_script.html)
- RFC for TVMScript: [https://discuss.tvm.apache.org/t/rfc-hybrid-script-support-for-tir/7516](https://discuss.tvm.apache.org/t/rfc-hybrid-script-support-for-tir/7516)
- Taichi: [https://www.taichi-lang.org](https://www.taichi-lang.org/)
- Triton: [http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf](http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf)

# Unresolved questions
[unresolved-questions]: #unresolved-questions

N/A

# Future possibilities
[future-possibilities]: #future-possibilities

N/A