diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index af31ff20b37..735e747938f 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -73,6 +73,7 @@ // from the IR before splitting. // #include "ir/module-splitting.h" +#include "ir/effects.h" #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" @@ -790,6 +791,16 @@ void ModuleSplitter::shareImportableItems() { primaryUsed.globals.insert(tableManager.activeBase.global); } + // Trapping globals should stay in the primary module to preserve the trapping + // behavior upon instantiation. + for (auto& global : primary.globals) { + if (global->init && + EffectAnalyzer(config.passOptions, primary, global->init) + .hasUnremovableSideEffects()) { + primaryUsed.globals.insert(global->name); + } + } + // 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 diff --git a/src/ir/module-splitting.h b/src/ir/module-splitting.h index 8260feb6b8d..fd21d052061 100644 --- a/src/ir/module-splitting.h +++ b/src/ir/module-splitting.h @@ -44,11 +44,14 @@ #ifndef wasm_ir_module_splitting_h #define wasm_ir_module_splitting_h +#include "pass.h" #include "wasm.h" namespace wasm::ModuleSplitting { struct Config { + // Pass options to use for effects analysis + PassOptions passOptions; // A vector of set of functions to split into that secondary. Each function // set belongs to a single secondary module. All others are kept in the // primary module. Must not include the start function if it exists. May or diff --git a/src/tools/wasm-split/split-options.cpp b/src/tools/wasm-split/split-options.cpp index dcea3bcf227..d62d22ad765 100644 --- a/src/tools/wasm-split/split-options.cpp +++ b/src/tools/wasm-split/split-options.cpp @@ -358,6 +358,16 @@ WasmSplitOptions::WasmSplitOptions() {Mode::Split, Mode::MultiSplit, Mode::Instrument}, Options::Arguments::Zero, [&](Options* o, const std::string& arguments) { stripDebug = true; }) + .add("--traps-never-happen", + "-tnh", + "Split under the helpful assumption that no trap is reached at " + "runtime (from load, div/mod, etc.)", + WasmSplitOption, + {Mode::Split, Mode::MultiSplit}, + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { + passOptions.trapsNeverHappen = true; + }) .add("--output", "-o", "Output file.", diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 82a3e9ab860..8faa1bef488 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -229,6 +229,7 @@ void writePlaceholderMap( void setCommonSplitConfigs(ModuleSplitting::Config& config, const WasmSplitOptions& options) { + config.passOptions = options.passOptions; config.usePlaceholders = options.usePlaceholders; config.minimizeNewExportNames = !options.passOptions.debugInfo; if (options.importNamespace) { diff --git a/test/lit/help/wasm-split.test b/test/lit/help/wasm-split.test index 950d3e5cea9..3503a46675d 100644 --- a/test/lit/help/wasm-split.test +++ b/test/lit/help/wasm-split.test @@ -151,6 +151,11 @@ ;; CHECK-NEXT: --strip-debug [split, multi-split, instrument] Strip ;; CHECK-NEXT: debug info (including the names section) ;; CHECK-NEXT: +;; CHECK-NEXT: --traps-never-happen,-tnh [split, multi-split] Split under the +;; CHECK-NEXT: helpful assumption that no trap is +;; CHECK-NEXT: reached at runtime (from load, div/mod, +;; CHECK-NEXT: etc.) +;; CHECK-NEXT: ;; CHECK-NEXT: --output,-o [instrument, merge-profiles, multi-split] ;; CHECK-NEXT: Output file. ;; CHECK-NEXT: diff --git a/test/lit/wasm-split/trapping-global.wast b/test/lit/wasm-split/trapping-global.wast new file mode 100644 index 00000000000..388973d835d --- /dev/null +++ b/test/lit/wasm-split/trapping-global.wast @@ -0,0 +1,24 @@ +;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split +;; RUN: wasm-dis -all %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-split %s -all -g -o1 %t.tnh.1.wasm -o2 %t.tnh.2.wasm --split-funcs=split --traps-never-happen +;; RUN: wasm-dis -all %t.tnh.1.wasm | filecheck %s --check-prefix PRIMARY-TNH + +;; This unused global should NOT be removed by wasm-split because its +;; initializer contains a side effect (a trap due to a null descriptor). +;; However, if we pass --traps-never-happen, we assume traps never occur, so the +;; global will be considered to have no side effects and will be removed. +(module + (rec + (type $struct (descriptor $desc) (struct)) + (type $desc (describes $struct) (struct)) + ) + ;; PRIMARY: (global $trap (ref $struct) + ;; PRIMARY-TNH-NOT: (global $trap (ref $struct) + (global $trap (ref $struct) + (struct.new_desc $struct + (ref.null none) + ) + ) + + (func $split) +)