From 01ab947c94111bfe3742523fceb2f98c3b8e1d85 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 12:35:22 -0800 Subject: [PATCH 01/24] start --- src/passes/RemoveUnusedModuleElements.cpp | 6 ++- .../remove-unused-module-elements_tnh.wast | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/lit/passes/remove-unused-module-elements_tnh.wast diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 6ceab013209..5c2cc38521a 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -700,11 +700,13 @@ struct RemoveUnusedModuleElements : public Pass { module->removeTables([&](Table* curr) { return !needed(ModuleElement(ModuleElementKind::Table, curr->name)); }); + // Segments that are not used may still have a noticeable effect, if they + // cause a trap during startup. module->removeDataSegments([&](DataSegment* curr) { - return !needed(ModuleElement(ModuleElementKind::DataSegment, curr->name)); + return !needed(ModuleElement(ModuleElementKind::DataSegment, curr->name)) && (!curr->offset || options.trapsNeverHappen); }); module->removeElementSegments([&](ElementSegment* curr) { - return !needed({ModuleElementKind::ElementSegment, curr->name}); + return !needed({ModuleElementKind::ElementSegment, curr->name}) && (!curr->offset || options.trapsNeverHappen);; }); // TODO: After removing elements, we may be able to remove more things, and // should continue to work. (For example, after removing a reference diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast new file mode 100644 index 00000000000..84fbdb0ad0f --- /dev/null +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -0,0 +1,45 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. + +;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -tnh -all -S -o - | filecheck %s --check-prefix=T_N_H + +;; The data segment here will trap during startup as it is out of bounds. We +;; can only remove such segments if we assume TrapsNeverHappen. + +(module + ;; CHECK: (type $0 (func (param i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (import "fuzzing-support" "log-i32" (func $fimport$0 (type $0) (param i32))) + ;; T_N_H: (type $0 (func (param i32))) + + ;; T_N_H: (type $1 (func)) + + ;; T_N_H: (import "fuzzing-support" "log-i32" (func $fimport$0 (type $0) (param i32))) + (import "fuzzing-support" "log-i32" (func $fimport$0 (param i32))) + + (memory $0 16 17 shared) + (data $1 (i32.const -1) "") + + ;; CHECK: (export "func_13_invoker" (func $0)) + ;; T_N_H: (export "func_13_invoker" (func $0)) + (export "func_13_invoker" (func $0)) + + ;; CHECK: (func $0 (type $1) + ;; CHECK-NEXT: (call $fimport$0 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; T_N_H: (func $0 (type $1) + ;; T_N_H-NEXT: (call $fimport$0 + ;; T_N_H-NEXT: (i32.const 0) + ;; T_N_H-NEXT: ) + ;; T_N_H-NEXT: ) + (func $0 + (call $fimport$0 + (i32.const 0) + ) + ) +) From 2bbf621f994a705fa71ff50202290cf79d018bde Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 12:37:51 -0800 Subject: [PATCH 02/24] work --- src/passes/RemoveUnusedModuleElements.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 5c2cc38521a..692ff811839 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -706,7 +706,7 @@ struct RemoveUnusedModuleElements : public Pass { return !needed(ModuleElement(ModuleElementKind::DataSegment, curr->name)) && (!curr->offset || options.trapsNeverHappen); }); module->removeElementSegments([&](ElementSegment* curr) { - return !needed({ModuleElementKind::ElementSegment, curr->name}) && (!curr->offset || options.trapsNeverHappen);; + return !needed({ModuleElementKind::ElementSegment, curr->name}) && (!curr->offset || options.trapsNeverHappen); }); // TODO: After removing elements, we may be able to remove more things, and // should continue to work. (For example, after removing a reference From 0c8a66d88b7da66342b19dce43155fa1205beb05 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 12:43:46 -0800 Subject: [PATCH 03/24] fix --- src/passes/RemoveUnusedModuleElements.cpp | 22 ++++++++++++------- .../remove-unused-module-elements_tnh.wast | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 692ff811839..baf18954d55 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -635,16 +635,24 @@ 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). + // TODO: We could infer a trap cannot happen if we see the offset is in + // bounds. + auto trapsMatter = !getPassOptions().trapsNeverHappen; ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { - if (module->getMemory(segment->memory)->imported() && - !segment->data.empty()) { + auto writesToVisible = module->getMemory(segment->memory)->imported() && + !segment->data.empty(); + if (writesToVisible || trapsMatter) { roots.emplace_back(ModuleElementKind::DataSegment, segment->name); } }); ModuleUtils::iterActiveElementSegments( *module, [&](ElementSegment* segment) { - if (module->getTable(segment->table)->imported() && - !segment->data.empty()) { + auto writesToVisible = module->getTable(segment->table)->imported() && + !segment->data.empty(); + if (writesToVisible || trapsMatter) { roots.emplace_back(ModuleElementKind::ElementSegment, segment->name); } }); @@ -700,13 +708,11 @@ struct RemoveUnusedModuleElements : public Pass { module->removeTables([&](Table* curr) { return !needed(ModuleElement(ModuleElementKind::Table, curr->name)); }); - // Segments that are not used may still have a noticeable effect, if they - // cause a trap during startup. module->removeDataSegments([&](DataSegment* curr) { - return !needed(ModuleElement(ModuleElementKind::DataSegment, curr->name)) && (!curr->offset || options.trapsNeverHappen); + return !needed(ModuleElement(ModuleElementKind::DataSegment, curr->name)); }); module->removeElementSegments([&](ElementSegment* curr) { - return !needed({ModuleElementKind::ElementSegment, curr->name}) && (!curr->offset || options.trapsNeverHappen); + return !needed({ModuleElementKind::ElementSegment, curr->name}); }); // TODO: After removing elements, we may be able to remove more things, and // should continue to work. (For example, after removing a reference diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 84fbdb0ad0f..8ddf71863c0 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -20,7 +20,9 @@ ;; T_N_H: (import "fuzzing-support" "log-i32" (func $fimport$0 (type $0) (param i32))) (import "fuzzing-support" "log-i32" (func $fimport$0 (param i32))) + ;; CHECK: (memory $0 16 17 shared) (memory $0 16 17 shared) + ;; CHECK: (data $1 (i32.const -1) "") (data $1 (i32.const -1) "") ;; CHECK: (export "func_13_invoker" (func $0)) From 61fdd000c481192ac244dcd5dda32832f5e6883c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 12:46:43 -0800 Subject: [PATCH 04/24] test --- .../remove-unused-module-elements_tnh.wast | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 8ddf71863c0..bac28afc0d3 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -22,24 +22,33 @@ ;; CHECK: (memory $0 16 17 shared) (memory $0 16 17 shared) + ;; CHECK: (data $1 (i32.const -1) "") (data $1 (i32.const -1) "") - ;; CHECK: (export "func_13_invoker" (func $0)) - ;; T_N_H: (export "func_13_invoker" (func $0)) - (export "func_13_invoker" (func $0)) + (table 1 1 funcref) + + (elem (i32.const -1) $func) + + ;; CHECK: (table $0 1 1 funcref) + + ;; CHECK: (elem $0 (i32.const -1) $func) + + ;; CHECK: (export "func" (func $func)) + ;; T_N_H: (export "func" (func $func)) + (export "func" (func $func)) - ;; CHECK: (func $0 (type $1) + ;; CHECK: (func $func (type $1) ;; CHECK-NEXT: (call $fimport$0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; T_N_H: (func $0 (type $1) + ;; T_N_H: (func $func (type $1) ;; T_N_H-NEXT: (call $fimport$0 ;; T_N_H-NEXT: (i32.const 0) ;; T_N_H-NEXT: ) ;; T_N_H-NEXT: ) - (func $0 + (func $func (call $fimport$0 (i32.const 0) ) From 42abeaca77642ee748b9e6c6451227e10772f0ac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 15:00:19 -0800 Subject: [PATCH 05/24] work --- src/passes/RemoveUnusedModuleElements.cpp | 36 +++++++++++++---------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index baf18954d55..44a2ca41071 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -638,24 +638,30 @@ struct RemoveUnusedModuleElements : public Pass { // // Likewise, if traps are possible during startup then just trapping is an // effect (which can happen if the offset is out of bounds). - // TODO: We could infer a trap cannot happen if we see the offset is in - // bounds. - auto trapsMatter = !getPassOptions().trapsNeverHappen; - ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { - auto writesToVisible = module->getMemory(segment->memory)->imported() && - !segment->data.empty(); - if (writesToVisible || trapsMatter) { - roots.emplace_back(ModuleElementKind::DataSegment, segment->name); + auto mustRootSegment = [&](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(); + Address 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 || ckd_add(&maxWritten, segmentSize, c->value.getInteger()) || maxWritten >= parentSize; + } + if (writesToVisible || trapsNeverHappen) { + roots.emplace_back(kind, name); } + }; + ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { + auto* memory = module->getMemory(segment->memory); + maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, memory, memory->initialSize * Memory::kPageSize); }); ModuleUtils::iterActiveElementSegments( - *module, [&](ElementSegment* segment) { - auto writesToVisible = module->getTable(segment->table)->imported() && - !segment->data.empty(); - if (writesToVisible || trapsMatter) { - roots.emplace_back(ModuleElementKind::ElementSegment, segment->name); - } - }); + auto* table = module->getTable(segment->table); + maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, table, table->initialSize * Table::kPageSize); + }); // For now, all functions that can be called indirectly are marked as roots. // TODO: Compute this based on which ElementSegments are actually used, From 5b80fa488ac20ce3d472cf5c5cd09c17099f419f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 15:04:57 -0800 Subject: [PATCH 06/24] work --- src/passes/RemoveUnusedModuleElements.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 44a2ca41071..0db4c56a7ba 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" @@ -638,29 +639,31 @@ struct RemoveUnusedModuleElements : public Pass { // // Likewise, if traps are possible during startup then just trapping is an // effect (which can happen if the offset is out of bounds). - auto mustRootSegment = [&](ModuleElementKind kind, Name segmentName, Index segmentSize, Expression* offset, Importable* parent, Index parentSize) { + 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(); - Address maxWritten; + // 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 || ckd_add(&maxWritten, segmentSize, c->value.getInteger()) || maxWritten >= parentSize; + mayTrap = !c || std::ckd_add(&maxWritten, (AddressType)segmentSize, (AddressType)c->value.getInteger()) || maxWritten >= parentSize; } - if (writesToVisible || trapsNeverHappen) { + if (writesToVisible || mayTrap) { roots.emplace_back(kind, name); } }; ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { auto* memory = module->getMemory(segment->memory); - maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, memory, memory->initialSize * Memory::kPageSize); + maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, memory, memory->initial * Memory::kPageSize); }); - ModuleUtils::iterActiveElementSegments( + ModuleUtils::iterActiveElementSegments(*module, [&](ElementSegment* segment) { auto* table = module->getTable(segment->table); - maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, table, table->initialSize * Table::kPageSize); + maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, table, table->initial * Table::kPageSize); }); // For now, all functions that can be called indirectly are marked as roots. From 29b5170013b41467f664361530a29013f902dc3c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 15:22:39 -0800 Subject: [PATCH 07/24] work --- src/passes/RemoveUnusedModuleElements.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 0db4c56a7ba..64271181f03 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -654,16 +654,20 @@ struct RemoveUnusedModuleElements : public Pass { mayTrap = !c || std::ckd_add(&maxWritten, (AddressType)segmentSize, (AddressType)c->value.getInteger()) || maxWritten >= parentSize; } if (writesToVisible || mayTrap) { - roots.emplace_back(kind, name); + roots.emplace_back(kind, segmentName); } }; ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { - auto* memory = module->getMemory(segment->memory); - maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, memory, memory->initial * Memory::kPageSize); + 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) { - auto* table = module->getTable(segment->table); - maybeRootSegment(ModuleElementKind::DataSegment, segment->name, segment->data.size(), segment->offset, table, table->initial * Table::kPageSize); + 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); + } }); // For now, all functions that can be called indirectly are marked as roots. From 06f39d7272309bd86dfa3358d06f7144249e4ec7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:30:01 -0800 Subject: [PATCH 08/24] fix --- test/lit/passes/remove-unused-module-elements_all-features.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit/passes/remove-unused-module-elements_all-features.wast b/test/lit/passes/remove-unused-module-elements_all-features.wast index 23f01878734..7c2ad11d801 100644 --- a/test/lit/passes/remove-unused-module-elements_all-features.wast +++ b/test/lit/passes/remove-unused-module-elements_all-features.wast @@ -170,7 +170,7 @@ (module ;; remove all tables and the memory (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) - (import "env" "table2" (table $1 1 2 funcref)) + (import "env" "table2" (table $1 2 2 funcref)) (elem (table $1) (offset (i32.const 0)) func) (elem (table $1) (offset (i32.const 1)) func) ) From 1a28be274e446d89e259753760e4b36e43d30246 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:40:31 -0800 Subject: [PATCH 09/24] test --- .../remove-unused-module-elements_tnh.wast | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index bac28afc0d3..97925b449de 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -54,3 +54,101 @@ ) ) ) + +;; Some segments can be removed: any segment that writes to address 131072 or +;; higher will trap, and must be kept (unless TNH). Only the $bad segment +;; should remain for that reason, however, it keeps the memory alive which +;; keeps the $ok* segments alive too. +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const 131070) "ab") + (data $bad (i32.const 131070) "ab") +) + +;; The following modules have variations on the bad segment. +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const 131071) "a") + (data $bad (i32.const 131071) "a") +) + +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const 9999999) "a") + (data $bad (i32.const 9999999) "a") +) + +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const -2) "a") + (data $bad (i32.const 4294967294) "a") +) + +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const -2) "abcdefg") + (data $bad (i32.const 4294967294) "abcdefg") +) + +(module + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $bad (i32.const -2) "a") + (data $bad (i32.const -2) "a") +) + +;; Finally, a module with no bad segments. We can remove all the contents. +(module + (memory $0 2 2) + + (data $ok1 (i32.const 0) "a") + (data $ok2 (i32.const 1000) "a") + (data $ok3 (i32.const 131070) "a") +) From 80d1eb17707b0be42b76ac8dea57fba2de8e19f6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:42:12 -0800 Subject: [PATCH 10/24] test --- .../remove-unused-module-elements_tnh.wast | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 97925b449de..eb4153c54dd 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -152,3 +152,31 @@ (data $ok2 (i32.const 1000) "a") (data $ok3 (i32.const 131070) "a") ) + +;; Similar testing for element segments. One bad segment keeps it all alive +;; here. +(module + (table 10 10 funcref) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (table $0 10 10 funcref) + + ;; CHECK: (elem $ok1 (i32.const 0) $func) + (elem $ok1 (i32.const 0) $func) + ;; CHECK: (elem $ok2 (i32.const 8) $func $func) + (elem $ok2 (i32.const 8) $func $func) + ;; CHECK: (elem $ok3 (i32.const 9) $func) + (elem $ok3 (i32.const 9) $func) + + ;; CHECK: (func $func (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; T_N_H: (type $0 (func)) + + ;; T_N_H: (func $func (type $0) + ;; T_N_H-NEXT: (nop) + ;; T_N_H-NEXT: ) + (func $func) +) + From b064b59627d0844b7b7ed48f3544c95d5a8a0637 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:46:47 -0800 Subject: [PATCH 11/24] test --- src/passes/RemoveUnusedModuleElements.cpp | 2 +- .../remove-unused-module-elements_tnh.wast | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 64271181f03..9b206c79ad1 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -651,7 +651,7 @@ struct RemoveUnusedModuleElements : public Pass { 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; + mayTrap = !c || std::ckd_add(&maxWritten, (AddressType)segmentSize, (AddressType)c->value.getInteger()) || maxWritten > parentSize; } if (writesToVisible || mayTrap) { roots.emplace_back(kind, segmentName); diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index eb4153c54dd..b6b239e3138 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -67,10 +67,10 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") - ;; CHECK: (data $bad (i32.const 131070) "ab") - (data $bad (i32.const 131070) "ab") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") + ;; CHECK: (data $bad (i32.const 131071) "ab") + (data $bad (i32.const 131071) "ab") ) ;; The following modules have variations on the bad segment. @@ -82,10 +82,10 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") - ;; CHECK: (data $bad (i32.const 131071) "a") - (data $bad (i32.const 131071) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") + ;; CHECK: (data $bad (i32.const 131072) "a") + (data $bad (i32.const 131072) "a") ) (module @@ -96,8 +96,8 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") ;; CHECK: (data $bad (i32.const 9999999) "a") (data $bad (i32.const 9999999) "a") ) @@ -110,8 +110,8 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") ;; CHECK: (data $bad (i32.const -2) "a") (data $bad (i32.const 4294967294) "a") ) @@ -124,8 +124,8 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") ;; CHECK: (data $bad (i32.const -2) "abcdefg") (data $bad (i32.const 4294967294) "abcdefg") ) @@ -138,8 +138,8 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (i32.const 131070) "a") - (data $ok3 (i32.const 131070) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") ;; CHECK: (data $bad (i32.const -2) "a") (data $bad (i32.const -2) "a") ) @@ -150,7 +150,7 @@ (data $ok1 (i32.const 0) "a") (data $ok2 (i32.const 1000) "a") - (data $ok3 (i32.const 131070) "a") + (data $ok3 (i32.const 131071) "a") ) ;; Similar testing for element segments. One bad segment keeps it all alive @@ -168,6 +168,8 @@ (elem $ok2 (i32.const 8) $func $func) ;; CHECK: (elem $ok3 (i32.const 9) $func) (elem $ok3 (i32.const 9) $func) + ;; CHECK: (elem $bad (i32.const 10) $func) + (elem $bad (i32.const 10) $func) ;; CHECK: (func $func (type $0) ;; CHECK-NEXT: (nop) From c4b899aafe2df4ef12bb01cfa4292382387c9d00 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:47:20 -0800 Subject: [PATCH 12/24] test --- .../remove-unused-module-elements_tnh.wast | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index b6b239e3138..033471a0b7f 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -182,3 +182,31 @@ (func $func) ) +;; A different bad segment. +(module + (table 10 10 funcref) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (table $0 10 10 funcref) + + ;; CHECK: (elem $ok1 (i32.const 0) $func) + (elem $ok1 (i32.const 0) $func) + ;; CHECK: (elem $ok2 (i32.const 8) $func $func) + (elem $ok2 (i32.const 8) $func $func) + ;; CHECK: (elem $ok3 (i32.const 9) $func) + (elem $ok3 (i32.const 9) $func) + ;; CHECK: (elem $bad (i32.const 9) $func $func) + (elem $bad (i32.const 9) $func $func) + + ;; CHECK: (func $func (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; T_N_H: (type $0 (func)) + + ;; T_N_H: (func $func (type $0) + ;; T_N_H-NEXT: (nop) + ;; T_N_H-NEXT: ) + (func $func) +) + From f5e068123aaa285815d605f65c52b9b6b92b95dc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:48:08 -0800 Subject: [PATCH 13/24] test --- .../remove-unused-module-elements_tnh.wast | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 033471a0b7f..d6baee4aaa6 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -210,3 +210,24 @@ (func $func) ) +;; No bad segments: all element segments vanish. TODO: the function could too +(module + (table 10 10 funcref) + + (elem $ok1 (i32.const 0) $func) + (elem $ok2 (i32.const 8) $func $func) + (elem $ok3 (i32.const 9) $func) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $func (type $0) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; T_N_H: (type $0 (func)) + + ;; T_N_H: (func $func (type $0) + ;; T_N_H-NEXT: (nop) + ;; T_N_H-NEXT: ) + (func $func) +) + From f28d88490474bb676375c1273c10d796fb24e754 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 24 Jan 2024 16:48:17 -0800 Subject: [PATCH 14/24] format --- src/passes/RemoveUnusedModuleElements.cpp | 38 +++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 9b206c79ad1..b3874b8dce3 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -639,7 +639,12 @@ struct RemoveUnusedModuleElements : public Pass { // // 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 maybeRootSegment = [&](ModuleElementKind kind, + Name segmentName, + Index segmentSize, + Expression* offset, + Importable* parent, + Index parentSize) { auto writesToVisible = parent->imported() && segmentSize; auto mayTrap = false; if (!getPassOptions().trapsNeverHappen) { @@ -651,7 +656,11 @@ struct RemoveUnusedModuleElements : public Pass { 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; + mayTrap = !c || + std::ckd_add(&maxWritten, + (AddressType)segmentSize, + (AddressType)c->value.getInteger()) || + maxWritten > parentSize; } if (writesToVisible || mayTrap) { roots.emplace_back(kind, segmentName); @@ -660,15 +669,26 @@ struct RemoveUnusedModuleElements : public Pass { ModuleUtils::iterActiveDataSegments(*module, [&](DataSegment* segment) { 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 (segment->table.is()) { - auto* table = module->getTable(segment->table); - maybeRootSegment(ModuleElementKind::ElementSegment, segment->name, segment->data.size(), segment->offset, table, table->initial * Table::kPageSize); + maybeRootSegment(ModuleElementKind::DataSegment, + segment->name, + segment->data.size(), + segment->offset, + memory, + memory->initial * Memory::kPageSize); } }); + ModuleUtils::iterActiveElementSegments( + *module, [&](ElementSegment* segment) { + 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); + } + }); // For now, all functions that can be called indirectly are marked as roots. // TODO: Compute this based on which ElementSegments are actually used, From 9b2ae950e975970d679b70476a69dba2fda49026 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:37:18 -0800 Subject: [PATCH 15/24] siml --- .../remove-unused-module-elements_tnh.wast | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index d6baee4aaa6..7d96bb42130 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -8,18 +8,10 @@ ;; can only remove such segments if we assume TrapsNeverHappen. (module - ;; CHECK: (type $0 (func (param i32))) - - ;; CHECK: (type $1 (func)) - - ;; CHECK: (import "fuzzing-support" "log-i32" (func $fimport$0 (type $0) (param i32))) - ;; T_N_H: (type $0 (func (param i32))) - - ;; T_N_H: (type $1 (func)) - - ;; T_N_H: (import "fuzzing-support" "log-i32" (func $fimport$0 (type $0) (param i32))) (import "fuzzing-support" "log-i32" (func $fimport$0 (param i32))) + ;; CHECK: (type $0 (func)) + ;; CHECK: (memory $0 16 17 shared) (memory $0 16 17 shared) @@ -30,29 +22,20 @@ (elem (i32.const -1) $func) + ;; CHECK: (table $0 1 1 funcref) ;; CHECK: (elem $0 (i32.const -1) $func) - ;; CHECK: (export "func" (func $func)) - ;; T_N_H: (export "func" (func $func)) - (export "func" (func $func)) - - ;; CHECK: (func $func (type $1) - ;; CHECK-NEXT: (call $fimport$0 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) + ;; CHECK: (func $func (type $0) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) - ;; T_N_H: (func $func (type $1) - ;; T_N_H-NEXT: (call $fimport$0 - ;; T_N_H-NEXT: (i32.const 0) - ;; T_N_H-NEXT: ) + ;; T_N_H: (type $0 (func)) + + ;; T_N_H: (func $func (type $0) + ;; T_N_H-NEXT: (nop) ;; T_N_H-NEXT: ) - (func $func - (call $fimport$0 - (i32.const 0) - ) - ) + (func $func) ) ;; Some segments can be removed: any segment that writes to address 131072 or From 154785eca37b5299113e8f8547afb1a8366e2dbf Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:37:29 -0800 Subject: [PATCH 16/24] siml --- test/lit/passes/remove-unused-module-elements_tnh.wast | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 7d96bb42130..5e615125c1f 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -8,8 +8,6 @@ ;; can only remove such segments if we assume TrapsNeverHappen. (module - (import "fuzzing-support" "log-i32" (func $fimport$0 (param i32))) - ;; CHECK: (type $0 (func)) ;; CHECK: (memory $0 16 17 shared) From 8573500ebf14344a71935dc9a4a26fd9b47d93da Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:39:06 -0800 Subject: [PATCH 17/24] siml --- .../remove-unused-module-elements_tnh.wast | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 5e615125c1f..20b86c15afe 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -125,6 +125,24 @@ (data $bad (i32.const -2) "a") ) +;; An imported global is an unknown offset, so it might trap. +(module + ;; CHECK: (import "a" "b" (global $imported i32)) + (import "a" "b" (global $imported i32)) + + ;; CHECK: (memory $0 2 2) + (memory $0 2 2) + + ;; CHECK: (data $ok1 (i32.const 0) "a") + (data $ok1 (i32.const 0) "a") + ;; CHECK: (data $ok2 (i32.const 1000) "a") + (data $ok2 (i32.const 1000) "a") + ;; CHECK: (data $ok3 (global.get $imported) "a") + (data $ok3 (global.get $imported) "a") + ;; CHECK: (data $bad (i32.const -2) "a") + (data $bad (i32.const -2) "a") +) + ;; Finally, a module with no bad segments. We can remove all the contents. (module (memory $0 2 2) From 3973ceb97b1953934e3a4308f0f17300019dddef Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:43:04 -0800 Subject: [PATCH 18/24] siml --- test/lit/passes/remove-unused-module-elements_tnh.wast | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 20b86c15afe..32fced6f82c 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -4,9 +4,8 @@ ;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -all -S -o - | filecheck %s ;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -tnh -all -S -o - | filecheck %s --check-prefix=T_N_H -;; The data segment here will trap during startup as it is out of bounds. We +;; The segments here will trap during startup as they are out of bounds. We ;; can only remove such segments if we assume TrapsNeverHappen. - (module ;; CHECK: (type $0 (func)) @@ -20,7 +19,6 @@ (elem (i32.const -1) $func) - ;; CHECK: (table $0 1 1 funcref) ;; CHECK: (elem $0 (i32.const -1) $func) From 4cf8e3966cdc72ef9c04f40233f911278dea4eb2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:46:58 -0800 Subject: [PATCH 19/24] siml --- .../remove-unused-module-elements_tnh.wast | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 32fced6f82c..3674ec9f1ae 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -6,32 +6,25 @@ ;; The segments here will trap during startup as they are out of bounds. We ;; can only remove such segments if we assume TrapsNeverHappen. +;; +;; The passive segments, however, can be removed: they do nothing during +;; startup, and have no uses. (module - ;; CHECK: (type $0 (func)) - ;; CHECK: (memory $0 16 17 shared) (memory $0 16 17 shared) - ;; CHECK: (data $1 (i32.const -1) "") - (data $1 (i32.const -1) "") - - (table 1 1 funcref) + ;; CHECK: (data $0 (i32.const -1) "") + (data $0 (i32.const -1) "") - (elem (i32.const -1) $func) + (data $1 "") ;; CHECK: (table $0 1 1 funcref) + (table $0 1 1 funcref) - ;; CHECK: (elem $0 (i32.const -1) $func) - - ;; CHECK: (func $func (type $0) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - ;; T_N_H: (type $0 (func)) + ;; CHECK: (elem $0 (i32.const -1)) + (elem $0 (i32.const -1)) - ;; T_N_H: (func $func (type $0) - ;; T_N_H-NEXT: (nop) - ;; T_N_H-NEXT: ) - (func $func) + (elem $1 func) ) ;; Some segments can be removed: any segment that writes to address 131072 or From 64d358607a3b265e2bb1e4afcb49cd756b3f3847 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:48:25 -0800 Subject: [PATCH 20/24] siml --- test/lit/passes/remove-unused-module-elements_tnh.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 3674ec9f1ae..16d602350ef 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -98,8 +98,8 @@ (data $ok2 (i32.const 1000) "a") ;; CHECK: (data $ok3 (i32.const 131071) "a") (data $ok3 (i32.const 131071) "a") - ;; CHECK: (data $bad (i32.const -2) "abcdefg") - (data $bad (i32.const 4294967294) "abcdefg") + ;; CHECK: (data $bad (i32.const -6) "abcdefg") + (data $bad (i32.const 4294967290) "abcdefg") ) (module From 52fbfb9b952690142f8738a365f471b08c690469 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:49:31 -0800 Subject: [PATCH 21/24] siml --- test/lit/passes/remove-unused-module-elements_tnh.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 16d602350ef..4f34eae5e0b 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -98,8 +98,8 @@ (data $ok2 (i32.const 1000) "a") ;; CHECK: (data $ok3 (i32.const 131071) "a") (data $ok3 (i32.const 131071) "a") - ;; CHECK: (data $bad (i32.const -6) "abcdefg") - (data $bad (i32.const 4294967290) "abcdefg") + ;; CHECK: (data $bad (i32.const -6) "abcdefghijklmnop_overflow") + (data $bad (i32.const 4294967290) "abcdefghijklmnop_overflow") ) (module From 506a1bb533a7c6abfb6b500538b4170290b042c3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 10:50:19 -0800 Subject: [PATCH 22/24] siml --- test/lit/passes/remove-unused-module-elements_tnh.wast | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 4f34eae5e0b..167c903dfb2 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -128,10 +128,10 @@ (data $ok1 (i32.const 0) "a") ;; CHECK: (data $ok2 (i32.const 1000) "a") (data $ok2 (i32.const 1000) "a") - ;; CHECK: (data $ok3 (global.get $imported) "a") - (data $ok3 (global.get $imported) "a") - ;; CHECK: (data $bad (i32.const -2) "a") - (data $bad (i32.const -2) "a") + ;; CHECK: (data $ok3 (i32.const 131071) "a") + (data $ok3 (i32.const 131071) "a") + ;; CHECK: (data $bad (global.get $imported) "a") + (data $bad (global.get $imported) "a") ) ;; Finally, a module with no bad segments. We can remove all the contents. From f869759f1bdeb161fa15395d33dffccbd684be65 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 12:47:11 -0800 Subject: [PATCH 23/24] Add testcase for multiple memories, one removable and one not --- .../remove-unused-module-elements_tnh.wast | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 167c903dfb2..6f6f87ae982 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -221,3 +221,29 @@ (func $func) ) +;; Multiple memories. One can be removed, the other remains due to a trapping +;; segment. +(module + ;; CHECK: (memory $small 1 1) + (memory $small 1 1) + + (memory $big 2 2) + + ;; CHECK: (data $a (i32.const 100000) "ab") + (data $a (memory $small) (i32.const 100000) "ab") ;; fits in $big; not $small + + (data $b (memory $big) (i32.const 100000) "ab") +) + +;; Reverse order of memories. +(module + (memory $big 2 2) + + ;; CHECK: (memory $small 1 1) + (memory $small 1 1) + + ;; CHECK: (data $a (i32.const 100000) "ab") + (data $a (memory $small) (i32.const 100000) "ab") ;; fits in $big; not $small + + (data $b (memory $big) (i32.const 100000) "ab") +) From 34a991add3703fdd8021a14cf621882add215c35 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 25 Jan 2024 12:48:00 -0800 Subject: [PATCH 24/24] improve testcase slightly --- test/lit/passes/remove-unused-module-elements_tnh.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/remove-unused-module-elements_tnh.wast b/test/lit/passes/remove-unused-module-elements_tnh.wast index 6f6f87ae982..22c09740dfc 100644 --- a/test/lit/passes/remove-unused-module-elements_tnh.wast +++ b/test/lit/passes/remove-unused-module-elements_tnh.wast @@ -232,7 +232,7 @@ ;; CHECK: (data $a (i32.const 100000) "ab") (data $a (memory $small) (i32.const 100000) "ab") ;; fits in $big; not $small - (data $b (memory $big) (i32.const 100000) "ab") + (data $b (memory $big) (i32.const 100000) "cd") ) ;; Reverse order of memories. @@ -245,5 +245,5 @@ ;; CHECK: (data $a (i32.const 100000) "ab") (data $a (memory $small) (i32.const 100000) "ab") ;; fits in $big; not $small - (data $b (memory $big) (i32.const 100000) "ab") + (data $b (memory $big) (i32.const 100000) "cd") )