diff options
author | Alon Zakai <azakai@google.com> | 2024-01-25 13:23:45 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-25 21:23:45 +0000 |
commit | 662835a88f6f47d5b1451b453046ae80b4b0fc62 (patch) | |
tree | 1f2b4ae7a2bfb35a5a13a0b26cadaee17bbdfb6e /src | |
parent | 0375d95a6e134c05ae5478164799a152051826a7 (diff) | |
download | binaryen-662835a88f6f47d5b1451b453046ae80b4b0fc62.tar.gz binaryen-662835a88f6f47d5b1451b453046ae80b4b0fc62.tar.bz2 binaryen-662835a88f6f47d5b1451b453046ae80b4b0fc62.zip |
RemoveUnusedModuleElements: Do not remove unused-but-trapping segments (#6242)
An out of bounds active segment traps during startup, which is an effect we must
preserve.
To avoid a regression here, ignore this in TNH mode (where the user assures us
nothing will trap), and also check if a segment will trivially be in bounds and not
trap (if so, it can be removed).
Fixes the remove-unused-module-elements part of #6230
The small change to an existing testcase made a segment there be in bounds,
to avoid this affecting it. Tests for this are in a new file.
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 53 |
1 files changed, 47 insertions, 6 deletions
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 6ceab0132..b3874b8dc 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -45,6 +45,7 @@ #include "ir/subtypes.h" #include "ir/utils.h" #include "pass.h" +#include "support/stdckdint.h" #include "wasm-builder.h" #include "wasm.h" @@ -635,17 +636,57 @@ struct RemoveUnusedModuleElements : public Pass { // Active segments that write to imported tables and memories are roots // because those writes are externally observable even if the module does // not otherwise use the tables or memories. + // + // Likewise, if traps are possible during startup then just trapping is an + // effect (which can happen if the offset is out of bounds). + auto maybeRootSegment = [&](ModuleElementKind kind, + Name segmentName, + Index segmentSize, + Expression* offset, + Importable* parent, + Index parentSize) { + auto writesToVisible = parent->imported() && segmentSize; + auto mayTrap = false; + if (!getPassOptions().trapsNeverHappen) { + // Check if this might trap. If it is obviously in bounds then it + // cannot. + auto* c = offset->dynCast<Const>(); + // Check for overflow in the largest possible space of addresses. + using AddressType = Address::address64_t; + AddressType maxWritten; + // If there is no integer, or if there is and the addition overflows, or + // if the addition leads to a too-large value, then we may trap. + mayTrap = !c || + std::ckd_add(&maxWritten, + (AddressType)segmentSize, + (AddressType)c->value.getInteger()) || + maxWritten > parentSize; + } + if (writesToVisible || mayTrap) { + roots.emplace_back(kind, segmentName); + } + }; ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { - if (module->getMemory(segment->memory)->imported() && - !segment->data.empty()) { - roots.emplace_back(ModuleElementKind::DataSegment, segment->name); + if (segment->memory.is()) { + auto* memory = module->getMemory(segment->memory); + maybeRootSegment(ModuleElementKind::DataSegment, + segment->name, + segment->data.size(), + segment->offset, + memory, + memory->initial * Memory::kPageSize); } }); ModuleUtils::iterActiveElementSegments( *module, [&](ElementSegment* segment) { - if (module->getTable(segment->table)->imported() && - !segment->data.empty()) { - roots.emplace_back(ModuleElementKind::ElementSegment, segment->name); + if (segment->table.is()) { + auto* table = module->getTable(segment->table); + maybeRootSegment(ModuleElementKind::ElementSegment, + segment->name, + segment->data.size(), + segment->offset, + table, + table->initial * Table::kPageSize); } }); |