summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/Inlining.cpp22
-rw-r--r--src/passes/NoInline.cpp77
-rw-r--r--src/passes/pass.cpp7
-rw-r--r--src/passes/passes.h3
-rw-r--r--src/wasm.h6
6 files changed, 108 insertions, 8 deletions
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 53f52e23f..279c50983 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -70,6 +70,7 @@ set(passes_SOURCES
MultiMemoryLowering.cpp
NameList.cpp
NameTypes.cpp
+ NoInline.cpp
OnceReduction.cpp
OptimizeAddedConstants.cpp
OptimizeCasts.cpp
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 20172d88d..e1e2dd022 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -686,8 +686,10 @@ struct FunctionSplitter {
auto outlinedFunctionSize = info.size - Measurer::measure(iff);
// If outlined function will be worth normal inline, skip the intermediate
- // state and inline fully now.
- if (outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
+ // state and inline fully now. Note that if full inlining is disabled we
+ // will not do this, and instead inline partially.
+ if (!func->noFullInline &&
+ outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
return InliningMode::Full;
}
@@ -771,10 +773,12 @@ struct FunctionSplitter {
// Success, this matches the pattern.
// If the outlined function will be worth inlining normally, skip the
- // intermediate state and inline fully now.
+ // intermediate state and inline fully now. (As above, if full inlining is
+ // disabled, we only partially inline.)
if (numIfs == 1) {
auto outlinedFunctionSize = Measurer::measure(iff->ifTrue);
- if (outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
+ if (!func->noFullInline &&
+ outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
return InliningMode::Full;
}
}
@@ -1197,7 +1201,9 @@ struct Inlining : public Pass {
// See explanation in doInlining() for the parameter nameHint.
Index inlinedNameHint = 0;
+ // Decide for a given function whether to inline, and if so in what mode.
InliningMode getInliningMode(Name name) {
+ auto* func = module->getFunction(name);
auto& info = infos[name];
if (info.inliningMode != InliningMode::Unknown) {
@@ -1205,19 +1211,19 @@ struct Inlining : public Pass {
}
// Check if the function itself is worth inlining as it is.
- if (info.worthFullInlining(getPassOptions())) {
- info.inliningMode = InliningMode::Full;
- return info.inliningMode;
+ if (!func->noFullInline && info.worthFullInlining(getPassOptions())) {
+ return info.inliningMode = InliningMode::Full;
}
// Otherwise, check if we can at least inline part of it, if we are
// interested in such things.
- if (functionSplitter) {
+ if (!func->noPartialInline && functionSplitter) {
info.inliningMode = functionSplitter->getSplitDrivenInliningMode(
module->getFunction(name), info);
return info.inliningMode;
}
+ // Cannot be fully or partially inlined => uninlineable.
info.inliningMode = InliningMode::Uninlineable;
return info.inliningMode;
}
diff --git a/src/passes/NoInline.cpp b/src/passes/NoInline.cpp
new file mode 100644
index 000000000..59e4f7e2c
--- /dev/null
+++ b/src/passes/NoInline.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 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.
+ */
+
+//
+// Mark functions as no-inline based on name wildcards. For example:
+//
+// --no-inline=*leave-alone* --inlining
+//
+// That will mark all functions with names like "runtime-leave-alone-1234" as
+// no-inline, and as a result the inlining pass that happens afterwards will not
+// inline them.
+//
+// Note that this is itself a pass, which does work - it marks things in the
+// IR - and so the order of operations matters. If we did
+//
+// --inlining --no-inline=*leave-alone*
+//
+// then we'd first perform the inlining and then the marking, which would mean
+// the marking has no effect.
+//
+
+#include "pass.h"
+#include "support/string.h"
+#include "wasm.h"
+
+namespace wasm {
+
+namespace {
+
+enum NoInlineMode { Full = 0, Partial = 1, Both = 2 };
+
+struct NoInline : public Pass {
+ NoInlineMode mode;
+
+ NoInline(NoInlineMode mode) : mode(mode) {}
+
+ void run(Module* module) override {
+ std::string pattern = getPassOptions().getArgument(
+ name, "Usage usage: wasm-opt --" + name + "=WILDCARD");
+
+ for (auto& func : module->functions) {
+ if (!String::wildcardMatch(pattern, func->name.toString())) {
+ continue;
+ }
+
+ if (mode == Full || mode == Both) {
+ func->noFullInline = true;
+ }
+ if (mode == Partial || mode == Both) {
+ func->noPartialInline = true;
+ }
+ }
+ }
+};
+
+} // anonymous namespace
+
+Pass* createNoInlinePass() { return new NoInline(NoInlineMode::Both); }
+Pass* createNoFullInlinePass() { return new NoInline(NoInlineMode::Full); }
+Pass* createNoPartialInlinePass() {
+ return new NoInline(NoInlineMode::Partial);
+}
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 38b6c73a6..525248924 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -294,6 +294,13 @@ void PassRegistry::registerPasses() {
createMultiMemoryLoweringWithBoundsChecksPass);
registerPass("nm", "name list", createNameListPass);
registerPass("name-types", "(re)name all heap types", createNameTypesPass);
+ registerPass("no-inline", "mark functions as no-inline", createNoInlinePass);
+ registerPass("no-full-inline",
+ "mark functions as no-inline (for full inlining only)",
+ createNoFullInlinePass);
+ registerPass("no-partial-inline",
+ "mark functions as no-inline (for partial inlining only)",
+ createNoPartialInlinePass);
registerPass("once-reduction",
"reduces calls to code that only runs once",
createOnceReductionPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index b0b42d5de..e6ff7709e 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -92,6 +92,9 @@ Pass* createMultiMemoryLoweringPass();
Pass* createMultiMemoryLoweringWithBoundsChecksPass();
Pass* createNameListPass();
Pass* createNameTypesPass();
+Pass* createNoInlinePass();
+Pass* createNoFullInlinePass();
+Pass* createNoPartialInlinePass();
Pass* createOnceReductionPass();
Pass* createOptimizeAddedConstantsPass();
Pass* createOptimizeAddedConstantsPropagatePass();
diff --git a/src/wasm.h b/src/wasm.h
index a7a2e0b1e..24138c99d 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2072,6 +2072,12 @@ public:
delimiterLocations;
BinaryLocations::FunctionLocations funcLocation;
+ // Inlining metadata: whether to disallow full and/or partial inlining (for
+ // details on what those mean, see Inlining.cpp).
+ bool noFullInline = false;
+ bool noPartialInline = false;
+
+ // Methods
Signature getSig() { return type.getSignature(); }
Type getParams() { return getSig().params; }
Type getResults() { return getSig().results; }