diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/tools/tool-options.h | 2 | ||||
-rw-r--r-- | src/tools/wasm-split.cpp | 367 | ||||
-rw-r--r-- | test/lit/wasm-split/basic.wast | 141 | ||||
-rw-r--r-- | test/lit/wasm-split/invalid-options.wast | 64 | ||||
-rw-r--r-- | test/lit/wasm-split/verbose.wast | 14 |
6 files changed, 588 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5af62c9c8..708eec54f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,7 @@ binaryen_add_executable(wasm-as src/tools/wasm-as.cpp) binaryen_add_executable(wasm-dis src/tools/wasm-dis.cpp) binaryen_add_executable(wasm-ctor-eval src/tools/wasm-ctor-eval.cpp) binaryen_add_executable(wasm-reduce src/tools/wasm-reduce.cpp) +binaryen_add_executable(wasm-split src/tools/wasm-split.cpp) # binaryen.js diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index 83d5b38d7..4b084e191 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -140,7 +140,7 @@ struct ToolOptions : public Options { return *this; } - void applyFeatures(Module& module) { + void applyFeatures(Module& module) const { if (hasFeatureOptions) { if (!detectFeatures && module.hasFeaturesSection) { FeatureSet optionsFeatures = FeatureSet::MVP; diff --git a/src/tools/wasm-split.cpp b/src/tools/wasm-split.cpp new file mode 100644 index 000000000..5f6207e9a --- /dev/null +++ b/src/tools/wasm-split.cpp @@ -0,0 +1,367 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// wasm-split: Split a module in two or instrument a module to inform future +// splitting. + +#include "ir/module-splitting.h" +#include "ir/module-utils.h" +#include "support/name.h" +#include "support/utilities.h" +#include "tool-options.h" +#include "wasm-io.h" +#include "wasm-validator.h" +#include <sstream> + +using namespace wasm; + +namespace { + +const std::string DEFAULT_PROFILE_EXPORT("__write_profile"); + +std::set<Name> parseNameList(const std::string& list) { + std::set<Name> names; + std::istringstream stream(list); + for (std::string name; std::getline(stream, name, ',');) { + names.insert(name); + } + return names; +} + +struct WasmSplitOptions : ToolOptions { + bool verbose = false; + + bool instrument = false; + + std::string profileFile; + std::string profileExport = DEFAULT_PROFILE_EXPORT; + + std::set<Name> keepFuncs; + std::set<Name> splitFuncs; + + std::string input; + std::string output; + std::string primaryOutput; + std::string secondaryOutput; + + std::string importNamespace; + std::string placeholderNamespace; + std::string exportPrefix; + + WasmSplitOptions(); + bool validate(); + void parse(int argc, const char* argv[]); +}; + +WasmSplitOptions::WasmSplitOptions() + : ToolOptions("wasm-split", + "Split a module into a primary module and a secondary " + "module or instrument a module to gather a profile that " + "can inform future splitting.") { + (*this) + .add("--instrument", + "", + "Instrument the module to generate a profile that can be used to " + "guide splitting", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { instrument = true; }) + .add( + "--profile", + "", + "The profile to use to guide splitting. May not be used with " + "--instrument.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { profileFile = argument; }) + .add("--profile-export", + "", + "The export name of the function the embedder calls to write the " + "profile into memory. Defaults to `__write_profile`. Must be used " + "with --instrument.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + profileExport = argument; + }) + .add("--keep-funcs", + "", + "Comma-separated list of functions to keep in the primary module, " + "regardless of any profile.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + keepFuncs = parseNameList(argument); + }) + .add("--split-funcs", + "", + "Comma-separated list of functions to split into the secondary " + "module, regardless of any profile. If there is no profile, then " + "this defaults to all functions defined in the module.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + splitFuncs = parseNameList(argument); + }) + .add("--output", + "-o", + "Output file. Only usable with --instrument.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { output = argument; }) + .add("--primary-output", + "-o1", + "Output file for the primary module. Not usable with --instrument.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + primaryOutput = argument; + }) + .add("--secondary-output", + "-o2", + "Output file for the secondary module. Not usable with --instrument.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + secondaryOutput = argument; + }) + .add("--import-namespace", + "", + "The namespace from which to import objects from the primary " + "module into the secondary module.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + importNamespace = argument; + }) + .add("--placeholder-namespace", + "", + "The namespace from which to import placeholder functions into " + "the primary module.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + placeholderNamespace = argument; + }) + .add( + "--export-prefix", + "", + "An identifying prefix to prepend to new export names created " + "by module splitting.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { exportPrefix = argument; }) + .add("--verbose", + "-v", + "Verbose output mode. Prints the functions that will be kept " + "and split out when splitting a module.", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { + verbose = true; + quiet = false; + }) + .add_positional( + "INFILE", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { input = argument; }); +} + +bool WasmSplitOptions::validate() { + bool valid = true; + auto fail = [&](auto msg) { + std::cerr << "error: " << msg << "\n"; + valid = false; + }; + + if (!input.size()) { + fail("no input file"); + } + if (instrument) { + using Opt = std::pair<const std::string&, const std::string>; + for (auto& opt : {Opt{profileFile, "--profile"}, + Opt{primaryOutput, "primary output"}, + Opt{secondaryOutput, "secondary output"}, + Opt{importNamespace, "--import-namespace"}, + Opt{placeholderNamespace, "--placeholder-namespace"}, + Opt{exportPrefix, "--export-prefix"}}) { + if (opt.first.size()) { + fail(opt.second + " cannot be used with --instrument"); + } + } + if (keepFuncs.size()) { + fail("--keep-funcs cannot be used with --instrument"); + } + if (splitFuncs.size()) { + fail("--split-funcs cannot be used with --instrument"); + } + } else { + if (output.size()) { + fail( + "must provide separate primary and secondary output with -o1 and -o2"); + } + if (profileExport != DEFAULT_PROFILE_EXPORT) { + fail("--profile-export must be used with --instrument"); + } + } + + std::vector<Name> impossible; + std::set_intersection(keepFuncs.begin(), + keepFuncs.end(), + splitFuncs.begin(), + splitFuncs.end(), + std::inserter(impossible, impossible.end())); + for (auto& func : impossible) { + fail(std::string("Cannot both keep and split out function ") + + func.c_str()); + } + + return valid; +} + +void WasmSplitOptions::parse(int argc, const char* argv[]) { + ToolOptions::parse(argc, argv); + // Since --quiet is defined in ToolOptions but --verbose is defined here, + // --quiet doesn't know to unset --verbose. Fix it up here. + if (quiet && verbose) { + verbose = false; + } +} + +void parseInput(Module& wasm, const WasmSplitOptions& options) { + ModuleReader reader; + reader.setProfile(options.profile); + try { + reader.read(options.input, wasm); + } catch (ParseException& p) { + p.dump(std::cerr); + std::cerr << '\n'; + Fatal() << "error parsing wasm"; + } catch (std::bad_alloc&) { + Fatal() << "error building module, std::bad_alloc (possibly invalid " + "request for silly amounts of memory)"; + } + options.applyFeatures(wasm); +} + +void instrumentModule(Module& wasm, const WasmSplitOptions& options) { + Fatal() << "TODO: implement instrumentation\n"; +} + +void splitModule(Module& wasm, const WasmSplitOptions& options) { + std::set<Name> keepFuncs; + + if (options.profileFile.size()) { + // Use the profile to initialize `keepFuncs` + Fatal() << "TODO: implement reading profiles\n"; + } + + // Add in the functions specified with --keep-funcs + for (auto& func : options.keepFuncs) { + if (!options.quiet && wasm.getFunctionOrNull(func) == nullptr) { + std::cerr << "warning: function " << func << " does not exist\n"; + } + keepFuncs.insert(func); + } + + // Remove the functions specified with --remove-funcs + for (auto& func : options.splitFuncs) { + auto* function = wasm.getFunctionOrNull(func); + if (!options.quiet && function == nullptr) { + std::cerr << "warning: function " << func << " does not exist\n"; + } + if (function && function->imported()) { + if (!options.quiet) { + std::cerr << "warning: cannot split out imported function " << func + << "\n"; + } + } else { + keepFuncs.erase(func); + } + } + + if (!options.quiet && keepFuncs.size() == 0) { + std::cerr << "warning: not keeping any functions in the primary module\n"; + } + + // If warnings are enabled, check that any functions are being split out. + if (!options.quiet) { + std::set<Name> splitFuncs; + ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) { + if (keepFuncs.count(func->name) == 0) { + splitFuncs.insert(func->name); + } + }); + + if (splitFuncs.size() == 0) { + std::cerr + << "warning: not splitting any functions out to the secondary module\n"; + } + + // Dump the kept and split functions if we are verbose + if (options.verbose) { + auto printCommaSeparated = [&](auto funcs) { + for (auto it = funcs.begin(); it != funcs.end(); ++it) { + if (it != funcs.begin()) { + std::cout << ", "; + } + std::cout << *it; + } + }; + + std::cout << "Keeping functions: "; + printCommaSeparated(keepFuncs); + std::cout << "\n"; + + std::cout << "Splitting out functions: "; + printCommaSeparated(splitFuncs); + std::cout << "\n"; + } + } + + // Actually perform the splitting + ModuleSplitting::Config config; + config.primaryFuncs = std::move(keepFuncs); + if (options.importNamespace.size()) { + config.importNamespace = options.importNamespace; + } + if (options.placeholderNamespace.size()) { + config.placeholderNamespace = options.placeholderNamespace; + } + if (options.exportPrefix.size()) { + config.newExportPrefix = options.exportPrefix; + } + std::unique_ptr<Module> secondary = + ModuleSplitting::splitFunctions(wasm, config); + + // Write the output modules + ModuleWriter writer; + writer.setBinary(true); + writer.write(wasm, options.primaryOutput); + writer.write(*secondary, options.secondaryOutput); +} + +} // anonymous namespace + +int main(int argc, const char* argv[]) { + WasmSplitOptions options; + options.parse(argc, argv); + + if (!options.validate()) { + Fatal() << "Invalid command line arguments"; + } + + Module wasm; + parseInput(wasm, options); + + if (options.passOptions.validate && !WasmValidator().validate(wasm)) { + Fatal() << "error validating input"; + } + + if (options.instrument) { + instrumentModule(wasm, options); + } else { + splitModule(wasm, options); + } +} diff --git a/test/lit/wasm-split/basic.wast b/test/lit/wasm-split/basic.wast new file mode 100644 index 000000000..d282b0bd6 --- /dev/null +++ b/test/lit/wasm-split/basic.wast @@ -0,0 +1,141 @@ +;; RUN: wasm-split %s --export-prefix='%' -o1 %t.none.1.wasm -o2 %t.none.2.wasm -v 2>&1 \ +;; RUN: | filecheck %s --check-prefix KEEP-NONE +;; RUN: wasm-dis %t.none.1.wasm | filecheck %s --check-prefix KEEP-NONE-PRIMARY +;; RUN: wasm-dis %t.none.2.wasm | filecheck %s --check-prefix KEEP-NONE-SECONDARY + +;; RUN: wasm-split %s --export-prefix='%' -o1 %t.foo.1.wasm -o2 %t.foo.2.wasm --keep-funcs=foo -v 2>&1 \ +;; RUN: | filecheck %s --check-prefix KEEP-FOO +;; RUN: wasm-dis %t.foo.1.wasm | filecheck %s --check-prefix KEEP-FOO-PRIMARY +;; RUN: wasm-dis %t.foo.2.wasm | filecheck %s --check-prefix KEEP-FOO-SECONDARY + +;; RUN: wasm-split %s --export-prefix='%' -o1 %t.bar.1.wasm -o2 %t.bar.2.wasm --keep-funcs=bar -v 2>&1 \ +;; RUN: | filecheck %s --check-prefix KEEP-BAR +;; RUN: wasm-dis %t.bar.1.wasm | filecheck %s --check-prefix KEEP-BAR-PRIMARY +;; RUN: wasm-dis %t.bar.2.wasm | filecheck %s --check-prefix KEEP-BAR-SECONDARY + +;; RUN: wasm-split %s --export-prefix='%' -o1 %t.both.1.wasm -o2 %t.both.2.wasm --keep-funcs=foo,bar -v 2>&1 \ +;; RUN: | filecheck %s --check-prefix KEEP-BOTH +;; RUN: wasm-dis %t.both.1.wasm | filecheck %s --check-prefix KEEP-BOTH-PRIMARY +;; RUN: wasm-dis %t.both.2.wasm | filecheck %s --check-prefix KEEP-BOTH-SECONDARY + +(module + (table $table 1 1 funcref) + (elem (i32.const 0) $foo) + (func $foo (param i32) (result i32) + (call $bar (i32.const 0)) + ) + (func $bar (param i32) (result i32) + (call $foo (i32.const 1)) + ) +) + +;; KEEP-NONE: warning: not keeping any functions in the primary module + +;; KEEP-NONE-PRIMARY: (module +;; KEEP-NONE-PRIMARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-NONE-PRIMARY-NEXT: (import "placeholder" "0" (func $fimport$0 (param i32) (result i32))) +;; KEEP-NONE-PRIMARY-NEXT: (table $0 1 1 funcref) +;; KEEP-NONE-PRIMARY-NEXT: (elem (i32.const 0) $fimport$0) +;; KEEP-NONE-PRIMARY-NEXT: (export "%table" (table $0)) +;; KEEP-NONE-PRIMARY-NEXT: ) + +;; KEEP-NONE-SECONDARY: (module +;; KEEP-NONE-SECONDARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-NONE-SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 1 1 funcref)) +;; KEEP-NONE-SECONDARY-NEXT: (elem (i32.const 0) $1) +;; KEEP-NONE-SECONDARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-NONE-SECONDARY-NEXT: (call $1 +;; KEEP-NONE-SECONDARY-NEXT: (i32.const 1) +;; KEEP-NONE-SECONDARY-NEXT: ) +;; KEEP-NONE-SECONDARY-NEXT: ) +;; KEEP-NONE-SECONDARY-NEXT: (func $1 (param $0 i32) (result i32) +;; KEEP-NONE-SECONDARY-NEXT: (call $0 +;; KEEP-NONE-SECONDARY-NEXT: (i32.const 0) +;; KEEP-NONE-SECONDARY-NEXT: ) +;; KEEP-NONE-SECONDARY-NEXT: ) +;; KEEP-NONE-SECONDARY-NEXT: ) + +;; KEEP-FOO: Keeping functions: foo{{$}} +;; KEEP-FOO-NEXT: Splitting out functions: bar{{$}} + +;; KEEP-FOO-PRIMARY: (module +;; KEEP-FOO-PRIMARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-FOO-PRIMARY-NEXT: (import "placeholder" "1" (func $fimport$0 (param i32) (result i32))) +;; KEEP-FOO-PRIMARY-NEXT: (table $0 2 2 funcref) +;; KEEP-FOO-PRIMARY-NEXT: (elem (i32.const 0) $0 $fimport$0) +;; KEEP-FOO-PRIMARY-NEXT: (export "%foo" (func $0)) +;; KEEP-FOO-PRIMARY-NEXT: (export "%table" (table $0)) +;; KEEP-FOO-PRIMARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-FOO-PRIMARY-NEXT: (call_indirect (type $i32_=>_i32) +;; KEEP-FOO-PRIMARY-NEXT: (i32.const 0) +;; KEEP-FOO-PRIMARY-NEXT: (i32.const 1) +;; KEEP-FOO-PRIMARY-NEXT: ) +;; KEEP-FOO-PRIMARY-NEXT: ) +;; KEEP-FOO-PRIMARY-NEXT: ) + +;; KEEP-FOO-SECONDARY: (module +;; KEEP-FOO-SECONDARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-FOO-SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 2 2 funcref)) +;; KEEP-FOO-SECONDARY-NEXT: (elem (i32.const 1) $0) +;; KEEP-FOO-SECONDARY-NEXT: (import "primary" "%foo" (func $fimport$0 (param i32) (result i32))) +;; KEEP-FOO-SECONDARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-FOO-SECONDARY-NEXT: (call $fimport$0 +;; KEEP-FOO-SECONDARY-NEXT: (i32.const 1) +;; KEEP-FOO-SECONDARY-NEXT: ) +;; KEEP-FOO-SECONDARY-NEXT: ) +;; KEEP-FOO-SECONDARY-NEXT: ) + +;; KEEP-BAR: Keeping functions: bar{{$}} +;; KEEP-BAR-NEXT: Splitting out functions: foo{{$}} + +;; KEEP-BAR-PRIMARY: (module +;; KEEP-BAR-PRIMARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-BAR-PRIMARY-NEXT: (import "placeholder" "0" (func $fimport$0 (param i32) (result i32))) +;; KEEP-BAR-PRIMARY-NEXT: (table $0 1 1 funcref) +;; KEEP-BAR-PRIMARY-NEXT: (elem (i32.const 0) $fimport$0) +;; KEEP-BAR-PRIMARY-NEXT: (export "%bar" (func $0)) +;; KEEP-BAR-PRIMARY-NEXT: (export "%table" (table $0)) +;; KEEP-BAR-PRIMARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-BAR-PRIMARY-NEXT: (call_indirect (type $i32_=>_i32) +;; KEEP-BAR-PRIMARY-NEXT: (i32.const 1) +;; KEEP-BAR-PRIMARY-NEXT: (i32.const 0) +;; KEEP-BAR-PRIMARY-NEXT: ) +;; KEEP-BAR-PRIMARY-NEXT: ) +;; KEEP-BAR-PRIMARY-NEXT: ) + +;; KEEP-BAR-SECONDARY: (module +;; KEEP-BAR-SECONDARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-BAR-SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 1 1 funcref)) +;; KEEP-BAR-SECONDARY-NEXT: (elem (i32.const 0) $0) +;; KEEP-BAR-SECONDARY-NEXT: (import "primary" "%bar" (func $fimport$0 (param i32) (result i32))) +;; KEEP-BAR-SECONDARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-BAR-SECONDARY-NEXT: (call $fimport$0 +;; KEEP-BAR-SECONDARY-NEXT: (i32.const 0) +;; KEEP-BAR-SECONDARY-NEXT: ) +;; KEEP-BAR-SECONDARY-NEXT: ) +;; KEEP-BAR-SECONDARY-NEXT: ) + +;; KEEP-BOTH: warning: not splitting any functions out to the secondary module +;; KEEP-BOTH-NEXT: Keeping functions: bar, foo{{$}} +;; KEEP-BOTH-NEXT: Splitting out functions:{{$}} + +;; KEEP-BOTH-PRIMARY: (module +;; KEEP-BOTH-PRIMARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32))) +;; KEEP-BOTH-PRIMARY-NEXT: (table $0 1 1 funcref) +;; KEEP-BOTH-PRIMARY-NEXT: (elem (i32.const 0) $0) +;; KEEP-BOTH-PRIMARY-NEXT: (export "%table" (table $0)) +;; KEEP-BOTH-PRIMARY-NEXT: (func $0 (param $0 i32) (result i32) +;; KEEP-BOTH-PRIMARY-NEXT: (call $1 +;; KEEP-BOTH-PRIMARY-NEXT: (i32.const 0) +;; KEEP-BOTH-PRIMARY-NEXT: ) +;; KEEP-BOTH-PRIMARY-NEXT: ) +;; KEEP-BOTH-PRIMARY-NEXT: (func $1 (param $0 i32) (result i32) +;; KEEP-BOTH-PRIMARY-NEXT: (call $0 +;; KEEP-BOTH-PRIMARY-NEXT: (i32.const 1) +;; KEEP-BOTH-PRIMARY-NEXT: ) +;; KEEP-BOTH-PRIMARY-NEXT: ) +;; KEEP-BOTH-PRIMARY-NEXT: ) + +;; KEEP-BOTH-SECONDARY: (module +;; KEEP-BOTH-SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 1 1 funcref)) +;; KEEP-BOTH-SECONDARY-NEXT: ) diff --git a/test/lit/wasm-split/invalid-options.wast b/test/lit/wasm-split/invalid-options.wast new file mode 100644 index 000000000..f4ef5b6b8 --- /dev/null +++ b/test/lit/wasm-split/invalid-options.wast @@ -0,0 +1,64 @@ +;; Test that invalid command line option combinations are properly rejected with +;; helpful error messages. + +;; --instrument cannot be used with --profile +;; RUN: not wasm-split %s --instrument --profile %t 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-PROFILE + +;; --instrument cannot be used with -o1 +;; RUN: not wasm-split %s --instrument -o1 %t 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-OUT1 + +;; --instrument cannot be used with -o2 +;; RUN: not wasm-split %s --instrument -o2 %t 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-OUT2 + +;; --instrument cannot be used with --import-namespace +;; RUN: not wasm-split %s --instrument --import-namespace=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-IMPORT-NS + +;; --instrument cannot be used with --placeholder-namespace +;; RUN: not wasm-split %s --instrument --placeholder-namespace=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-PLACEHOLDER-NS + +;; --instrument cannot be used with --export-prefix +;; RUN: not wasm-split %s --instrument --export-prefix=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-EXPORT-PREFIX + +;; --instrument cannot be used with --keep-funcs +;; RUN: not wasm-split %s --instrument --keep-funcs=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-KEEP-FUNCS + +;; --instrument cannot be used with --split-funcs +;; RUN: not wasm-split %s --instrument --split-funcs=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix INSTRUMENT-SPLIT-FUNCS + +;; --instrument requires -o1 and -o2 rather than -o +;; RUN: not wasm-split %s -o %t 2>&1 \ +;; RUN: | filecheck %s --check-prefix NO-INSTRUMENT-OUT + +;; --instrument is required to use --profile-export +;; RUN: not wasm-split %s --profile-export=foo 2>&1 \ +;; RUN: | filecheck %s --check-prefix NO-INSTRUMENT-PROFILE-EXPORT + +;; INSTRUMENT-PROFILE: error: --profile cannot be used with --instrument + +;; INSTRUMENT-OUT1: error: primary output cannot be used with --instrument + +;; INSTRUMENT-OUT2: error: secondary output cannot be used with --instrument + +;; INSTRUMENT-IMPORT-NS: error: --import-namespace cannot be used with --instrument + +;; INSTRUMENT-PLACEHOLDER-NS: error: --placeholder-namespace cannot be used with --instrument + +;; INSTRUMENT-EXPORT-PREFIX: error: --export-prefix cannot be used with --instrument + +;; INSTRUMENT-KEEP-FUNCS: error: --keep-funcs cannot be used with --instrument + +;; INSTRUMENT-SPLIT-FUNCS: error: --split-funcs cannot be used with --instrument + +;; NO-INSTRUMENT-OUT: error: must provide separate primary and secondary output with -o1 and -o2 + +;; NO-INSTRUMENT-PROFILE-EXPORT: error: --profile-export must be used with --instrument + +(module) diff --git a/test/lit/wasm-split/verbose.wast b/test/lit/wasm-split/verbose.wast new file mode 100644 index 000000000..cdedbfae6 --- /dev/null +++ b/test/lit/wasm-split/verbose.wast @@ -0,0 +1,14 @@ +;; Test that --verbose mode correctly prints the kept and split funcs + +;; RUN: wasm-split %s --keep-funcs=foo,bar --split-funcs=baz --verbose \ +;; RUN: -o1 %t1.wasm -o2 %t2.wasm | filecheck %s + +;; CHECK: Keeping functions: bar, foo{{$}} +;; CHECK: Splitting out functions: baz, quux{{$}} + +(module + (func $foo) + (func $bar) + (func $baz) + (func $quux) +) |