text: /*
   * Copyright 2016 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.
   */
  
  #ifndef wasm_tools_optimization_options_h
  #define wasm_tools_optimization_options_h
  
  #include "tool-options.h"
  
  //
  // Shared optimization options for commandline tools
  //
  
  namespace wasm {
  
  struct OptimizationOptions : public ToolOptions {
    static constexpr const char* DEFAULT_OPT_PASSES = "O";
  
    std::vector<std::string> passes;
  
    OptimizationOptions(const std::string& command,
                        const std::string& description)
      : ToolOptions(command, description) {
      (*this)
        .add("",
             "-O",
             "execute default optimization passes",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.setDefaultOptimizationOptions();
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add("",
             "-O0",
             "execute no optimization passes",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 0;
               passOptions.shrinkLevel = 0;
             })
        .add("",
             "-O1",
             "execute -O1 optimization passes (quick&useful opts, useful for "
             "iteration builds)",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 1;
               passOptions.shrinkLevel = 0;
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add(
          "",
          "-O2",
          "execute -O2 optimization passes (most opts, generally gets most perf)",
          Options::Arguments::Zero,
          [this](Options*, const std::string&) {
            passOptions.optimizeLevel = 2;
            passOptions.shrinkLevel = 0;
            passes.push_back(DEFAULT_OPT_PASSES);
          })
        .add("",
             "-O3",
             "execute -O3 optimization passes (spends potentially a lot of time "
             "optimizing)",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 3;
               passOptions.shrinkLevel = 0;
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add("",
             "-O4",
             "execute -O4 optimization passes (also flatten the IR, which can "
             "take a lot more time and memory, but is useful on more nested / "
             "complex / less-optimized input)",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 4;
               passOptions.shrinkLevel = 0;
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add("",
             "-Os",
             "execute default optimization passes, focusing on code size",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 2;
               passOptions.shrinkLevel = 1;
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add("",
             "-Oz",
             "execute default optimization passes, super-focusing on code size",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.optimizeLevel = 2;
               passOptions.shrinkLevel = 2;
               passes.push_back(DEFAULT_OPT_PASSES);
             })
        .add("--optimize-level",
             "-ol",
             "How much to focus on optimizing code",
             Options::Arguments::One,
             [this](Options* o, const std::string& argument) {
               passOptions.optimizeLevel = atoi(argument.c_str());
             })
        .add("--shrink-level",
             "-s",
             "How much to focus on shrinking code size",
             Options::Arguments::One,
             [this](Options* o, const std::string& argument) {
               passOptions.shrinkLevel = atoi(argument.c_str());
             })
        .add("--debuginfo",
             "-g",
             "Emit names section in wasm binary (or full debuginfo in wast)",
             Options::Arguments::Zero,
             [&](Options* o, const std::string& arguments) {
               passOptions.debugInfo = true;
             })
        .add("--always-inline-max-function-size",
             "-aimfs",
             "Max size of functions that are always inlined (default " +
               std::to_string(InliningOptions().alwaysInlineMaxSize) +
               ", which "
               "is safe for use with -Os builds)",
             Options::Arguments::One,
             [this](Options* o, const std::string& argument) {
               passOptions.inlining.alwaysInlineMaxSize =
                 static_cast<Index>(atoi(argument.c_str()));
             })
        .add("--flexible-inline-max-function-size",
             "-fimfs",
             "Max size of functions that are inlined when lightweight (no loops "
             "or function calls) when optimizing aggressively for speed (-O3). "
             "Default: " +
               std::to_string(InliningOptions().flexibleInlineMaxSize),
             Options::Arguments::One,
             [this](Options* o, const std::string& argument) {
               passOptions.inlining.flexibleInlineMaxSize =
                 static_cast<Index>(atoi(argument.c_str()));
             })
        .add("--one-caller-inline-max-function-size",
             "-ocimfs",
             "Max size of functions that are inlined when there is only one "
             "caller (default -1, which means all such functions are inlined)",
             Options::Arguments::One,
             [this](Options* o, const std::string& argument) {
               static_assert(InliningOptions().oneCallerInlineMaxSize ==
                               Index(-1),
                             "the help text here is written to assume -1");
               passOptions.inlining.oneCallerInlineMaxSize =
                 static_cast<Index>(atoi(argument.c_str()));
             })
        .add("--inline-functions-with-loops",
             "-ifwl",
             "Allow inlining functions with loops",
             Options::Arguments::Zero,
             [this](Options* o, const std::string&) {
               passOptions.inlining.allowFunctionsWithLoops = true;
             })
        .add("--ignore-implicit-traps",
             "-iit",
             "Optimize under the helpful assumption that no surprising traps "
             "occur (from load, div/mod, etc.)",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.ignoreImplicitTraps = true;
             })
        .add("--low-memory-unused",
             "-lmu",
             "Optimize under the helpful assumption that the low 1K of memory is "
             "not used by the application",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.lowMemoryUnused = true;
             })
        .add(
          "--fast-math",
          "-ffm",
          "Optimize floats without handling corner cases of NaNs and rounding",
          Options::Arguments::Zero,
          [this](Options*, const std::string&) { passOptions.fastMath = true; })
        .add("--zero-filled-memory",
             "-uim",
             "Assume that an imported memory will be zero-initialized",
             Options::Arguments::Zero,
             [this](Options*, const std::string&) {
               passOptions.zeroFilledMemory = true;
             });
      // add passes in registry
      for (const auto& p : PassRegistry::get()->getRegisteredNames()) {
        (*this).add(std::string("--") + p,
                    "",
                    PassRegistry::get()->getPassDescription(p),
                    // Allow an optional parameter to a pass. If provided, it is
                    // the same as if using --pass-arg, that is,
                    //
                    //   --foo=ARG
                    //
                    // is the same as
                    //
                    //   --foo --pass-arg=foo@ARG
                    Options::Arguments::Optional,
                    [this, p](Options*, const std::string& arg) {
                      if (!arg.empty()) {
                        if (passOptions.arguments.count(p)) {
                          Fatal()
                            << "Cannot pass multiple pass arguments to " << p;
                        }
                        passOptions.arguments[p] = arg;
                      }
                      passes.push_back(p);
                    });
      }
    }
  
    bool runningDefaultOptimizationPasses() {
      for (auto& pass : passes) {
        if (pass == DEFAULT_OPT_PASSES) {
          return true;
        }
      }
      return false;
    }
  
    bool runningPasses() { return passes.size() > 0; }
  
    void runPasses(Module& wasm) {
      PassRunner passRunner(&wasm, passOptions);
      if (debug) {
        passRunner.setDebug(true);
      }
      for (auto& pass : passes) {
        if (pass == DEFAULT_OPT_PASSES) {
          passRunner.addDefaultOptimizationPasses();
        } else {
          passRunner.add(pass);
        }
      }
      passRunner.run();
    }
  };
  
  } // namespace wasm
  
  #endif
  