summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-01-25 13:23:45 -0800
committerGitHub <noreply@github.com>2024-01-25 21:23:45 +0000
commit662835a88f6f47d5b1451b453046ae80b4b0fc62 (patch)
tree1f2b4ae7a2bfb35a5a13a0b26cadaee17bbdfb6e /src
parent0375d95a6e134c05ae5478164799a152051826a7 (diff)
downloadbinaryen-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.cpp53
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);
}
});