You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tvm.apache.org by Ziheng Jiang via Apache TVM Discuss <no...@discuss.tvm.ai> on 2020/09/16 21:17:52 UTC

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL


## Introduction

[TVM Object system]([https://tvm.apache.org/docs/dev/runtime.html#tvm-object-and-compiler-stack](https://tvm.apache.org/docs/dev/runtime.html#tvm-object-and-compiler-stack)) provides a convenient and decent way to share objects between backend (C++) and frontend (Python/Java/Rust/etc.). For example, one can construct a variable in Python and pass it to functions written in C++, and vice versa.

However, adding one object node into TVM stack requires manually adding lines of code to different places in both Python and C++. For example, here’s how `tvm::tir::IntImm` is implemented and registered,

- Definition for Node and its Reference: [https://github.com/apache/incubator-tvm/blob/master/include/tvm/ir/expr.h#L228-L270](https://github.com/apache/incubator-tvm/blob/master/include/tvm/ir/expr.h#L228-L270)
- Implement functionality: [https://github.com/apache/incubator-tvm/blob/master/src/ir/expr.cc#L58-L68](https://github.com/apache/incubator-tvm/blob/master/src/ir/expr.cc#L58-L68)
- Node registry in C++: [https://github.com/apache/incubator-tvm/blob/master/src/ir/expr.cc#L70-L84](https://github.com/apache/incubator-tvm/blob/master/src/ir/expr.cc#L70-L84)
- Node registry in Frontend (Python): [https://github.com/apache/incubator-tvm/blob/master/python/tvm/tir/expr.py#L275-L290](https://github.com/apache/incubator-tvm/blob/master/python/tvm/tir/expr.py#L275-L290)

This RFC advocates the approach to generate C++ implement directly from Python class definition and registry. Moreover, as we still allow users to write C++ code manually in order to bring in more complex features, the object transpiler will provide basic validation for these manually written C++ code.

Here is an example of how an object can be described in Python and how the generated C++ code looks like:

```python
@declare
class BaseExprNode(Object):
    """
    Base type of all the expression.

    See Also
    --------
    BaseExpr
    """
    type_key = "BaseExpr"
    default_visit_attrs = False
    default_sequal_reduce = False
    default_shash_reduce = False

@declare
class IntImmNode(PrimExprNode):
    """
    Constant integer literals in the program.

    See Also
    --------
    IntImm

    Attributes
    ----------
    value
        The internal value.
    """
    type_key = "IntImm"
    value: ty.int64_t
```

```cpp

/*!
 * \brief Base type of all the expressions.
 * \sa Expr
 */
class BaseExprNode : public Object {
 public:
  TVM_DECLARE_BASE_OBJECT_INFO(BaseExprNode, Object);
};

/*!
 * \brief Managed reference to BaseExprNode.
 * \sa BaseExprNode
 */
class BaseExpr : public ObjectRef {
 public:
  TVM_DEFINE_OBJECT_REF_METHODS(BaseExpr, ObjectRef, BaseExprNode);
};

/*!
 * \brief Constant integer literals in the program.
 */
class IntImmNode : public PrimExprNode {
 public:
  /*! \brief The internal value. */
  int64_t value;
  void VisitAttrs(AttrVisitor* v) {
    v->Visit("dtype", &dtype);
    v->Visit("value", &value);
  }
  void SEqualReduce(const IntImmNode* other, SEqualReducer equal) const {
    return equal(dtype, other->dtype) && equal(value, other->value)
  }
  void SHashReduce(SHashReducer hash_reducer) const {
    hash_reducer(dtype);
    hash_reducer(value);
  }
  static constexpr const char* _type_key = "IntImm";
  TVM_DECLARE_BASE_OBJECT_INFO(IntImmNode, PrimExprNode);
};

/*!
 * \brief Managed reference class to IntImmNode.
 *
 * \sa IntImmNode
 */
class IntImm : public PrimExpr {
 public:
  TVM_DEFINE_OBJECT_REF_METHODS(IntImm, PrimExpr, IntImmNode);
};
```

We name it as TVM Object Schema DSL, or tschema. In summary, tschema will bring several benefits for the TVM architecture:

- Reduce boilerplate code;
- Verify to avoid missing some definition like `TVM_REGISTER(...)`;
- Enable deployment on all kinds environment even without C++;
- Fields like `type_child_slots` can be automatically generate for optimizing;
- Allow users to define Objects in Python, build and export them to a .o/.so file;
- Have more type information during runtime, enable some optimizations in TIR compilation;

## High-level Object Compilation Pipeline

- Define TVM Object in Python. This object definition Python file is in a seperate directory (which will not be a part of PYTHONPATH) other than python/tvm/
- Run python class parser to generate related .h, .cc files. This step can be triggered manually or via cmake. The generated files will be checked into the code base so that code completion tools can locate them.
- Compile TVM using cmake as usual.

Notice that the second step happens during (or before) compiling TVM itself. We provide a standalone tool to parse the Python code.

## TSchema DSL

Take `IntImm` as an example, the

```python
@declare
class IntImmNode(PrimExprNode):
    """
    Constant integer literals in the program.

    See Also
    --------
    IntImm

    Attributes
    ----------
    value
        The internal value.
    """
    type_key = "IntImm"
    value: ty.int64_t
```

There are several things require to be parsed,

- Object name. In the above example it is `IntImmNode`, therefore `class IntImmNode` (extends Object) will be generated.
- Type key. In the above example it is `IntImm`, therefore `class IntImm` (extends ObjectRef) will be generated.
- Parent class. In the above example it is `PrimExprNode`
- Member variables. In the above example they are,
    - `value` and its type annotation `int64_t`
- The constructor arguments in C++ will be generated as the same order of the arguments in Python class definition.
- We also will generate default `VisitAttrs`, `SEqualReduce`, `SHashReduce`  methods unless user specify `default_visit_attrs` as `False`.

## **Inplace C++ Source File Modification**

As we mentioned before, there are cases where users need to implement complex functions manually. To leverage the convenience of Python declaration and automatic code generation in such cases, we provide an option to modify the C++ source file in-place, and give users the control to specify which part of the file can be modified.

We provide comment parser for .h and .cc file, in which users can wrap the auto-generated section by comments, e.g.,

```
// tschema: ObjectName

The lines between tschema: ObjectName and tschema: end
will be manipulated by tschema

// tschema: custom-begin

User can also mark sections which should be left unchanged by objgen
This section will be inserted at the end of the class definition,
right before the close brace

// tschema: custom-end
// tschema: end
```

Here is also an example for it:

**Before generation**

```cpp
// tschema: GlobalVarNode
// tschema: custom-begin
bool SEqualReduce(const GlobalVarNode* other, SEqualReducer equal) const {
  return equal(name_hint, other->name_hint) && equal.FreeVarEqualImpl(this, other)
}
bool SHashReduce(SHashReducer hash_reducer) const {
  hash_reduce(name_hint);
  hash_reduce.FreeVarHashImpl(this);
}
// tschema: custom-end
// tschema: end
```

**TSchema Definition**

```cpp
@declare
class GlobalVarNode(RelayExprNode):
    """
    Global variable that lives in the top-level module.

    A GlobalVar only refers to function definitions.
    This is used to enable recursive calls between function.

    See Also
    --------
    GlobalVarNode

    Attributes
    ----------
    name_hint
        The name of the variable, this only acts as a hint.
    """
    type_key = "GlobalVar"
    default_sequal_reduce = False
    default_shash_reduce = False
    name_hint: ty.String
```

**Generated Code**

```cpp
// tschema: GlobalVarNode
class GlobalVarNode : public RelayExprNode {
 public:
  String name_hint;
  void VisitAttrs(AttrVisitor* v) {
    v->Visit("span", &span);
    v->Visit("checked_type_", &checked_type_);
    v->Visit("name_hint", &name_hint);
  }
  static constexpr const char* _type_key = "GlobalVar";
  TVM_DECLARE_BASE_OBJECT_INFO(GlobalVarNode, RelayExprNode);
  // tschema: custom-begin
  bool SEqualReduce(const GlobalVarNode* other, SEqualReducer equal) const {
    return equal(name_hint, other->name_hint) && equal.FreeVarEqualImpl(this, other)
  }
  bool SHashReduce(SHashReducer hash_reducer) const {
    hash_reduce(name_hint);
    hash_reduce.FreeVarHashImpl(this);
  }
  // tschema: custom-end
};
// tschema: end
```

@tqchen @yzhliu @jwfromm @jroesch @junrushao1994 , also thanks Yizhi for the initial idea and RFC writing.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/1) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/d4b6f75d7c462a948da56217a1f4878c25e48f1262c2d3d4c6ffecfe01fac5a3).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Lixiaoquan via Apache TVM Discuss <no...@discuss.tvm.ai>.

I just wonder why the generated files are tracked in repo? It seems not neccessary since they are generated.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/12) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/b682ba1008b573d5039cf8f6dfa1d460a3cda4eaa7d7969d4b0a7070c98c0343).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by "Cody H. Yu via Apache TVM Discuss" <no...@discuss.tvm.ai>.

Thanks for the clarification :)  So this is more like a tool to facilitate the development process. After the C++ code has been generated, we can continue working on it as always.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/4) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/3e3767ded32b65ed2a9d8bdab73a16d89015957dec4f6c85eb123fff1de33ac9).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Ziheng Jiang via Apache TVM Discuss <no...@discuss.tvm.ai>.

Hi @comaniac,

[quote="comaniac, post:2, topic:7930"]
* Are the generated .h and .cc files supposed to be tracked in the repo, or they are more like the build files?
[/quote]

They will be tracked in the repo. But user should write the tschema for objects instead of writing the cpp files directly except the `tschema-custom` part.

[quote="comaniac, post:2, topic:7930"]
1. For the in-place modification, I am a bit confused about the example of customized C++ code (the example of **Before generation** ). I imagine the TSchema definition is a standalone Python file. Then where should this piece of C++ code be specified?
[/quote]

Sorry that I did not make it clear, there actually no such "before generation" files. Finally we will keep the generated code in our codebase and normal users will build them directly. I just use the code snippets to explain what's tschema's job.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/3) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/9e881f9d59b2719f4c544e446c9c724a05c81ecb1ed6221092d340e747835d4b).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by "Cody H. Yu via Apache TVM Discuss" <no...@discuss.tvm.ai>.

Thanks for the RFC and it looks super useful! I have two questions from the RFC:

1. Are the generated .h and .cc files supposed to be tracked in the repo, or they are more like the build files?

2. For the in-place modification, I am a bit confused about the example of customized C++ code (the example of **Before generation**). I imagine the TSchema definition is a standalone Python file. Then where should this piece of C++ code be specified?

Thanks.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/2) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/89a8ef80d0ac9952d3cc63ea508d05b7cd8eaa283667f7137b22c4dfa21e378c).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by tqchen via Apache TVM Discuss <no...@discuss.tvm.ai>.

I like the idea of using rust to generate rust side. In the meantime, a python syntax for data structure setup can be useful in the future when we want to design custom data types from python side. One potential solution is we keep the python schema frontend, and generate a json exchange format that the rust generator can take. Essentially a separation of frontend, ir repr and backend.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/11) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/ce943d052864c4dc3cfcab9752bc8bd2a8fb00bcb8404efb6fedc1b94400950b).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Ziheng Jiang via Apache TVM Discuss <no...@discuss.tvm.ai>.

Hi @mwillsey,
The decentralizing code generation sounds a good idea technically! We choose Python mainly for user-friendly. I would also like to know @tqchen's opinion here.

[quote="mwillsey, post:9, topic:7930"]
On the topic of checking the generated code in, I’m not sure why that is necessary. As long as the files are generated by the build system, shouldn’t autocomplete and stuff work fine?
[/quote]

We can make an automated build pipeline, but checking in the code directly will make the project codebase more clear. After all, not all the user need to know those details.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/10) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/6cbb5d009e3d6156eff87f11bc8db33ba4fc958779ae01269d38b9d589c172ae).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Max Willsey via Apache TVM Discuss <no...@discuss.tvm.ai>.

Hey @ziheng! I think this is a great idea. As someone who is pushing on the Rust bindings right now (along with @jroesch), I love the idea of deduplicating work.

One design choice I see is whether to centralize or decentralize code-generation. It seems like your original design is leaning towards centralizing it. It would like to start a little discussion on why/if this is the right idea.

Decentralizing code generation could have some benefits, here's how I see it looking. The schemas themselves live in some central location like `/schema`, and they are defined as simply data (perhaps JSON). Each "backend", including C++ and Python, is then responsible for reading this data and generating code for itself. The downside is that there may be some duplicated logic in the code generation. But on the upside, each backend gets to use different tooling to do the codegen; for example, it would be nice to use Rust (using [syn](https://docs.rs/syn/) and [quote](https://docs.rs/quote)) to generate the Rust code. This could also simplify the story for implementing additional code for the methods: each backend just handles it itself, no need to toggle or parse anything.

Here's a example on what the JSON could look like:

```json
[
    {
        "name": "IntImmNode",
        "key": "IntImm",
        "ref": "IntImm",
        "parent": "PrimExprNode",
        "fields": { "value": "int64" }
    },
    ...
]
```
You could imaging grouping these schema into namespaces or something too, if you want.

On the topic of checking the generated code in, I'm not sure why that is necessary. As long as the files are generated by the build system, shouldn't autocomplete and stuff work fine?





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/9) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/e6a97a49bbcdc9e395870a9a6fdaa3a5bc03d333d6b8fc5292e8532eedcedf58).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Ziheng Jiang via Apache TVM Discuss <no...@discuss.tvm.ai>.

Hi @jcf94,

[quote="jcf94, post:5, topic:7930"]
IMHO, advanced users who may benefit from it are more likely to write their C++ code directly, while other users may not really have a requirement on this.
[/quote]

First, this is not only for C++ code generation. In the future, we will extend it for Python/Rust code generation, which is helpful for unifying object definitions between different languages. 

Second, some object fields is hard to fill in even for advanced users, e.g, `type_child_slots`, which is the number of object's children. 

And last but not least, by defining objects with tschema, we will have more in-memory information about the object itself. For example, the type hierarchy between objects, the memory layout of an object, etc. This will enable more compilation optimization in the TIR and help us improve TIR's type system (my next step).

[quote="jcf94, post:5, topic:7930"]
Another problem is I guess it will be hard for a IDE or editor(e.g. VSCode, Vim with CTags) to track the code and provide navigation?
[/quote]

Since we will keep the generated C++ code in the codebase, it will not make any difference with current code in terms of code navigation.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/7) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/e4d194bd51b13c8cb8ae128ecf02ca1c9b08c8bfd1af12b557b75680d736026b).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Chenfan via Apache TVM Discuss <no...@discuss.tvm.ai>.

Thanks, this is really an interesting work! For those who have a requirement to add their own modifications to use TVM, it will be very helpful!

I'm just thinking about how frequently will this new feature be used. IMHO, advanced users who may benefit from it are more likely to write their C++ code directly, while other users may not really have a requirement on this.

Another problem is I guess it will be hard for a IDE or editor(e.g. VSCode, Vim with CTags) to track the code and provide navigation?





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/5) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/4d4bc069e8d8905851ce26dc8560ef44372828222f9d88d1a7fd2a443cc2480d).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by tqchen via Apache TVM Discuss <no...@discuss.tvm.ai>.

First of all, given that the schema generation itself is de-coupled as a frontend, there won't be a problem for the lower-level production system, as the object themselves are still presented as part of the C++ and build into the system. The schema generation is ran separately just like clang-format (and the language to implement the tool matters less).

One thing that a lot of the discussion rightfully point out is that it is hard to build a generator that handles method binding in a language agnostic way. Given the above consideration and the cost mentioned about the complete generation. The current proposal focused on the problem that we can solve, namely the data layout generation. Notably, the proposal gives a more inplace generation process, which means that we can start from the codebase as it is right now, gradually add object into schema and make use of the feature, without having to do a disruptive transition. The tool will also serve as a clang-format style, which means it can be repeatively invoked, and complete the regions that needs to be completed.

Now back to the overall problems and complexity. There are a few considerations:

- C0: We are adding more language bindings, and would want quick data structure accessor to these language binding(e.g. rust, or even a first class cython based member accessor)
- C1: As we introduce more typing into the TIR, we want to enable direct access of the data structures from the generated code(Allow TIR to access runtime::Array and any ir nodes), which would require such data layout schema of these data structures.
- C2: As we start to enhance the python side of the frontend, we eventually want user to be able to declare their ADT in python, as part of enhanced TVMScript. 

While it is true that keeping the current C++ only binding would not gain a lot from the schema generation. There are additonal gains in the area of C0. More importantly, a schema is the blocker to enable C1. Notably, the compiler does not have to depend on python to make use of C1, as we can still generate layout info into a backend language and register there. But python could be a quick starting point.

Of course the above considerations do not force us to use python ast as one of the frontend to the schema. C2 is certainly one motivation to enable this route. Notably, there is no intention to support arbitary python, like TVMscript, we want to define a clear syntax for data layout itself, which is critical to the above enablement, but also acknowledge that it is simply not possible to define a language that handles method def/bindings in all langauges well, thus still allow developers to provide editing directly in the target language. Notably, in most of the objects of interest(IR objects), we intentionally do not have method functions. While there is certainly a bit of complexity being bought in via AST parsing, the goal of a clear pythonic syntax(defined by ourselves) is managable, and aligned with the first class python support philosophy. 

Of course our other core philosophy is to not get into the ways of the developers and usecases. If the introduction of the python frontend hampers the developer's ability to custom define a new object, or port any application on resource constrained devices and/or languages, then we would need to think more carefully about it. My understanding is that the current proposal does not provide constraint in that regard. 

Moreover, the explicit design choice of inplace generation(e.g. the clang-format approach) instead of the full generation greatly reduces the path for adoption and transition. The codebase can stand without the schema tool and continue to add objects manually. The annotated region get generated (and checked via linting pass) as we gradually add objects that requires schema generation. The code are checked in as part of the codebase alleviating the concern of complexity of a full generator system. While I understand that there might be desire to push for a full-fledged generator, we do feel that the strong need for customization, and gradual adoption would make this path a better one with less resistance.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/14) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/43508a5383efea3e5eecb7720ab0e626a9da5a875fbf9aa50909b31533e1ef8f).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Matthew Brookhart via Apache TVM Discuss <no...@discuss.tvm.ai>.

I've been looking at the PR and some of the discussion, and I thought I'd bring my thoughts back jto this RFC, it seems like a better place for broader design thoughts.

First, thanks for the RFC, @ziheng. There is definitely waaaay too much boilerplate in TVM right now, and finding ways to streamline that will help development in the future. 

I'm a still a little confused on what the exact goal of this RFC is.

It seems like the current design is as a setup tool: You write it once, execute it once, and then throw away the schema code. After that, you edit and check in the generated code. At most, I think that would save 15-30 minutes of development time per new datatype introduced, I'm not sure it's really worth the complexity of parsing the python AST.

If we want to move to a situation where we remove the boilerplate code from the repository and generate it on every build, that becomes a more complicated question. First, declarative code like that can be very difficult to debug, it places enormous pressure on the correctness of the parser implementation. Second, if we do want to move to a system where we automatically generate more of the bindings, I really don't think it should be in python. The more we write core TVM functions in python, the less portable the entire system becomes for lower level production uses and more resource constrained systems.

I guess I'm not sure I fully understand the problem this is trying to solve?

Thanks,
Matthew





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/13) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/ae65a4234ac8a233e238bb86cdf7a19a8e4223f9c1b23333950b23a3ee078ef0).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Ziheng Jiang via Apache TVM Discuss <no...@discuss.tvm.ai>.

@zhiics  Yep, we have an option to turn off the default method generation and allow user to fill their customized code snippets.





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/8) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/f6f07e78694a5831841ec93f6de10d8d599d856b5273e7d2b77972125b40b378).

[Apache TVM Discuss] [Development/RFC] [RFC] TVM Object Schema DSL

Posted by Zhi via Apache TVM Discuss <no...@discuss.tvm.ai>.

Yeah, this could be a useful tool to generate the generic templates or the code with the fixed pattern which is actually the major part of a node. For some other members, e.g. SEqualReduce and SHashReduce, we may still need users to manually check/add since they are not always `Equal(this->a, other->a) && Equal(this->b, other->b)`;





---
[Visit Topic](https://discuss.tvm.apache.org/t/rfc-tvm-object-schema-dsl/7930/6) to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, [click here](https://discuss.tvm.apache.org/email/unsubscribe/5be9d9e8b1a8ef474962dc8efbd9e2845da0cd38360e43508f7c39696a21376f).