-
Notifications
You must be signed in to change notification settings - Fork 59
Do not rename external func decls during --inline-nested-modules #2244
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2244 +/- ##
=======================================
Coverage 97.56% 97.56%
=======================================
Files 93 93
Lines 11210 11210
Branches 1068 1068
=======================================
Hits 10937 10937
Misses 208 208
Partials 65 65 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
josephleekl
left a comment
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 @paul0403 ! This makes sense to me.
I don't know enough about the other passes in Catalyst, will any of them be affected by this? e.g. decompose lowering?
dime10
left a comment
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.
Nice work Paul!
| gates, overwriting their effects. | ||
| [(#2239)](https://github.com/PennyLaneAI/catalyst/pull/2239) | ||
|
|
||
| * The `--inline-nested-module` pass no longer renames external function declarations. |
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.
Usually I jump over these, but I think typically you'd want more context in the release notes. Why is this relevant, where would a user or developer have encountered this, what is the implications, etc. Doesn't have to be long (succinct is good!), but it's more important to highlight the context over the thing that changed.
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.
8eaf2a5 added some context here
| LogicalResult matchAndRewrite(Operation *op, PatternRewriter &rewriter) const override; | ||
|
|
||
| SmallVector<Operation *> *_symbolTables; | ||
| llvm::SmallSet<StringRef, 8> *_externalFuncDeclNames; |
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 is just me being paranoid, but since we've had so many incidents with object lifetimes I want to confirm with you that the string storage lives for as long as this set does?
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 is just the struct's internal handle to the set (hence the pointer), I created this set outside the struct in the global "main" function of the pass where the pattern struct is called:
llvm::SmallSet<StringRef, 8> externalFuncDeclNames;
renameFunctions.add<RenameFunctionsPattern>(context, &symbolTables, &externalFuncDeclNames);
// ...stuff...
inlineNested.add<InlineNestedModule>(context, externalFuncDeclNames,
&alreadyInlinedFuncDeclNames);I need the rename pattern to record which names are external func decls, so the subsequent inline pattern knows to handle them specially. Note that the rename pattern needs to write to the set so it's passed in as a pointer, but the inline pattern only needs to read it so it's passed in as const reference.
So the global llvm::SmallSet<StringRef, 8> externalFuncDeclNames is the one that controls the lifetime. And there is only ever one set! The local variables are just handles, aliasing the global one.
| { | ||
| } | ||
|
|
||
| mutable llvm::SmallSet<StringRef, 8> alreadyInlinedFuncDeclNames; |
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.
Interesting, didn't even know this mechanism existed. Are you comfortable breaking the constness of the mlir rewrite function?
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 guess all I needed was a set to record some names. I can create this set outside the struct and pass it in as reference 👍
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.
| // Look for the func decls in the current qnode module | ||
| // If it is a recorded external func decl, erase it if it already has been inlined. | ||
| SmallVector<Operation *> _erasureWorklist; | ||
| op->walk([&](func::FuncOp f) { |
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 op a module? I'm not sure we actually need to walk it recursively because I've never seen function definitions being nested in other functions 🤔 I simple iteration might suffice
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.
op is the qnode module. Yeah come to think of it direct iteration makes more sense, this is also what Erick chose in other places in this pass as well.
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.
Context:
One of the first steps in the compilation pipeline is
--inline-nested-modules. This pass lifts the per-qnode module up into the global module of the qjit.Because (usually) the symbol table of a module is isolated from above, multiple modules in parallel may contain the same names inside. Therefore when inlining child modules into the global module, we need to rename them to unique names.
However, there's a small problem. Each qnode module has their own specific transform sequence, so the quantum passes (applied during
--apply-transform-sequence) have to be resolved before--inline-nested-module. If a pass generates a function declaration to an external API (e.g. runtime functions), that name must not be altered.Description of the Change:
Do not perform renaming on external function declarations from within the qnode modules during
--inline-nested-modules.Only inline the first occurrence of such func decls from the qnode modules into the global qjit module.
Benefits:
Quantum passes that generate calls to other APIs can work.
The flipside is also true! API developers do not have to maintain multiple aliases for their API functions (e.g. gridsynth pass #2140 )
[sc-105020]