-
Notifications
You must be signed in to change notification settings - Fork 856
Improve indirect call effects in GlobalEffects #8644
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ | |
| #include <ranges> | ||
|
|
||
| #include "ir/effects.h" | ||
| #include "ir/element-utils.h" | ||
| #include "ir/module-utils.h" | ||
| #include "pass.h" | ||
| #include "support/graph_traversal.h" | ||
|
|
@@ -47,6 +48,56 @@ struct FuncInfo { | |
| std::unordered_set<HeapType> indirectCalledTypes; | ||
| }; | ||
|
|
||
| /* | ||
| Only funcs that are 'addressed' may be the target of an indirect call. A | ||
| function is addressed if: | ||
| - It appears in a ref.func expression | ||
| - It appears in an `elem` segment (note that we already ignore `elem declare` | ||
| statements in our IR, but we check separately for funcs that appear in | ||
| `ref.func`). | ||
| - It's exported, because it may flow back to us as a reference. | ||
| - It's imported, which implies it is `elem declare`d. | ||
|
|
||
| If a function doesn't meet any of these criteria, it can't be the target of | ||
| an indirect call and we don't need to include its effects in indirect calls. | ||
| */ | ||
| std::unordered_set<Function*> getAddressedFuncs(Module& module) { | ||
| struct AddressedFuncsWalker : PostWalker<AddressedFuncsWalker> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might as well make this a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 on using |
||
| std::unordered_set<Function*>& addressedFuncs; | ||
|
|
||
| AddressedFuncsWalker(std::unordered_set<Function*>& addressedFuncs) | ||
| : addressedFuncs(addressedFuncs) {} | ||
|
|
||
| void visitRefFunc(RefFunc* refFunc) { | ||
| addressedFuncs.insert(getModule()->getFunction(refFunc->func)); | ||
| } | ||
| }; | ||
|
|
||
| std::unordered_set<Function*> addressedFuncs; | ||
| AddressedFuncsWalker walker(addressedFuncs); | ||
| walker.walkModule(&module); | ||
|
|
||
| ModuleUtils::iterImportedFunctions( | ||
| module, [&addressedFuncs, &module](Function* import) { | ||
| addressedFuncs.insert(module.getFunction(import->name)); | ||
| }); | ||
|
|
||
| for (const auto& export_ : module.exports) { | ||
| if (export_->kind != ExternalKind::Function) { | ||
| continue; | ||
| } | ||
|
|
||
| addressedFuncs.insert(module.getFunction(*export_->getInternalName())); | ||
| } | ||
|
|
||
| ElementUtils::iterAllElementFunctionNames( | ||
| &module, [&addressedFuncs, &module](Name func) { | ||
| addressedFuncs.insert(module.getFunction(func)); | ||
| }); | ||
|
Comment on lines
+93
to
+96
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to do this separately. Walking the module code will already walk all the element segments and find the |
||
|
|
||
| return addressedFuncs; | ||
| } | ||
|
|
||
| std::map<Function*, FuncInfo> analyzeFuncs(Module& module, | ||
| const PassOptions& passOptions) { | ||
| ModuleUtils::ParallelFunctionAnalysis<FuncInfo> analysis( | ||
|
|
@@ -146,6 +197,7 @@ using CallGraph = | |
|
|
||
| CallGraph buildCallGraph(const Module& module, | ||
| const std::map<Function*, FuncInfo>& funcInfos, | ||
| const std::unordered_set<Function*>& addressedFuncs, | ||
| bool closedWorld) { | ||
| CallGraph callGraph; | ||
| if (!closedWorld) { | ||
|
|
@@ -181,7 +233,9 @@ CallGraph buildCallGraph(const Module& module, | |
| } | ||
|
|
||
| // Type -> Function | ||
| callGraph[caller->type.getHeapType()].insert(caller); | ||
| if (addressedFuncs.contains(caller)) { | ||
| callGraph[caller->type.getHeapType()].insert(caller); | ||
| } | ||
| } | ||
|
|
||
| // Type -> Type | ||
|
|
@@ -345,8 +399,10 @@ struct GenerateGlobalEffects : public Pass { | |
| std::map<Function*, FuncInfo> funcInfos = | ||
| analyzeFuncs(*module, getPassOptions()); | ||
|
|
||
| auto callGraph = | ||
| buildCallGraph(*module, funcInfos, getPassOptions().closedWorld); | ||
| auto addressedFuncs = getAddressedFuncs(*module); | ||
|
|
||
| auto callGraph = buildCallGraph( | ||
| *module, funcInfos, addressedFuncs, getPassOptions().closedWorld); | ||
|
|
||
| propagateEffects(*module, getPassOptions(), funcInfos, callGraph); | ||
|
|
||
|
|
||
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.
Maybe it's only me but 'it is
elem declared' sounds a little confusing (because it is not, even though I get what you mean). How about just saying imported functions can be passed a reference (as you did with the exported functions)?