diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2020-11-19 21:51:55 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-19 21:51:55 -0800 |
commit | 171cba44fe6fdaff63fff79d2c660b02d7a79747 (patch) | |
tree | 509b906a080e318bf4de6cca7d3dd822c41282c2 | |
parent | de5e7365957b3eaf0d9aa05eea4ee759efb67ca4 (diff) | |
download | binaryen-171cba44fe6fdaff63fff79d2c660b02d7a79747.tar.gz binaryen-171cba44fe6fdaff63fff79d2c660b02d7a79747.tar.bz2 binaryen-171cba44fe6fdaff63fff79d2c660b02d7a79747.zip |
Initial wasm-split tool (#3359)
Implement an initial version of the wasm-split tool, which splits modules into a
primary module and a secondary module that can be instantiated after the primary
module. Eventually, this tool will be able to not only split modules, but also
instrument modules to collect profiles that will be able to guide later
splitting. In this initial version, however, wasm-split can neither perform
instrumentation nor consume any kind of profile data.
Despite those shortcomings, this initial version of the tool is already able to
perform module splitting according to function lists manually provided by the
user via the command line. Follow-up PRs will implement the stubbed out
instrumentation and profile consumption functionality.
-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) +) |