Skip to content

[wasm-split] Split active segments (part 1) - #8840

Merged
aheejin merged 6 commits into
WebAssembly:mainfrom
aheejin:wasm_split_active_segments1
Jun 17, 2026
Merged

[wasm-split] Split active segments (part 1)#8840
aheejin merged 6 commits into
WebAssembly:mainfrom
aheejin:wasm_split_active_segments1

Conversation

@aheejin

@aheejin aheejin commented Jun 16, 2026

Copy link
Copy Markdown
Member

This removes the assumption that all segments should be pinned to the primary module. This first checks memory and table usage, and if a memory/table is exclusively used in a secondary module, moves active data / elem segments that reference the table to that secondary module.

We check whether a segment can trap, and if so, we pin it to the primary because they should be evaluated at the primary instantiation time.

When a memory/table is used in multiple modules, it is suboptimal to just scan all segments of that memory/table and mark them as used in those modules, because as in the case of #8832, this can generate unnecessary exports. For example, if table $t is used both in the primary and the secondary, and elem $e is like

(elem $e (table $t) (i32.const 0) ... (global.get $g))

because $e will stay in the primary module, the secondary module is not going to see $g, so it should NOT be exported and imported. This PR figures out a single owner module for each memory/table, and marks segments as "used" there.

This does not currently reduces the size of primary modules of acx_gallery and essentials because every table is exported, so there is no tables exclusively used by a secondary. This may change when we remove internal exports.

This removes the assumption that all segments should be pinned to the
primary module. This first checks memory and table usage, and if a
memory/table is exclusively used in a secondary module, moves active
data / elem segments that reference the table to that secondary
module.

We check whether a segment can trap, and if so, we pin it to the primary
because they should be evaluated at the primary instantiation time.

When a memory/table is used in multiple modules, it is suboptimal to
just scan all segments of that memory/table and mark them as used in
those modules, because as in the case of #????, this can generate
unnecessary exports. For example, if table $t is used both in the
primary and the secondary, and elem $e is like
```wast
(elem $e (table $t) (i32.const 0) ... (global.get $g))
```
because $e will stay in the primary module, the secondary module is not
going to see $g, so it should NOT be exported and imported. This PR
figures out a single owner module for each memory/table, and marks
segments as "used" there.

This does not currently reduces the size of primary modules of
acx_gallery and essentials because every table is exported, so there is
no tables exclusively used by a secondary. This may change when we
remove internal exports.
@aheejin
aheejin requested a review from tlively June 16, 2026 03:50
@aheejin
aheejin requested a review from a team as a code owner June 16, 2026 03:50
Comment thread src/ir/module-splitting.cpp Outdated
Comment on lines +824 to +828
if (segment->offset &&
EffectAnalyzer(config.passOptions, primary, segment->offset)
.hasUnremovableSideEffects()) {
return true;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible for an i32 constant expression to have side effects, since the only source of side effects is struct allocations with null descriptors. Maybe we can turn this into an assertion?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I just removed the whole if. Maybe not even worth an assertion.
(In case you feel we need an assertion after all, let me know)

Comment thread src/ir/module-splitting.cpp Outdated
} else {
segmentSize = segment->data.size();
auto* table = primary.getTable(segment->table);
parentSize = table->initial * Table::kPageSize;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Table::kPageSize should just be 1 (I guess we define it to make some binary reader or writer code more general?) so I don't think we need to use it here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: 09f81a9

Again, copied from here..

table->initial * Table::kPageSize);

Comment thread src/ir/module-splitting.cpp Outdated
Comment on lines +858 to +859
using AddressType = Address::address64_t;
AddressType maxWritten;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any advantage to this over just using uint64_t?

@aheejin aheejin Jun 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are the same thing:

using address64_t = uint64_t;

And the reason I used this was just I copied the code from here:

using AddressType = Address::address64_t;
AddressType maxWritten;

Either should be fine. Changed to uint64_t: bff8917

Comment on lines +1081 to +1082
if (!inPrimary && usingSecondaries.empty()) {
dataSegmentsToRemove.push_back(dataSegment->name);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this pattern of having compute and check all the using secondaries could also be simplified if we computed the single owning module for every item as suggested in #8832 (comment).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I do that as a followup?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

@aheejin
aheejin merged commit 3b78959 into WebAssembly:main Jun 17, 2026
16 checks passed
@aheejin
aheejin deleted the wasm_split_active_segments1 branch June 17, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants