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

Unify name mangling in TVM #12066

Merged
merged 7 commits into from
Aug 11, 2022
Merged

Unify name mangling in TVM #12066

merged 7 commits into from
Aug 11, 2022

Conversation

gigiblender
Copy link
Contributor

@gigiblender gigiblender commented Jul 12, 2022

This PR is an implementation for this RFC discussion.
Tracking issue: #12181
Forum discussion: https://discuss.tvm.apache.org/t/pre-rfc-name-mangling-in-irmodules/12944/7

It implements a NameSupply and GlobalVarSupply to be used in TVM for obtaining unique names / global vars. It also replaces the previous usages of a name_map and the GetUniqueName method with a NameSupply, or GlobalVarSupply depending on the case.

Copy link
Member

@tqchen tqchen left a comment

Choose a reason for hiding this comment

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

Given that we can recreate GlobalVarSupply from the current IRModule, and we don't need global var renaming for most of the passes(that only do local edits of functions).

It might make sense to create helper method to generate GlobalVarSupply rather than maintaining as a member of IRModule when needed, to keep the main IRModule data structure simple.

Doing so would also simplify the serialization part of IRModule, right now the unordered map data structure is not serialized, as a result save_json/load_json of IRModule will no longer work if we make it as a member.

@@ -64,6 +65,8 @@ class IRModuleNode : public Object {
/* \brief Additional attributes storing meta-data about the module. */
DictAttrs attrs;

GlobalVarSupply global_var_supply;
Copy link
Member

Choose a reason for hiding this comment

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

Given that we can recreate GlobalVarSupply from the current IRModule, and we don't need global var renaming for most of the passes(that only do local edits of functions).

It might make sense to create helper method to generate GlobalVarSupply rather than maintaining as a member of IRModule when needed, to keep the main IRModule data structure simple.

Doing so would also simplify the serialization part of IRModule, right now the unordered map data structure is not serialized, as a result save_json/load_json of IRModule will no longer work if we make it as a member.

Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we can recreate GlobalVarSupply from the current IRModule

I'm not sure this is so trivial. A couple of cases to think through:

  • right now we still lower per-backend separately, so the NameSupply needs to track names across a collection of IRModules in the compiler
  • Suppose we needed to reserve an extern name not mentioned in the IR anywhere. We would surely need to serialize something to track that.

The first case here seems to be the one most at tension with the idea of making NameSupply idempotent. I agree that NameSupply isn't necessarily attached to any given IRModule now, so having a way to access it should mainly be a convenience method and not participate in IRModule serialization at least right now. I'm not so sure it means that it's valid to serialize an IRModule and destroy a TVM instance, then load it back in another TVM instance and expect the NameSupply to be the same.

Copy link
Member

Choose a reason for hiding this comment

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

I agree @areusch , both of your pts seems to point that we should bring NameSupply outside IRModule, and have additional functionalities to re-create from multple (collection) of IRModules

@tqchen
Copy link
Member

tqchen commented Jul 12, 2022

Alternatively, we can make name supply an idempotent object, which means it is not part of the serialization and will be nullptr initially. Calling an API IRModule->GetOrCreateNameSupply() will recreate it and store it in the internal private field if it starts as nullptr, and reuse the created object.

Even in this alternative case, we need to work carefully with cases where we copy on write the IRModule. In nature NameSupply is a mutable object while IRModule is moving towards more immutable style. e.g. if we copy an IRModule and mutate it once, we need to understand the API impact on the original IRModule. Allowing recreation of NameSupply from fresh might be the best way to avoid such problems.

So possible options:

  • F0: IRModule->CreateNameSupply() which recreates NameSupply when needed, always fresh, not all passes need it and minimize IRModule data structure for serialization and other purpoes
  • F1: IRModule->GetOrCreateNameSupply() initialize NameSupply in first use, get around serialization problem. Need to be careful when we Copy and branch out IRModules (where the new IRModule needs to effectively start from a nullptr again and GetOrCreateNameSupply recreates it lazily).

Example to demonstrate the branching case

def transform_example(moda):
     modb = tvm.transform.SomePass1()(moda)
     modc = tvm.transform.SomePass2()(moda)

In the above example, we can either choose to persist NameSupply during transformation, as a result modb, modc share the same NameSupply, causing bugs since they really are two different pathways. Or we duplicate/recreate the NameSupply in each pass(since we do not know whether or not the NameSupply will be used in another mod), which leads to overhead.

This is of course follows the design rationale of keeping things immutable, in this world, likely likely F0 and F1 will have similar effects and recreation when needed(in few times that it is necessary) may not be a bad idea.

@gigiblender
Copy link
Contributor Author

This is of course follows the design rationale of keeping things immutable, in this world, likely likely F0 and F1 will have similar effects and recreation when needed(in few times that it is necessary) may not be a bad idea.

Thank you @tqchen for the review. I also prefer F0, where we always create for immediate use a fresh NameSupply or GlobalVarSupply from the IRModule.

One possible issue I can see with both F0 and F1 is that usually the module name is used as a prefix to the GlobalVars that belong to the same module. Currently, the module name is not a member of IRModule and is only sometimes passed along (for example in te_compiler.cc LowerTE).
One solution I was thinking about is to add the module name as an attribute to the IRModule. If no such attribute is present, then we can fallback to a default (as is the case currently in te_compiler.cc when a module name is not provided by the user).

@tqchen
Copy link
Member

tqchen commented Jul 12, 2022

Thanks @gigiblender Adding module name to IRModule as an optional attr sounds great. BTW, we still need to distinguish the case where the user expect "the name". This is different from cases when a pass wants to allocate a new name that will be used later, but there is no expectation of external reference(as a result the name can be flexible). This happens in cases where user expects an external function name that they can call during runtime.

In which case NameSupply can not do anything but to check duplication.

@gigiblender gigiblender force-pushed the name-mangling branch 3 times, most recently from 4ccc004 to 96a7135 Compare July 19, 2022 09:54
@github-actions
Copy link
Contributor

Built docs for commit 96a7135 can be found here.

@gigiblender gigiblender force-pushed the name-mangling branch 2 times, most recently from 84ae2ef to 428a598 Compare July 20, 2022 17:19
@gigiblender gigiblender marked this pull request as ready for review July 21, 2022 00:47
Copy link
Contributor

@areusch areusch left a comment

Choose a reason for hiding this comment

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

thanks @gigiblender, left a round of comments

include/tvm/ir/module.h Outdated Show resolved Hide resolved
include/tvm/ir/name_supply.h Outdated Show resolved Hide resolved
include/tvm/ir/name_supply.h Outdated Show resolved Hide resolved
include/tvm/ir/name_supply.h Outdated Show resolved Hide resolved
src/target/source/codegen_source_base.cc Outdated Show resolved Hide resolved
src/ir/name_supply.cc Show resolved Hide resolved
src/relay/backend/te_compiler.cc Outdated Show resolved Hide resolved
src/relay/backend/te_compiler.cc Outdated Show resolved Hide resolved
tests/cpp/name_supply_test.cc Show resolved Hide resolved
src/ir/name_supply.cc Outdated Show resolved Hide resolved
@areusch
Copy link
Contributor

areusch commented Jul 26, 2022

@gigiblender gigiblender force-pushed the name-mangling branch 2 times, most recently from e1177da to 30c27af Compare July 27, 2022 15:46
Copy link
Contributor

@kparzysz-quic kparzysz-quic left a comment

Choose a reason for hiding this comment

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

I reviewed half of it so far.

Make sure that your algorithm can handle this (pseudo code):

get_new_name("name2");   // creates "name2"
get_new_name("name");    // creates "name"
get_new_name("name");    // creates "name1"
get_new_name("name");    // does not create "name2" again!

src/ir/global_var_supply.cc Outdated Show resolved Hide resolved
src/ir/global_var_supply.cc Outdated Show resolved Hide resolved
src/ir/module.cc Outdated Show resolved Hide resolved
src/ir/name_supply.cc Outdated Show resolved Hide resolved
for (size_t i = 0; i < prefix.size(); ++i) {
if (prefix[i] == '.') prefix[i] = '_';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are you doing this replacement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the dot operator is used to select a member of a class/struct and therefore should be mangled in all names.

src/ir/name_supply.cc Outdated Show resolved Hide resolved
src/relay/backend/graph_executor_codegen.cc Show resolved Hide resolved
src/relay/backend/te_compiler.cc Outdated Show resolved Hide resolved
src/relay/backend/te_compiler.cc Outdated Show resolved Hide resolved
@gigiblender gigiblender force-pushed the name-mangling branch 2 times, most recently from f4ee874 to 25ca250 Compare July 28, 2022 20:34
@areusch
Copy link
Contributor

areusch commented Aug 2, 2022

@gigiblender is this one ready for another look?

@gigiblender
Copy link
Contributor Author

@gigiblender is this one ready for another look?

Yes. All comments addressed so far.

@areusch areusch merged commit ecfd969 into apache:main Aug 11, 2022
comaniac added a commit to awslabs/raf that referenced this pull request Aug 15, 2022
comaniac added a commit to awslabs/raf that referenced this pull request Aug 15, 2022
* [TVM] Update Submodule

* [Compatible] Fix apache/tvm#12066

* [Compatible] Fix apache/tvm#12382

Co-authored-by: SubmoduleUpdaterBot <submodule-updater-bot@users.noreply.github.com>
Co-authored-by: Cody Yu <comaniac0422@gmail.com>
xinetzone pushed a commit to daobook/tvm that referenced this pull request Nov 25, 2022
* Add NameSupply and GlobalVarSupply

* Build GlobalVarSupply from IRModules instead of having it attached to an IRModule.

* Pass GlobalVarSupply when lowering shape funcs

* Partially replace instantiations of GlobalVar with GlobalVarSupply

* Construct GlobalVarSupply from IRModule

* Add tests for supply

* Add documentation for NameSupply and GlobalVarSupply

Co-authored-by: Florin-Gabriel Blanaru <fgb@system76-pc.localdomain>
mikeseven pushed a commit to mikeseven/tvm that referenced this pull request Sep 27, 2023
* Add NameSupply and GlobalVarSupply

* Build GlobalVarSupply from IRModules instead of having it attached to an IRModule.

* Pass GlobalVarSupply when lowering shape funcs

* Partially replace instantiations of GlobalVar with GlobalVarSupply

* Construct GlobalVarSupply from IRModule

* Add tests for supply

* Add documentation for NameSupply and GlobalVarSupply

Co-authored-by: Florin-Gabriel Blanaru <fgb@system76-pc.localdomain>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants