-
Notifications
You must be signed in to change notification settings - Fork 863
[wasm-split] Remove unnecessary global exports #8832
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
6d4b1aa
27041c2
5dea293
e1248fc
0957b7b
8558f24
c75f24d
0b3f7b4
b490f2a
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 |
|---|---|---|
|
|
@@ -77,7 +77,6 @@ | |
| #include "ir/find_all.h" | ||
| #include "ir/module-utils.h" | ||
| #include "ir/names.h" | ||
| #include "support/insert_ordered.h" | ||
| #include "support/unique_deferring_queue.h" | ||
| #include "wasm-builder.h" | ||
| #include "wasm.h" | ||
|
|
@@ -755,13 +754,6 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { | |
| } | ||
| } | ||
|
|
||
| for (auto name : used.tables) { | ||
| if (auto* table = primary.getTableOrNull(name)) { | ||
| if (table->init) { | ||
| collector.walk(table->init); | ||
| } | ||
| } | ||
| } | ||
| return used; | ||
| }; | ||
|
|
||
|
|
@@ -821,32 +813,69 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { | |
| } | ||
| } | ||
|
|
||
| // Compute the transitive closure of globals referenced in other globals' | ||
| // initializers. Since globals can reference other globals, we must ensure | ||
| // that if a global is used in a module, all its dependencies are also marked | ||
| // as used. | ||
| auto computeTransitiveGlobals = [&](UsedNames& used) { | ||
| UniqueNonrepeatingDeferredQueue<Name> worklist; | ||
| for (auto global : used.globals) { | ||
| worklist.push(global); | ||
| // Given a name and a module item kind (field pointer), find which module | ||
| // "owns" it. If it is used by exactly one secondary module, that secondary | ||
| // module is the owner. If it is used by the primary module or multiple | ||
| // secondary modules, the primary module is the owner. If it is not used, | ||
| // returns nullptr. | ||
| auto getOwner = [&](Name name, auto UsedNames::* field) -> UsedNames* { | ||
| UsedNames* owner = nullptr; | ||
| if ((primaryUsed.*field).contains(name)) { | ||
| owner = &primaryUsed; | ||
| } | ||
| while (!worklist.empty()) { | ||
| Name name = worklist.pop(); | ||
| // At this point all globals are still in the primary module, so this | ||
| // exists | ||
| auto* global = primary.getGlobal(name); | ||
| if (!global->imported() && global->init) { | ||
| for (auto* get : FindAll<GlobalGet>(global->init).list) { | ||
| worklist.push(get->name); | ||
| used.globals.insert(get->name); | ||
| for (auto& sec : secondaryUsed) { | ||
| if ((sec.*field).contains(name)) { | ||
| if (owner) { | ||
| owner = &primaryUsed; | ||
| break; | ||
| } | ||
| owner = &sec; | ||
| } | ||
| } | ||
| return owner; | ||
| }; | ||
|
|
||
| computeTransitiveGlobals(primaryUsed); | ||
| for (auto& used : secondaryUsed) { | ||
| computeTransitiveGlobals(used); | ||
| // Scan table initializers into their owning modules. If a table is used by a | ||
| // single secondary module, its initializer dependencies are marked as "used" | ||
| // in that secondary module. Otherwise, they are marked as used in the primary | ||
| // module. | ||
| if (primary.features.hasGC()) { | ||
| for (auto& table : primary.tables) { | ||
| if (!table->init) { | ||
| continue; | ||
| } | ||
| if (UsedNames* owner = getOwner(table->name, &UsedNames::tables)) { | ||
| NameCollector(*owner).walk(table->init); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Compute the transitive closure of globals referenced in other globals' | ||
| // initializers. A global's initializer is evaluated by the module that | ||
| // defines it. | ||
| UniqueDeferredQueue<Name> worklist; | ||
| for (auto name : primaryUsed.globals) { | ||
| worklist.push(name); | ||
| } | ||
| for (auto& sec : secondaryUsed) { | ||
| for (auto name : sec.globals) { | ||
| worklist.push(name); | ||
| } | ||
| } | ||
|
|
||
| while (!worklist.empty()) { | ||
| Name name = worklist.pop(); | ||
| auto* global = primary.getGlobal(name); | ||
| if (!global->init) { | ||
| continue; | ||
| } | ||
| if (UsedNames* owner = getOwner(name, &UsedNames::globals)) { | ||
| for (auto* get : FindAll<GlobalGet>(global->init).list) { | ||
| if (owner->globals.insert(get->name).second) { | ||
| worklist.push(get->name); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+872
to
+878
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. It looks like a name may end up in the |
||
| } | ||
|
|
||
| return std::make_pair(primaryUsed, secondaryUsed); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. | ||
| ;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --keep-funcs=keep | ||
| ;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY | ||
| ;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY | ||
|
|
||
| (module | ||
| ;; PRIMARY: (type $0 (func)) | ||
|
|
||
| ;; PRIMARY: (import "env" "g" (global $g funcref)) | ||
| (import "env" "g" (global $g funcref)) | ||
|
|
||
| ;; A table used by both $keep and $split. | ||
| ;; The table stays in PRIMARY. $g should stay in PRIMARY and NOT be | ||
| ;; exported/imported. | ||
| ;; PRIMARY: (table $t 1 1 funcref (global.get $g)) | ||
| (table $t 1 1 funcref (global.get $g)) | ||
|
|
||
| ;; PRIMARY: (export "table" (table $t)) | ||
|
|
||
| ;; PRIMARY: (func $keep | ||
| ;; PRIMARY-NEXT: (drop | ||
| ;; PRIMARY-NEXT: (table.get $t | ||
| ;; PRIMARY-NEXT: (i32.const 0) | ||
| ;; PRIMARY-NEXT: ) | ||
| ;; PRIMARY-NEXT: ) | ||
| ;; PRIMARY-NEXT: ) | ||
| (func $keep | ||
| (drop | ||
| (table.get $t | ||
| (i32.const 0) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; SECONDARY: (type $0 (func)) | ||
|
|
||
| ;; SECONDARY: (import "primary" "table" (table $t 1 1 funcref)) | ||
|
|
||
| ;; SECONDARY: (func $split | ||
| ;; SECONDARY-NEXT: (drop | ||
| ;; SECONDARY-NEXT: (table.get $t | ||
| ;; SECONDARY-NEXT: (i32.const 0) | ||
| ;; SECONDARY-NEXT: ) | ||
| ;; SECONDARY-NEXT: ) | ||
| ;; SECONDARY-NEXT: ) | ||
| (func $split | ||
| (drop | ||
| (table.get $t | ||
| (i32.const 0) | ||
| ) | ||
| ) | ||
| ) | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. | ||
| ;; RUN: wasm-split -all -g --multi-split %s --manifest %s.manifest --out-prefix=%t -o %t.wasm | ||
| ;; RUN: wasm-dis %t.wasm | filecheck %s --check-prefix PRIMARY | ||
| ;; RUN: wasm-dis %t1.wasm | filecheck %s --check-prefix SECONDARY1 | ||
| ;; RUN: wasm-dis %t2.wasm | filecheck %s --check-prefix SECONDARY2 | ||
|
|
||
| ;; Because global $e is used in both module1 ($split1) and module2 ($split2), $e | ||
| ;; will be exported / imported, but we don't need to export $f. | ||
|
|
||
| (module | ||
| ;; PRIMARY: (type $0 (func)) | ||
|
|
||
| ;; PRIMARY: (global $f i32 (i32.const 42)) | ||
| (global $f i32 (i32.const 42)) | ||
| ;; PRIMARY: (global $e i32 (global.get $f)) | ||
| (global $e i32 (global.get $f)) | ||
|
|
||
| ;; PRIMARY: (export "global" (global $e)) | ||
|
|
||
| ;; PRIMARY: (func $keep | ||
| ;; PRIMARY-NEXT: (nop) | ||
| ;; PRIMARY-NEXT: ) | ||
| (func $keep | ||
| (nop) | ||
| ) | ||
|
|
||
| ;; SECONDARY1: (type $0 (func)) | ||
|
|
||
| ;; SECONDARY1: (import "primary" "global" (global $e i32)) | ||
|
|
||
| ;; SECONDARY1: (func $split1 | ||
| ;; SECONDARY1-NEXT: (drop | ||
| ;; SECONDARY1-NEXT: (global.get $e) | ||
| ;; SECONDARY1-NEXT: ) | ||
| ;; SECONDARY1-NEXT: ) | ||
| (func $split1 | ||
| (drop (global.get $e)) | ||
| ) | ||
|
|
||
| ;; SECONDARY2: (type $0 (func)) | ||
|
|
||
| ;; SECONDARY2: (import "primary" "global" (global $e i32)) | ||
|
|
||
| ;; SECONDARY2: (func $split2 | ||
| ;; SECONDARY2-NEXT: (drop | ||
| ;; SECONDARY2-NEXT: (global.get $e) | ||
| ;; SECONDARY2-NEXT: ) | ||
| ;; SECONDARY2-NEXT: ) | ||
| (func $split2 | ||
| (drop (global.get $e)) | ||
| ) | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| 1: | ||
| split1 | ||
|
|
||
| 2: | ||
| split2 |
Uh oh!
There was an error while loading. Please reload this page.