summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-07-01 13:37:36 -0700
committerGitHub <noreply@github.com>2021-07-01 13:37:36 -0700
commit45703971dffbbb1e4bbf7b026ca01cabf18b989a (patch)
treedc15c8d86323cf6254ad564ac62aa5150481cfd3 /src
parentca27f40a2f1070a16ee7c0efc18ff35d342d8027 (diff)
downloadbinaryen-45703971dffbbb1e4bbf7b026ca01cabf18b989a.tar.gz
binaryen-45703971dffbbb1e4bbf7b026ca01cabf18b989a.tar.bz2
binaryen-45703971dffbbb1e4bbf7b026ca01cabf18b989a.zip
Set an absolute maximum inlining limit (#3959)
We have seen some cases in both Chrome and Firefox where extremely large modules cause overhead, #3730 (comment) (and link therein) emscripten-core/emscripten#13899 (comment) There is no "right" value to use as a limit here, but pick an arbitrary one that is very high. (This setting is verified to have no effect on the emscripten benchmark suite.)
Diffstat (limited to 'src')
-rw-r--r--src/ir/utils.h6
-rw-r--r--src/passes/Inlining.cpp21
2 files changed, 27 insertions, 0 deletions
diff --git a/src/ir/utils.h b/src/ir/utils.h
index 157293e31..1964f148a 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -33,11 +33,17 @@ struct Measurer
void visitExpression(Expression* curr) { size++; }
+ // Measure the number of expressions.
static Index measure(Expression* tree) {
Measurer measurer;
measurer.walk(tree);
return measurer.size;
}
+
+ // A rough estimate of average binary size per expression. The number here is
+ // based on measurements on real-world (MVP) wasm files, on which observed
+ // ratios were 2.2 - 2.8.
+ static constexpr double BytesPerExpr = 2.5;
};
struct ExpressionAnalyzer {
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 1a84918bb..7c51e78cb 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -420,6 +420,9 @@ struct Inlining : public Pass {
continue;
}
Name inlinedName = inlinedFunction->name;
+ if (!isUnderSizeLimit(func->name, inlinedName)) {
+ continue;
+ }
#ifdef INLINING_DEBUG
std::cout << "inline " << inlinedName << " into " << func->name << '\n';
#endif
@@ -446,6 +449,24 @@ struct Inlining : public Pass {
// return whether we did any work
return inlinedUses.size() > 0;
}
+
+ // Checks if the combined size of the code after inlining is under the
+ // absolute size limit. We have an absolute limit in order to avoid
+ // extremely-large sizes after inlining, as they may hit limits in VMs and/or
+ // slow down startup (measurements there indicate something like ~1 second to
+ // optimize a 100K function). See e.g.
+ // https://github.com/WebAssembly/binaryen/pull/3730#issuecomment-867939138
+ // https://github.com/emscripten-core/emscripten/issues/13899#issuecomment-825073344
+ bool isUnderSizeLimit(Name target, Name source) {
+ // Estimate the combined binary size from the number of instructions.
+ auto combinedSize = infos[target].size + infos[source].size;
+ auto estimatedBinarySize = Measurer::BytesPerExpr * combinedSize;
+ // The limit is arbitrary, but based on the links above. It is a very high
+ // value that should appear very rarely in practice (for example, it does
+ // not occur on the Emscripten benchmark suite of real-world codebases).
+ const Index MaxCombinedBinarySize = 400 * 1024;
+ return estimatedBinarySize < MaxCombinedBinarySize;
+ }
};
Pass* createInliningPass() { return new Inlining(); }