summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pass.h25
-rw-r--r--src/passes/Inlining.cpp31
-rw-r--r--src/tools/optimization-options.h35
3 files changed, 68 insertions, 23 deletions
diff --git a/src/pass.h b/src/pass.h
index 3917c8908..9f1be9a3b 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -56,6 +56,29 @@ private:
std::map<std::string, PassInfo> passInfos;
};
+struct InliningOptions {
+ // Function size at which we always inline.
+ // Typically a size so small that after optimizations, the inlined code will
+ // be smaller than the call instruction itself. 2 is a safe number because
+ // there is no risk of things like
+ // (func $reverse (param $x i32) (param $y i32)
+ // (call $something (local.get $y) (local.get $x))
+ // )
+ // in which case the reversing of the params means we'll possibly need
+ // a block and a temp local. But that takes at least 3 nodes, and 2 < 3.
+ // More generally, with 2 items we may have a local.get, but no way to
+ // require it to be saved instead of directly consumed.
+ Index alwaysInlineMaxSize = 2;
+ // Function size which we inline when functions are lightweight (no loops
+ // and calls) and we are doing aggressive optimisation for speed (-O3).
+ // In particular it's nice that with this limit we can inline the clamp
+ // functions (i32s-div, f64-to-int, etc.), that can affect perf.
+ Index flexibleInlineMaxSize = 20;
+ // Function size which we inline when there is only one caller.
+ // FIXME: this should logically be higher than flexibleInlineMaxSize.
+ Index oneCallerInlineMaxSize = 15;
+};
+
struct PassOptions {
// Run passes in debug mode, doing extra validation and timing checks.
bool debug = false;
@@ -67,6 +90,8 @@ struct PassOptions {
int optimizeLevel = 0;
// 0, 1, 2 correspond to -O0, -Os, -Oz
int shrinkLevel = 0;
+ // Tweak thresholds for the Inlining pass.
+ InliningOptions inlining;
// Optimize assuming things like div by 0, bad load/store, will not trap.
bool ignoreImplicitTraps = false;
// Optimize assuming that the low 1K of memory is not valid memory for the
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 04e82579c..5ed68a783 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -43,26 +43,6 @@
namespace wasm {
-// A limit on how big a function to inline when being careful about size
-static const int CAREFUL_SIZE_LIMIT = 15;
-
-// A limit on how big a function to inline when being more flexible. In
-// particular it's nice that with this limit we can inline the clamp
-// functions (i32s-div, f64-to-int, etc.), that can affect perf.
-static const int FLEXIBLE_SIZE_LIMIT = 20;
-
-// A size so small that after optimizations, the inlined code will be
-// smaller than the call instruction itself. 2 is a safe number because
-// there is no risk of things like
-// (func $reverse (param $x i32) (param $y i32)
-// (call $something (local.get $y) (local.get $x))
-// )
-// in which case the reversing of the params means we'll possibly need
-// a block and a temp local. But that takes at least 3 nodes, and 2 < 3.
-// More generally, with 2 items we may have a local.get, but no way to
-// require it to be saved instead of directly consumed.
-static const int INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT = 2;
-
// Useful into on a function, helping us decide if we can inline it
struct FunctionInfo {
std::atomic<Index> calls;
@@ -77,20 +57,25 @@ struct FunctionInfo {
usedGlobally = false;
}
+ // See pass.h for how defaults for these options were chosen.
bool worthInlining(PassOptions& options) {
// if it's big, it's just not worth doing (TODO: investigate more)
- if (size > FLEXIBLE_SIZE_LIMIT) {
+ if (size > options.inlining.flexibleInlineMaxSize) {
return false;
}
// if it's so small we have a guarantee that after we optimize the
// size will not increase, inline it
- if (size <= INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT) {
+ if (size <= options.inlining.alwaysInlineMaxSize) {
return true;
}
// if it has one use, then inlining it would likely reduce code size
// since we are just moving code around, + optimizing, so worth it
// if small enough that we are pretty sure its ok
- if (calls == 1 && !usedGlobally && size <= CAREFUL_SIZE_LIMIT) {
+ // FIXME: move this check to be first in this function, since we should
+ // return true if oneCallerInlineMaxSize is bigger than
+ // flexibleInlineMaxSize (which it typically should be).
+ if (calls == 1 && !usedGlobally &&
+ size <= options.inlining.oneCallerInlineMaxSize) {
return true;
}
// more than one use, so we can't eliminate it after inlining,
diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h
index 6af247924..7f769463c 100644
--- a/src/tools/optimization-options.h
+++ b/src/tools/optimization-options.h
@@ -123,6 +123,41 @@ struct OptimizationOptions : public ToolOptions {
[this](Options* o, const std::string& argument) {
passOptions.shrinkLevel = atoi(argument.c_str());
})
+ .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 " +
+ std::to_string(InliningOptions().oneCallerInlineMaxSize) +
+ "). Reason this is not unbounded is that some "
+ "implementations may have a hard time optimizing really large "
+ "functions",
+ Options::Arguments::One,
+ [this](Options* o, const std::string& argument) {
+ passOptions.inlining.oneCallerInlineMaxSize =
+ static_cast<Index>(atoi(argument.c_str()));
+ })
.add("--ignore-implicit-traps",
"-iit",
"Optimize under the helpful assumption that no surprising traps "