-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Clean up IRModule attrs and LowerTEPass #8914
Conversation
e5e2516
to
7ee49c9
Compare
@Mousius @mbs-octoml @mikepapadim @leandron @manupa-arm Could you take a look? Thanks! |
@@ -43,7 +43,8 @@ namespace tvm { | |||
|
|||
IRModule::IRModule(tvm::Map<GlobalVar, BaseFunc> functions, | |||
tvm::Map<GlobalTypeVar, TypeData> type_definitions, | |||
std::unordered_set<String> import_set, parser::SourceMap source_map) { | |||
std::unordered_set<String> import_set, parser::SourceMap source_map, | |||
DictAttrs attrs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add attrs
to SEqualReduce
(return equal(attrs, other->attrs);
) and SHashReduce
(hash_reduce(attrs)
) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'll do this. FunctionNode
has attrs
in the hash and equal functions, so we should be consistent with that.
src/relay/ir/transform.cc
Outdated
IRModule updated_mod = | ||
IRModule(mod->functions, mod->type_definitions, mod->Imports(), mod->source_map); | ||
IRModule(mod->functions, mod->type_definitions, mod->Imports(), mod->source_map, mod->attrs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this getting to the point where a copy constructor would be more appropriate so it'd just be IRModule(mod)
rather than having to replicate the arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about something like that, but unfortunately the copy constructor will only increment refs on the shared pointers of the IRModule fields, so it won't work.
I can make a helper function that returns IRModule(mod->functions, ..., mod->attrs)
, though.
src/relay/backend/te_compiler.cc
Outdated
// be called afterwards | ||
return tvm::transform::CreateModulePass(pass_func, 1, "LowerTE", {}); | ||
return tvm::transform::Sequential( | ||
{tvm::transform::CreateModulePass(pass_func, 1, "LowerTE", {}), InferType()}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opt_level
should be 0
for LowerTE
so it's always ran - it wasn't checked before this change but Sequential
checks it when it decides whether to run the pass or not (that's why you're seeing weird graph executor codegen failures as the test runs with opt_level=0
)
As a side note, we should keep the test to ensure things compile successfully at opt_level=0
even after the compile engine stuff is removed 😸
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thanks for catching this! I'll add a note to that test about not removing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Lily for the change.
The changes in the scope of InfertType and removal of per_target_module seems good modulo the existing comments.
However, the addition of attrs to IRModule is an important change and a good one.
I feel we should make sure to add tests that it is passed on to lowered_funcs eventually here or in a separate PR.
(If we are going with a seperate PR, we should not introduce attrs here, just yet).
@@ -122,6 +122,7 @@ class IRModuleNode : public Object { | |||
v->Visit("global_var_map_", &global_var_map_); | |||
v->Visit("global_type_var_map_", &global_type_var_map_); | |||
v->Visit("source_map", &source_map); | |||
v->Visit("attrs", &attrs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a very important change and should be called out in the PR title or the main description.
What happens to these when they are split to Map<Target, IRModule> in lowered_funcs or per_target_module in the intepretter ? Are they copied in ?
Will it be possible to add a test to make sure attrs are passed onto TIR lowering ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After having checked :
tvm/src/relay/backend/te_compiler.cc
Lines 890 to 906 in 9f52e7e
Map<Target, IRModule> GetPerTargetModules(IRModule mod) { | |
std::unordered_map<Target, IRModule, backend::TargetStrHash, backend::TargetStrEqual> | |
per_target_modules; | |
for (const auto& kv : mod->functions) { | |
const GlobalVar& var = kv.first; | |
const BaseFunc& func = kv.second; | |
if (func->IsInstance<tir::PrimFuncNode>()) { | |
// Extract target | |
Optional<Target> target = func->GetAttr<Target>(tvm::attr::kTarget); | |
ICHECK(target) << "Target should be set at this point"; | |
// Put the function in per_target_modules | |
if (!per_target_modules.count(target.value())) { | |
// Initialize the IRModule for this target and add the function | |
IRModule target_module; | |
target_module->Add(var, func); | |
per_target_modules[target.value()] = target_module; |
I dont think it is transferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @manupa-arm, attrs
were actually added to IRModule
in #8750 so anything we do now is an iterative improvement on that. Given this series of PRs aims to remove the Map<Target, IRModule>
entirely (this one removes the per_target_module
from the interpreter) I don't think we need to ensure the copy happens here but I agree we should have some test coverage when the unified IRModule
is lowered to ensure it contains all the attributes we've accrued - this should be a follow up when we change the interface from Map<Target, IRModule>
to IRModule
- does that sound reasonable to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a PR to remove lowered_funcs as well ? @electriclilies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not very clear to me how we can avoid per_target_module throughout the lowering process -- we could push it way down.
Unified IRModule --> per_target_modules (lowered_funcs) --> runtime.Modules
Unless Im missing something here, there will always be a stage that IRModule contains things that gets lowered to a single runtime.Module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most likely, although I have not dug into that part of the codebase in depth yet so I can't say for sure.
The two options that I think are most likely are
build
consuming the IRModule directly, traversing the functions in the IRModule and dealing with each directly (which is what you just mentioned)- Right before
build
is invoked, separating the functions in the module by Target (although we wouldn't store them in aMap<Target, IRModule>
)
So to summarize, we'll either completely remove the data structure that stores functions separated by target, or just push it all the way down to right before build
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the attrs of IRModule are globally valid to all targets. There I feel conveying them to the codegen might still be beneficial.
For 1, we would need to expand the build API to pass the attrs
For 2, we can embed them to each IRModule.
The attrs allows a channel that pass through all of the unified lowering process up until the concept of IRModule cease to exist.
If you agree, I feel we should pass them in to the per target IRModule, then later changed in either way we decide to proceed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I can add them to the per target IRModules and the we can figure out what to do with it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to:
- preserving module attrs through all passes on the assumption they apply to all targets
- xfering them when we project a cross-target IRModule to a specific-target IRModule just before the transition into lowering phases
We should probably have a convention that IRModule attrs should be for describing properties of the code and not of the compilation? Since they will appear everywhere it would be tempting to start adding Target-like things there.
Does anyone have thoughts on whether we should try to line up the IRModule and runtime::Module worlds?
cross-target single-target
IR IRModule ?
Executable ? runtime::Module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That convention sounds good to me, but one thing we should be cautious of is that the attributes of functions do contain properties of the compilation flow (like targets), and this is inconsistent.
@mbs-octoml By lining up the IRModule and runtime::Module worlds do you mean also moving to a world where runtime::Modules are cross-target? I'm honestly not sure if that's something we want to do. @areusch do you have any thoughts about this?
2c8d8c2
to
6d00a9c
Compare
@manupa-arm @Mousius This is green, can you take another look and approve? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for the changes Lily!
@Mousius PTAL when you have some time? and explicitly approve, possibly |
@manupa-arm can you merge? #8974 is waiting on this PR! Thanks |
Hi Lily, After addressing those comments, could you update the description on the PR as to what it should say in the commit message ? |
@manupa-arm I changed the title and responded to the comment! Ready to merge |
3d2ba1b
to
7c77a7a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me @manupa-arm, sorry for the delay @electriclilies!
In this PR, I add
InferType
to the end ofLowerTEPass
so we don't have to call it after we callLowerTEPass
. I also removeper_target_modules_
from the interpreter.To do this, I did have to fix a few bugs, namely:
IRModule
when we soft-copy theIRModule
GlobalVars
by name, not by pointer addresstir::PrimFunc
, we skip the function instead of failing (this allows us to call InferType on the unified module which contains bothrelay::Functions
andtir::PrimFuncs
)This is follow up work from #8886
(Also note that although the branch is called remove-lowered-output, I don't actually remove LoweredOutput in this PR. I started to but there was some other cleanup I needed to do first)