Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple noinline instances with different arguments. #6687

Merged
merged 11 commits into from
Jul 15, 2024
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ v118
- The build-time option to use legacy WasmGC opcodes is removed.
- The strings in `string.const` instructions must now be valid WTF-8.
- The `TraverseCalls` flag for `ExpressionRunner` is removed.
- Passes can now receive individual pass arguments, that is --foo=A --foo=B for
a pass foo will run the pass twice (which was possible before) and will now
run it first with argument A and second with B. --pass-arg=foo@BAR will now
apply to the most recent --foo pass on the commandline, if foo is a pass
(while global pass arguments - that are not the name of a pass - remain, as
before, global for all passes).

v117
----
Expand Down
10 changes: 8 additions & 2 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5453,7 +5453,10 @@ void BinaryenModuleRunPasses(BinaryenModuleRef module,
PassRunner passRunner((Module*)module);
passRunner.options = globalPassOptions;
for (BinaryenIndex i = 0; i < numPasses; i++) {
passRunner.add(passes[i]);
passRunner.add(passes[i],
globalPassOptions.arguments.count(passes[i]) > 0
? globalPassOptions.arguments[passes[i]]
: std::optional<std::string>());
DirtyHairy marked this conversation as resolved.
Show resolved Hide resolved
}
passRunner.run();
}
Expand Down Expand Up @@ -5704,7 +5707,10 @@ void BinaryenFunctionRunPasses(BinaryenFunctionRef func,
PassRunner passRunner((Module*)module);
passRunner.options = globalPassOptions;
for (BinaryenIndex i = 0; i < numPasses; i++) {
passRunner.add(passes[i]);
passRunner.add(passes[i],
globalPassOptions.arguments.count(passes[i]) > 0
? globalPassOptions.arguments[passes[i]]
: std::optional<std::string>());
}
passRunner.runOnFunction((Function*)func);
}
Expand Down
25 changes: 22 additions & 3 deletions src/pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ struct PassRegistry {
using Creator = std::function<Pass*()>;

void registerPass(const char* name, const char* description, Creator create);

// Register a pass that's used for internal testing. These passes do not show
// up in --help.
void
registerTestPass(const char* name, const char* description, Creator create);
std::unique_ptr<Pass> createPass(std::string name);
std::vector<std::string> getRegisteredNames();
bool containsPass(const std::string& name);
std::string getPassDescription(std::string name);
bool isPassHidden(std::string name);

Expand Down Expand Up @@ -103,6 +105,8 @@ class EffectAnalyzer;
using FuncEffectsMap = std::unordered_map<Name, EffectAnalyzer>;

struct PassOptions {
friend Pass;

// Run passes in debug mode, doing extra validation and timing checks.
bool debug = false;
// Whether to run the validator to check for errors.
Expand Down Expand Up @@ -269,6 +273,7 @@ struct PassOptions {
return PassOptions(); // defaults are to not optimize
}

private:
bool hasArgument(std::string key) { return arguments.count(key) > 0; }

std::string getArgument(std::string key, std::string errorTextIfMissing) {
Expand Down Expand Up @@ -322,9 +327,8 @@ struct PassRunner {
}

// Add a pass using its name.
void add(std::string passName) {
doAdd(PassRegistry::get()->createPass(passName));
}
void add(std::string passName,
std::optional<std::string> passArg = std::nullopt);

// Add a pass given an instance.
void add(std::unique_ptr<Pass> pass) { doAdd(std::move(pass)); }
Expand Down Expand Up @@ -486,6 +490,8 @@ class Pass {
// to imports must override this to return true.
virtual bool addsEffects() { return false; }

void setPassArg(const std::string& value) { passArg = value; }

std::string name;

PassRunner* getPassRunner() { return runner; }
Expand All @@ -497,6 +503,19 @@ class Pass {
PassOptions& getPassOptions() { return runner->options; }

protected:
bool hasArgument(const std::string& key);
std::string getArgument(const std::string& key,
const std::string& errorTextIfMissing);
std::string getArgumentOrDefault(const std::string& key,
const std::string& defaultValue);

// The main argument of the pass, which can be specified individually for
// every pass . getArgument() and friends will refer to this value if queried
// for a key that matches the pass name. All other arguments are taken from
// the runner / passOptions and therefore are global for all instances of a
// pass.
std::optional<std::string> passArg;

Pass() = default;
Pass(const Pass&) = default;
Pass(Pass&&) = default;
Expand Down
39 changes: 17 additions & 22 deletions src/passes/Asyncify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1608,54 +1608,49 @@ struct Asyncify : public Pass {
bool addsEffects() override { return true; }

void run(Module* module) override {
auto& options = getPassOptions();
bool optimize = options.optimizeLevel > 0;
bool optimize = getPassOptions().optimizeLevel > 0;

// Find which things can change the state.
auto stateChangingImports = String::trim(read_possible_response_file(
options.getArgumentOrDefault("asyncify-imports", "")));
auto ignoreImports =
options.getArgumentOrDefault("asyncify-ignore-imports", "");
getArgumentOrDefault("asyncify-imports", "")));
auto ignoreImports = getArgumentOrDefault("asyncify-ignore-imports", "");
bool allImportsCanChangeState =
stateChangingImports == "" && ignoreImports == "";
String::Split listedImports(stateChangingImports,
String::Split::NewLineOr(","));
// canIndirectChangeState is the default. asyncify-ignore-indirect sets it
// to false.
auto canIndirectChangeState =
!options.hasArgument("asyncify-ignore-indirect");
auto canIndirectChangeState = !hasArgument("asyncify-ignore-indirect");
std::string removeListInput =
options.getArgumentOrDefault("asyncify-removelist", "");
getArgumentOrDefault("asyncify-removelist", "");
if (removeListInput.empty()) {
// Support old name for now to avoid immediate breakage TODO remove
removeListInput = options.getArgumentOrDefault("asyncify-blacklist", "");
removeListInput = getArgumentOrDefault("asyncify-blacklist", "");
}
String::Split removeList(
String::trim(read_possible_response_file(removeListInput)),
String::Split::NewLineOr(","));
String::Split addList(
String::trim(read_possible_response_file(
options.getArgumentOrDefault("asyncify-addlist", ""))),
String::Split::NewLineOr(","));
std::string onlyListInput =
options.getArgumentOrDefault("asyncify-onlylist", "");
String::Split addList(String::trim(read_possible_response_file(
getArgumentOrDefault("asyncify-addlist", ""))),
String::Split::NewLineOr(","));
std::string onlyListInput = getArgumentOrDefault("asyncify-onlylist", "");
if (onlyListInput.empty()) {
// Support old name for now to avoid immediate breakage TODO remove
onlyListInput = options.getArgumentOrDefault("asyncify-whitelist", "");
onlyListInput = getArgumentOrDefault("asyncify-whitelist", "");
}
String::Split onlyList(
String::trim(read_possible_response_file(onlyListInput)),
String::Split::NewLineOr(","));
auto asserts = options.hasArgument("asyncify-asserts");
auto verbose = options.hasArgument("asyncify-verbose");
auto relocatable = options.hasArgument("asyncify-relocatable");
auto secondaryMemory = options.hasArgument("asyncify-in-secondary-memory");
auto propagateAddList = options.hasArgument("asyncify-propagate-addlist");
auto asserts = hasArgument("asyncify-asserts");
auto verbose = hasArgument("asyncify-verbose");
auto relocatable = hasArgument("asyncify-relocatable");
auto secondaryMemory = hasArgument("asyncify-in-secondary-memory");
auto propagateAddList = hasArgument("asyncify-propagate-addlist");

// Ensure there is a memory, as we need it.
if (secondaryMemory) {
auto secondaryMemorySizeString =
options.getArgumentOrDefault("asyncify-secondary-memory-size", "1");
getArgumentOrDefault("asyncify-secondary-memory-size", "1");
Address secondaryMemorySize = std::stoi(secondaryMemorySizeString);
asyncifyMemory = createSecondaryMemory(module, secondaryMemorySize);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/passes/Directize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ struct Directize : public Pass {

// TODO: consider a per-table option here
auto initialContentsImmutable =
getPassOptions().hasArgument("directize-initial-contents-immutable");
hasArgument("directize-initial-contents-immutable");

// Set up the initial info.
TableInfoMap tables;
Expand Down
9 changes: 4 additions & 5 deletions src/passes/ExtractFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct ExtractFunction : public Pass {
bool addsEffects() override { return true; }

void run(Module* module) override {
Name name = getPassOptions().getArgument(
Name name = getArgument(
"extract-function",
"ExtractFunction usage: wasm-opt --extract-function=FUNCTION_NAME");
extract(getPassRunner(), module, name);
Expand All @@ -74,10 +74,9 @@ struct ExtractFunctionIndex : public Pass {
bool addsEffects() override { return true; }

void run(Module* module) override {
std::string index =
getPassOptions().getArgument("extract-function-index",
"ExtractFunctionIndex usage: wasm-opt "
"--extract-function-index=FUNCTION_INDEX");
std::string index = getArgument("extract-function-index",
"ExtractFunctionIndex usage: wasm-opt "
"--extract-function-index=FUNCTION_INDEX");
for (char c : index) {
if (!std::isdigit(c)) {
Fatal() << "Expected numeric function index";
Expand Down
3 changes: 1 addition & 2 deletions src/passes/FuncCastEmulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ struct FuncCastEmulation : public Pass {
bool addsEffects() override { return true; }

void run(Module* module) override {
Index numParams = std::stoul(
getPassOptions().getArgumentOrDefault("max-func-params", "16"));
Index numParams = std::stoul(getArgumentOrDefault("max-func-params", "16"));
// we just need the one ABI function type for all indirect calls
HeapType ABIType(
Signature(Type(std::vector<Type>(numParams, Type::i64)), Type::i64));
Expand Down
11 changes: 5 additions & 6 deletions src/passes/JSPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,17 @@ struct JSPI : public Pass {
void run(Module* module) override {
Builder builder(*module);

auto& options = getPassOptions();
// Find which imports can suspend.
auto stateChangingImports = String::trim(read_possible_response_file(
options.getArgumentOrDefault("jspi-imports", "")));
auto stateChangingImports = String::trim(
read_possible_response_file(getArgumentOrDefault("jspi-imports", "")));
String::Split listedImports(stateChangingImports, ",");

// Find which exports should create a promise.
auto stateChangingExports = String::trim(read_possible_response_file(
options.getArgumentOrDefault("jspi-exports", "")));
auto stateChangingExports = String::trim(
read_possible_response_file(getArgumentOrDefault("jspi-exports", "")));
String::Split listedExports(stateChangingExports, ",");

bool wasmSplit = options.hasArgument("jspi-split-module");
bool wasmSplit = hasArgument("jspi-split-module");
if (wasmSplit) {
// Make an import for the load secondary module function so a JSPI wrapper
// version will be created.
Expand Down
5 changes: 2 additions & 3 deletions src/passes/LegalizeJSInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ struct LegalizeJSInterface : public Pass {
setTempRet0 = nullptr;
getTempRet0 = nullptr;
auto exportOriginals =
getPassOptions().hasArgument("legalize-js-interface-export-originals");
exportedHelpers =
getPassOptions().hasArgument("legalize-js-interface-exported-helpers");
hasArgument("legalize-js-interface-export-originals");
exportedHelpers = hasArgument("legalize-js-interface-exported-helpers");
// for each illegal export, we must export a legalized stub instead
std::vector<std::unique_ptr<Export>> newExports;
for (auto& ex : module->exports) {
Expand Down
3 changes: 1 addition & 2 deletions src/passes/LogExecution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ struct LogExecution : public WalkerPass<PostWalker<LogExecution>> {
bool addsEffects() override { return true; }

void run(Module* module) override {
auto& options = getPassOptions();
loggerModule = options.getArgumentOrDefault("log-execution", "");
loggerModule = getArgumentOrDefault("log-execution", "");
super::run(module);
}

Expand Down
4 changes: 2 additions & 2 deletions src/passes/NoInline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ struct NoInline : public Pass {
NoInline(NoInlineMode mode) : mode(mode) {}

void run(Module* module) override {
std::string pattern = getPassOptions().getArgument(
name, "Usage usage: wasm-opt --" + name + "=WILDCARD");
std::string pattern =
getArgument(name, "Usage usage: wasm-opt --" + name + "=WILDCARD");

for (auto& func : module->functions) {
if (!String::wildcardMatch(pattern, func->name.toString())) {
Expand Down
6 changes: 2 additions & 4 deletions src/passes/PostEmscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ struct PostEmscripten : public Pass {
std::vector<Address> segmentOffsets; // segment index => address offset
calcSegmentOffsets(module, segmentOffsets);

auto& options = getPassOptions();
auto sideModule = options.hasArgument("post-emscripten-side-module");
auto sideModule = hasArgument("post-emscripten-side-module");
if (!sideModule) {
removeData(module, segmentOffsets, "__start_em_asm", "__stop_em_asm");
removeData(module, segmentOffsets, "__start_em_js", "__stop_em_js");
Expand All @@ -235,8 +234,7 @@ struct PostEmscripten : public Pass {
}

void removeEmJsExports(Module& module) {
auto& options = getPassOptions();
auto sideModule = options.hasArgument("post-emscripten-side-module");
auto sideModule = hasArgument("post-emscripten-side-module");
EmJsWalker walker(sideModule);
walker.walkModule(&module);
for (const Export& exp : walker.toRemove) {
Expand Down
2 changes: 1 addition & 1 deletion src/passes/PrintFunctionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct PrintFunctionMap : public Pass {
void run(Module* module) override {
// If an argument is provided, write to that file; otherwise write to
// stdout.
auto outFile = getPassOptions().getArgumentOrDefault("symbolmap", "");
auto outFile = getArgumentOrDefault("symbolmap", "");
Output output(outFile, Flags::Text);
auto& o = output.getStream();
Index i = 0;
Expand Down
14 changes: 7 additions & 7 deletions src/passes/SeparateDataSegments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ struct SeparateDataSegments : public Pass {

void run(Module* module) override {
std::string outfileName =
getPassOptions().getArgument("separate-data-segments",
"SeparateDataSegments usage: wasm-opt "
"--separate-data-segments@FILENAME");
getArgument("separate-data-segments",
"SeparateDataSegments usage: wasm-opt "
"--separate-data-segments@FILENAME");
Output outfile(outfileName, Flags::Binary);
std::string baseStr = getPassOptions().getArgument(
"separate-data-segments-global-base",
"SeparateDataSegments usage: wasm-opt "
"--pass-arg=separate-data-segments-global-base@NUMBER");
std::string baseStr =
getArgument("separate-data-segments-global-base",
"SeparateDataSegments usage: wasm-opt "
"--pass-arg=separate-data-segments-global-base@NUMBER");
Address base = std::stoi(baseStr);
size_t lastEnd = 0;
for (auto& seg : module->dataSegments) {
Expand Down
6 changes: 3 additions & 3 deletions src/passes/SetGlobals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ struct SetGlobals : public Pass {
bool requiresNonNullableLocalFixups() override { return false; }

void run(Module* module) override {
Name input = getPassRunner()->options.getArgument(
"set-globals",
"SetGlobals usage: wasm-opt --pass-arg=set-globals@x=y,z=w");
Name input =
getArgument("set-globals",
"SetGlobals usage: wasm-opt --pass-arg=set-globals@x=y,z=w");

// The input is a set of X=Y pairs separated by commas.
String::Split pairs(input.toString(), ",");
Expand Down
3 changes: 1 addition & 2 deletions src/passes/StackCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ struct StackCheck : public Pass {
auto stackLimitName = Names::getValidGlobalName(*module, "__stack_limit");

Name handler;
auto handlerName =
getPassOptions().getArgumentOrDefault("stack-check-handler", "");
auto handlerName = getArgumentOrDefault("stack-check-handler", "");
if (handlerName != "") {
handler = handlerName;
importStackOverflowHandler(
Expand Down
8 changes: 4 additions & 4 deletions src/passes/TraceCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ struct TraceCalls : public Pass {
bool addsEffects() override { return true; }

void run(Module* module) override {
auto functionsDefinitions = getPassOptions().getArgument(
"trace-calls",
"TraceCalls usage: wasm-opt "
"--trace-calls=FUNCTION_TO_TRACE[:TRACER_NAME][,...]");
auto functionsDefinitions =
getArgument("trace-calls",
"TraceCalls usage: wasm-opt "
"--trace-calls=FUNCTION_TO_TRACE[:TRACER_NAME][,...]");

auto tracedFunctions = parseArgument(functionsDefinitions);

Expand Down
Loading
Loading