-
Notifications
You must be signed in to change notification settings - Fork 855
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); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -329,15 +329,12 @@ | |||||
| ) | ||||||
|
|
||||||
| ;; CHECK: (func $f (type $1) (param $ref (ref $only-has-effects-in-not-addressable-function)) | ||||||
| ;; CHECK-NEXT: (call $calls-type-with-effects-but-not-addressable | ||||||
| ;; CHECK-NEXT: (local.get $ref) | ||||||
| ;; CHECK-NEXT: ) | ||||||
| ;; CHECK-NEXT: (nop) | ||||||
| ;; CHECK-NEXT: ) | ||||||
| (func $f (param $ref (ref $only-has-effects-in-not-addressable-function)) | ||||||
| ;; The type $has-effects-but-not-exported doesn't have an address because | ||||||
|
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.
Suggested change
|
||||||
| ;; it's not exported and it's never the target of a ref.func. | ||||||
| ;; We should be able to determine that $ref can only point to $nop. | ||||||
| ;; TODO: Only aggregate effects from functions that are addressed. | ||||||
| ;; So the call_ref has no potential targets and thus no effects. | ||||||
|
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.
Suggested change
It has a target, |
||||||
| (call $calls-type-with-effects-but-not-addressable (local.get $ref)) | ||||||
| ) | ||||||
| ) | ||||||
|
|
||||||
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)?