summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md13
-rw-r--r--src/binaryen-c.cpp18
-rw-r--r--src/binaryen-c.h8
-rw-r--r--src/ir/module-utils.cpp3
-rw-r--r--src/js/binaryen.js-post.js4
-rw-r--r--src/pass.h10
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/Metrics.cpp6
-rw-r--r--src/passes/Monomorphize.cpp5
-rw-r--r--src/passes/Print.cpp46
-rw-r--r--src/passes/RoundTrip.cpp2
-rw-r--r--src/passes/StackIR.cpp505
-rw-r--r--src/passes/pass.cpp136
-rw-r--r--src/passes/passes.h3
-rw-r--r--src/tools/optimization-options.h11
-rw-r--r--src/tools/tool-options.h30
-rw-r--r--src/tools/wasm-as.cpp2
-rw-r--r--src/tools/wasm-ctor-eval.cpp2
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp2
-rw-r--r--src/tools/wasm-merge.cpp2
-rw-r--r--src/tools/wasm-metadce.cpp2
-rw-r--r--src/tools/wasm-opt.cpp16
-rw-r--r--src/tools/wasm-reduce.cpp4
-rw-r--r--src/tools/wasm-split/wasm-split.cpp2
-rw-r--r--src/wasm-binary.h8
-rw-r--r--src/wasm-io.h7
-rw-r--r--src/wasm-stack.h96
-rw-r--r--src/wasm.h20
-rw-r--r--src/wasm/CMakeLists.txt1
-rw-r--r--src/wasm/wasm-binary.cpp16
-rw-r--r--src/wasm/wasm-io.cpp2
-rw-r--r--src/wasm/wasm-stack-opts.cpp456
-rw-r--r--src/wasm/wasm-stack.cpp57
-rw-r--r--test/binaryen.js/hello-world.js.txt2
-rw-r--r--test/binaryen.js/low-memory-unused.js.txt4
-rw-r--r--test/binaryen.js/optimize-levels.js.txt4
-rw-r--r--test/binaryen.js/sieve.js.txt2
-rw-r--r--test/binaryen.js/stackir.js3
-rw-r--r--test/example/c-api-kitchen-sink.c7
-rw-r--r--test/example/c-api-kitchen-sink.txt10
-rw-r--r--test/example/relooper-fuzz.txt4
-rw-r--r--test/example/relooper-fuzz1.txt4
-rw-r--r--test/lit/help/wasm-as.test6
-rw-r--r--test/lit/help/wasm-ctor-eval.test6
-rw-r--r--test/lit/help/wasm-dis.test6
-rw-r--r--test/lit/help/wasm-emscripten-finalize.test6
-rw-r--r--test/lit/help/wasm-merge.test6
-rw-r--r--test/lit/help/wasm-metadce.test6
-rw-r--r--test/lit/help/wasm-opt.test13
-rw-r--r--test/lit/help/wasm-reduce.test6
-rw-r--r--test/lit/help/wasm-split.test6
-rw-r--r--test/lit/help/wasm2js.test13
-rw-r--r--test/lit/passes/O.wast10
-rw-r--r--test/lit/passes/O1_skip.wast2
-rw-r--r--test/lit/passes/O3_Oz.wast2
-rw-r--r--test/lit/passes/O3_inline-functions-with-loops_flexible-inline-max-function-size=30.wast10
-rw-r--r--test/lit/passes/O4_disable-bulk-memory.wast20
-rw-r--r--test/lit/passes/O_fast-math.wast14
-rw-r--r--test/lit/passes/Oz.wast18
-rw-r--r--test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast10
-rw-r--r--test/lit/passes/asyncify_mod-asyncify-never-unwind_O.wast10
-rw-r--r--test/lit/passes/flatten_dfo_O3_enable-threads.wast12
-rw-r--r--test/lit/passes/flatten_simplify-locals-nonesting_dfo_O3.wast10
-rw-r--r--test/lit/passes/global-effects-O.wast12
-rw-r--r--test/lit/passes/gto_and_cfp_in_O.wast6
-rw-r--r--test/lit/passes/type-ssa_and_merging.wast8
-rw-r--r--test/passes/O.bin.txt10
-rw-r--r--test/passes/O1_print-stack-ir.txt31
-rw-r--r--test/passes/O2_precompute-propagate_print-stack-ir.txt8
-rw-r--r--test/passes/O2_print-stack-ir.txt34
-rw-r--r--test/passes/O3_low-memory-unused_metrics.txt2
-rw-r--r--test/passes/O3_print-stack-ir.txt30
-rw-r--r--test/passes/Os_print-stack-ir_all-features_disable-gc.txt30
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.txt32
-rw-r--r--test/passes/converge_O3_metrics.bin.txt36
-rw-r--r--test/passes/fuzz-exec_O.txt6
-rw-r--r--test/passes/remove-unused-brs_generate-stack-ir_print-stack-ir.txt34
-rw-r--r--test/unit/test_stack_ir.py17
78 files changed, 1007 insertions, 1017 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a44aedcef..7d997e702 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,19 @@ full changeset diff at the end of each section.
Current Trunk
-------------
+ - StackIR is now handled entirely during binary writing. This is mostly not
+ noticeable, except that:
+ - Text output no longer notes `(; has Stack IR ;)` (as Stack IR only exists
+ during binary writing).
+ - `--generate-stack-ir`, `--optimize-stack-ir`, and `--print-stack-ir` are
+ now flags and not passes. That means the order of operations may seem
+ different, as they apply during binary writing (or, if no binary is written
+ but we were still asked to print StackIR, `wasm-opt` does it at the very
+ end).
+ - Whether to generate, optimize, and print StackIR is now noted as part of
+ the PassOptions. As a result `BinaryenModulePrintStackIR` and similar APIs
+ do not receive an `optimize` flag, as they read the PassOption
+ `optimizeStackIR` instead.
- The new, standards-compliant text parser is now the default. `wasm-opt` has a
`--deprecated-wat-parser` flag that will switch back to using the old text
parser, but that option will go away soon.
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index f27f6e2c8..1c9a5be0e 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -5590,8 +5590,8 @@ void BinaryenModulePrint(BinaryenModuleRef module) {
std::cout << *(Module*)module;
}
-void BinaryenModulePrintStackIR(BinaryenModuleRef module, bool optimize) {
- wasm::printStackIR(std::cout, (Module*)module, optimize);
+void BinaryenModulePrintStackIR(BinaryenModuleRef module) {
+ wasm::printStackIR(std::cout, (Module*)module, globalPassOptions);
}
void BinaryenModulePrintAsmjs(BinaryenModuleRef module) {
@@ -5737,7 +5737,7 @@ static BinaryenBufferSizes writeModule(BinaryenModuleRef module,
char* sourceMap,
size_t sourceMapSize) {
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer((Module*)module, buffer);
+ WasmBinaryWriter writer((Module*)module, buffer, globalPassOptions);
writer.setNamesSection(globalPassOptions.debugInfo);
std::ostringstream os;
if (sourceMapUrl) {
@@ -5778,12 +5778,11 @@ size_t BinaryenModuleWriteText(BinaryenModuleRef module,
size_t BinaryenModuleWriteStackIR(BinaryenModuleRef module,
char* output,
- size_t outputSize,
- bool optimize) {
+ size_t outputSize) {
// use a stringstream as an std::ostream. Extract the std::string
// representation, and then store in the output.
std::stringstream ss;
- wasm::printStackIR(ss, (Module*)module, optimize);
+ wasm::printStackIR(ss, (Module*)module, globalPassOptions);
const auto temp = ss.str();
const auto ctemp = temp.c_str();
@@ -5808,7 +5807,7 @@ BinaryenModuleAllocateAndWriteResult
BinaryenModuleAllocateAndWrite(BinaryenModuleRef module,
const char* sourceMapUrl) {
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer((Module*)module, buffer);
+ WasmBinaryWriter writer((Module*)module, buffer, globalPassOptions);
writer.setNamesSection(globalPassOptions.debugInfo);
std::ostringstream os;
if (sourceMapUrl) {
@@ -5842,13 +5841,12 @@ char* BinaryenModuleAllocateAndWriteText(BinaryenModuleRef module) {
return output;
}
-char* BinaryenModuleAllocateAndWriteStackIR(BinaryenModuleRef module,
- bool optimize) {
+char* BinaryenModuleAllocateAndWriteStackIR(BinaryenModuleRef module) {
std::ostringstream os;
bool colors = Colors::isEnabled();
Colors::setEnabled(false); // do not use colors for writing
- wasm::printStackIR(os, (Module*)module, optimize);
+ wasm::printStackIR(os, (Module*)module, globalPassOptions);
Colors::setEnabled(colors); // restore colors state
auto str = os.str();
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index bc63e963a..595086d6a 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -2984,8 +2984,7 @@ BINARYEN_API BinaryenModuleRef BinaryenModuleParse(const char* text);
BINARYEN_API void BinaryenModulePrint(BinaryenModuleRef module);
// Print a module to stdout in stack IR text format. Useful for debugging.
-BINARYEN_API void BinaryenModulePrintStackIR(BinaryenModuleRef module,
- bool optimize);
+BINARYEN_API void BinaryenModulePrintStackIR(BinaryenModuleRef module);
// Print a module to stdout in asm.js syntax.
BINARYEN_API void BinaryenModulePrintAsmjs(BinaryenModuleRef module);
@@ -3126,8 +3125,7 @@ BINARYEN_API size_t BinaryenModuleWriteText(BinaryenModuleRef module,
// outputSize
BINARYEN_API size_t BinaryenModuleWriteStackIR(BinaryenModuleRef module,
char* output,
- size_t outputSize,
- bool optimize);
+ size_t outputSize);
typedef struct BinaryenBufferSizes {
size_t outputBytes;
@@ -3173,7 +3171,7 @@ BINARYEN_API char* BinaryenModuleAllocateAndWriteText(BinaryenModuleRef module);
// char* with malloc(), and expects the user to free() them manually
// once not needed anymore.
BINARYEN_API char*
-BinaryenModuleAllocateAndWriteStackIR(BinaryenModuleRef module, bool optimize);
+BinaryenModuleAllocateAndWriteStackIR(BinaryenModuleRef module);
// Deserialize a module from binary form, assuming the MVP feature set.
BINARYEN_API BinaryenModuleRef BinaryenModuleRead(char* input,
diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp
index 8d9e88d18..e169f0ff1 100644
--- a/src/ir/module-utils.cpp
+++ b/src/ir/module-utils.cpp
@@ -69,9 +69,6 @@ Function* copyFunction(Function* func,
ret->base = func->base;
ret->noFullInline = func->noFullInline;
ret->noPartialInline = func->noPartialInline;
-
- // TODO: copy Stack IR
- assert(!func->stackIR);
return out.addFunction(std::move(ret));
}
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 1461420bb..dfde2d7ef 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -2688,8 +2688,8 @@ function wrapModule(module, self = {}) {
if (textPtr) _free(textPtr);
return text;
};
- self['emitStackIR'] = function(optimize) {
- let textPtr = Module['_BinaryenModuleAllocateAndWriteStackIR'](module, optimize);
+ self['emitStackIR'] = function() {
+ let textPtr = Module['_BinaryenModuleAllocateAndWriteStackIR'](module);
let text = UTF8ToString(textPtr);
if (textPtr) _free(textPtr);
return text;
diff --git a/src/pass.h b/src/pass.h
index 2c2fa0619..ab060309b 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -219,6 +219,16 @@ struct PassOptions {
bool closedWorld = false;
// Whether to try to preserve debug info through, which are special calls.
bool debugInfo = false;
+ // Whether to generate StackIR during binary writing. This is on by default
+ // in -O2 and above.
+ bool generateStackIR = false;
+ // Whether to optimize StackIR during binary writing. How we optimize depends
+ // on other optimization flags like optimizeLevel. This is on by default in
+ // -O2 and above.
+ bool optimizeStackIR = false;
+ // Whether to print StackIR during binary writing, and if so to what stream.
+ // This is mainly useful for debugging.
+ std::optional<std::ostream*> printStackIR;
// Whether we are targeting JS. In that case we want to avoid emitting things
// in the optimizer that do not translate well to JS, or that could cause us
// to need extra lowering work or even a loop (where we optimize to something
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 1e5540ab0..72fa02b62 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -87,7 +87,6 @@ set(passes_SOURCES
PrintFunctionMap.cpp
RoundTrip.cpp
SetGlobals.cpp
- StackIR.cpp
SignaturePruning.cpp
SignatureRefining.cpp
SignExtLowering.cpp
diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp
index d2e072a6f..1778eb9bd 100644
--- a/src/passes/Metrics.cpp
+++ b/src/passes/Metrics.cpp
@@ -94,7 +94,7 @@ struct Metrics
printCounts("global");
// compute binary info, so we know function sizes
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer(module, buffer);
+ WasmBinaryWriter writer(module, buffer, getPassOptions());
writer.write();
// print for each function
Index binaryIndex = 0;
@@ -108,14 +108,14 @@ struct Metrics
});
// print for each export how much code size is due to it, i.e.,
// how much the module could shrink without it.
- auto sizeAfterGlobalCleanup = [](Module* module) {
+ auto sizeAfterGlobalCleanup = [&](Module* module) {
PassRunner runner(module,
PassOptions::getWithDefaultOptimizationOptions());
runner.setIsNested(true);
runner.addDefaultGlobalOptimizationPostPasses(); // remove stuff
runner.run();
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer(module, buffer);
+ WasmBinaryWriter writer(module, buffer, getPassOptions());
writer.write();
return buffer.size();
};
diff --git a/src/passes/Monomorphize.cpp b/src/passes/Monomorphize.cpp
index f8ee4a6e6..012f24750 100644
--- a/src/passes/Monomorphize.cpp
+++ b/src/passes/Monomorphize.cpp
@@ -151,11 +151,6 @@ struct Monomorphize : public Pass {
// monomorphizing.
// Create a new function with refined parameters as a copy of the original.
- // (Note we must clear stack IR on the original: atm we do not have the
- // ability to copy stack IR, so we'd hit an internal error. But as we will
- // be optimizing the function anyhow, we'd be throwing away stack IR later
- // so this isn't a problem.)
- func->stackIR.reset();
auto refinedTarget = Names::getValidFunctionName(*module, target);
auto* refinedFunc = ModuleUtils::copyFunction(func, *module, refinedTarget);
TypeUpdating::updateParamTypes(refinedFunc, refinedTypes, *module);
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 1ecf382d9..99c86cec4 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -109,11 +109,11 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
const char* maybeSpace;
const char* maybeNewLine;
- bool full = false; // whether to not elide nodes in output when possible
- // (like implicit blocks) and to emit types
- bool stackIR = false; // whether to print stack IR if it is present
- // (if false, and Stack IR is there, we just
- // note it exists)
+ // Whether to not elide nodes in output when possible (like implicit blocks)
+ // and to emit types.
+ bool full = false;
+ // If present, it contains StackIR that we will print.
+ std::optional<ModuleStackIR> moduleStackIR;
Module* currModule = nullptr;
Function* currFunction = nullptr;
@@ -268,7 +268,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
void setFull(bool full_) { full = full_; }
- void setStackIR(bool stackIR_) { stackIR = stackIR_; }
+ void generateStackIR(const PassOptions& options) {
+ moduleStackIR.emplace(*currModule, options);
+ }
void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; }
@@ -2978,9 +2980,6 @@ void PrintSExpression::visitDefinedFunction(Function* curr) {
o << " (type ";
printHeapType(curr->type) << ')';
}
- if (!stackIR && curr->stackIR && !minify) {
- o << " (; has Stack IR ;)";
- }
if (curr->getParams().size() > 0) {
Index i = 0;
for (const auto& param : curr->getParams()) {
@@ -3007,7 +3006,13 @@ void PrintSExpression::visitDefinedFunction(Function* curr) {
o << maybeNewLine;
}
// Print the body.
- if (!stackIR || !curr->stackIR) {
+ StackIR* stackIR = nullptr;
+ if (moduleStackIR) {
+ stackIR = moduleStackIR->getStackIROrNull(curr);
+ }
+ if (stackIR) {
+ printStackIR(stackIR, *this);
+ } else {
// It is ok to emit a block here, as a function can directly contain a
// list, even if our ast avoids that for simplicity. We can just do that
// optimization here..
@@ -3021,9 +3026,6 @@ void PrintSExpression::visitDefinedFunction(Function* curr) {
printFullLine(curr->body);
}
assert(controlFlowDepth == 0);
- } else {
- // Print the stack IR.
- printStackIR(curr->stackIR.get(), *this);
}
if (currFunction->epilogLocation.size()) {
// Print last debug location: mix of decIndent and printDebugLocation
@@ -3430,14 +3432,12 @@ public:
void run(Module* module) override {
PrintSExpression print(o);
print.setDebugInfo(getPassOptions().debugInfo);
- print.setStackIR(true);
print.currModule = module;
+ print.generateStackIR(getPassOptions());
print.visitModule(module);
}
};
-Pass* createPrintStackIRPass() { return new PrintStackIR(); }
-
static std::ostream& printExpression(Expression* expression,
std::ostream& o,
bool minify,
@@ -3602,12 +3602,9 @@ static std::ostream& printStackIR(StackIR* ir, PrintSExpression& printer) {
return o;
}
-std::ostream& printStackIR(std::ostream& o, Module* module, bool optimize) {
- wasm::PassRunner runner(module);
- runner.add("generate-stack-ir");
- if (optimize) {
- runner.add("optimize-stack-ir");
- }
+std::ostream&
+printStackIR(std::ostream& o, Module* module, const PassOptions& options) {
+ wasm::PassRunner runner(module, options);
runner.add(std::make_unique<PrintStackIR>(&o));
runner.run();
return o;
@@ -3659,9 +3656,4 @@ std::ostream& operator<<(std::ostream& o, wasm::StackInst& inst) {
return wasm::printStackInst(&inst, o);
}
-std::ostream& operator<<(std::ostream& o, wasm::StackIR& ir) {
- wasm::PrintSExpression printer(o);
- return wasm::printStackIR(&ir, printer);
-}
-
} // namespace std
diff --git a/src/passes/RoundTrip.cpp b/src/passes/RoundTrip.cpp
index b930b195c..129b89ac8 100644
--- a/src/passes/RoundTrip.cpp
+++ b/src/passes/RoundTrip.cpp
@@ -41,7 +41,7 @@ struct RoundTrip : public Pass {
// to tell the builder which features to build with.
auto features = module->features;
// Write, clear, and read the module
- WasmBinaryWriter(module, buffer).write();
+ WasmBinaryWriter(module, buffer, getPassOptions()).write();
ModuleUtils::clearModule(*module);
auto input = buffer.getAsChars();
WasmBinaryReader parser(*module, features, input);
diff --git a/src/passes/StackIR.cpp b/src/passes/StackIR.cpp
deleted file mode 100644
index 24b4fcbe8..000000000
--- a/src/passes/StackIR.cpp
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-//
-// Operations on Stack IR.
-//
-
-#include "ir/iteration.h"
-#include "ir/local-graph.h"
-#include "pass.h"
-#include "wasm-stack.h"
-#include "wasm.h"
-
-namespace wasm {
-
-// Generate Stack IR from Binaryen IR
-
-struct GenerateStackIR : public WalkerPass<PostWalker<GenerateStackIR>> {
- bool isFunctionParallel() override { return true; }
-
- std::unique_ptr<Pass> create() override {
- return std::make_unique<GenerateStackIR>();
- }
-
- bool modifiesBinaryenIR() override { return false; }
-
- void doWalkFunction(Function* func) {
- StackIRGenerator stackIRGen(*getModule(), func);
- stackIRGen.write();
- func->stackIR = std::make_unique<StackIR>();
- func->stackIR->swap(stackIRGen.getStackIR());
- }
-};
-
-Pass* createGenerateStackIRPass() { return new GenerateStackIR(); }
-
-// Optimize
-
-class StackIROptimizer {
- Function* func;
- PassOptions& passOptions;
- StackIR& insts;
- FeatureSet features;
-
-public:
- StackIROptimizer(Function* func,
- PassOptions& passOptions,
- FeatureSet features)
- : func(func), passOptions(passOptions), insts(*func->stackIR.get()),
- features(features) {
- assert(func->stackIR);
- }
-
- void run() {
- dce();
- // FIXME: local2Stack is currently rather slow (due to localGraph),
- // so for now run it only when really optimizing
- if (passOptions.optimizeLevel >= 3 || passOptions.shrinkLevel >= 1) {
- local2Stack();
- }
- removeUnneededBlocks();
- dce();
- vacuum();
- }
-
-private:
- // Passes.
-
- // Remove unreachable code.
- void dce() {
- bool inUnreachableCode = false;
- for (Index i = 0; i < insts.size(); i++) {
- auto* inst = insts[i];
- if (!inst) {
- continue;
- }
- if (inUnreachableCode) {
- // Does the unreachable code end here?
- if (isControlFlowBarrier(inst)) {
- inUnreachableCode = false;
- } else {
- // We can remove this.
- removeAt(i);
- }
- } else if (inst->type == Type::unreachable) {
- inUnreachableCode = true;
- }
- }
- }
-
- // Remove obviously-unneeded code.
- void vacuum() {
- // In the wasm binary format a nop is never needed. (In Binaryen IR, in
- // comparison, it is necessary e.g. in a function body or an if arm.)
- //
- // It is especially important to remove nops because we add nops when we
- // read wasm into Binaryen IR. That is, this avoids a potential increase in
- // code size.
- for (Index i = 0; i < insts.size(); i++) {
- auto*& inst = insts[i];
- if (inst && inst->origin->is<Nop>()) {
- inst = nullptr;
- }
- }
- }
-
- // If ordered properly, we can avoid a local.set/local.get pair,
- // and use the value directly from the stack, for example
- // [..produce a value on the stack..]
- // local.set $x
- // [..much code..]
- // local.get $x
- // call $foo ;; use the value, foo(value)
- // As long as the code in between does not modify $x, and has
- // no control flow branching out, we can remove both the set
- // and the get.
- void local2Stack() {
- // We use the localGraph to tell us if a get-set pair is indeed
- // a set that is read by that get, and only that get. Note that we run
- // this on the Binaryen IR, so we are assuming that no previous opt
- // has changed the interaction of local operations.
- // TODO: we can do this a lot faster, as we just care about linear
- // control flow.
- LocalGraph localGraph(func);
- localGraph.computeSetInfluences();
- // We maintain a stack of relevant values. This contains:
- // * a null for each actual value that the value stack would have
- // * an index of each LocalSet that *could* be on the value
- // stack at that location.
- const Index null = -1;
- std::vector<Index> values;
- // We also maintain a stack of values vectors for control flow,
- // saving the stack as we enter and restoring it when we exit.
- std::vector<std::vector<Index>> savedValues;
-#ifdef STACK_OPT_DEBUG
- std::cout << "func: " << func->name << '\n' << insts << '\n';
-#endif
- for (Index instIndex = 0; instIndex < insts.size(); instIndex++) {
- auto* inst = insts[instIndex];
- if (!inst) {
- continue;
- }
- // First, consume values from the stack as required.
- auto consumed = getNumConsumedValues(inst);
-#ifdef STACK_OPT_DEBUG
- std::cout << " " << instIndex << " : " << *inst << ", " << values.size()
- << " on stack, will consume " << consumed << "\n ";
- for (auto s : values)
- std::cout << s << ' ';
- std::cout << '\n';
-#endif
- // TODO: currently we run dce before this, but if we didn't, we'd need
- // to handle unreachable code here - it's ok to pop multiple values
- // there even if the stack is at size 0.
- while (consumed > 0) {
- assert(values.size() > 0);
- // Whenever we hit a possible stack value, kill it - it would
- // be consumed here, so we can never optimize to it.
- while (values.back() != null) {
- values.pop_back();
- assert(values.size() > 0);
- }
- // Finally, consume the actual value that is consumed here.
- values.pop_back();
- consumed--;
- }
- // After consuming, we can see what to do with this. First, handle
- // control flow.
- if (isControlFlowBegin(inst)) {
- // Save the stack for when we end this control flow.
- savedValues.push_back(values); // TODO: optimize copies
- values.clear();
- } else if (isControlFlowEnd(inst)) {
- assert(!savedValues.empty());
- values = savedValues.back();
- savedValues.pop_back();
- } else if (isControlFlow(inst)) {
- // Otherwise, in the middle of control flow, just clear it
- values.clear();
- }
- // This is something we should handle, look into it.
- if (inst->type.isConcrete()) {
- bool optimized = false;
- // Do not optimize multivalue locals, since those will be better
- // optimized when they are visited in the binary writer and this
- // optimization would intefere with that one.
- if (auto* get = inst->origin->dynCast<LocalGet>();
- get && inst->type.isSingle()) {
- // Use another local to clarify what instIndex means in this scope.
- auto getIndex = instIndex;
-
- // This is a potential optimization opportunity! See if we
- // can reach the set.
- if (values.size() > 0) {
- Index j = values.size() - 1;
- while (1) {
- // If there's an actual value in the way, we've failed.
- auto setIndex = values[j];
- if (setIndex == null) {
- break;
- }
- auto* set = insts[setIndex]->origin->cast<LocalSet>();
- if (set->index == get->index) {
- // This might be a proper set-get pair, where the set is
- // used by this get and nothing else, check that.
- auto& sets = localGraph.getSetses[get];
- if (sets.size() == 1 && *sets.begin() == set) {
- auto& setInfluences = localGraph.setInfluences[set];
- // If this has the proper value of 1, also do the potentially-
- // expensive check of whether we can remove this pair at all.
- if (setInfluences.size() == 1 &&
- canRemoveSetGetPair(setIndex, getIndex)) {
- assert(*setInfluences.begin() == get);
- // Do it! The set and the get can go away, the proper
- // value is on the stack.
-#ifdef STACK_OPT_DEBUG
- std::cout << " stackify the get\n";
-#endif
- insts[setIndex] = nullptr;
- insts[getIndex] = nullptr;
- // Continuing on from here, replace this on the stack
- // with a null, representing a regular value. We
- // keep possible values above us active - they may
- // be optimized later, as they would be pushed after
- // us, and used before us, so there is no conflict.
- values[j] = null;
- optimized = true;
- break;
- }
- }
- }
- // We failed here. Can we look some more?
- if (j == 0) {
- break;
- }
- j--;
- }
- }
- }
- if (!optimized) {
- // This is an actual regular value on the value stack.
- values.push_back(null);
- }
- } else if (inst->origin->is<LocalSet>() && inst->type == Type::none) {
- // This set is potentially optimizable later, add to stack.
- values.push_back(instIndex);
- }
- }
- }
-
- // There may be unnecessary blocks we can remove: blocks
- // without branches to them are always ok to remove.
- // TODO: a branch to a block in an if body can become
- // a branch to that if body
- void removeUnneededBlocks() {
- for (auto*& inst : insts) {
- if (!inst) {
- continue;
- }
- if (auto* block = inst->origin->dynCast<Block>()) {
- if (!BranchUtils::BranchSeeker::has(block, block->name)) {
- // TODO optimize, maybe run remove-unused-names
- inst = nullptr;
- }
- }
- }
- }
-
- // Utilities.
-
- // A control flow "barrier" - a point where stack machine
- // unreachability ends.
- bool isControlFlowBarrier(StackInst* inst) {
- switch (inst->op) {
- case StackInst::BlockEnd:
- case StackInst::IfElse:
- case StackInst::IfEnd:
- case StackInst::LoopEnd:
- case StackInst::Catch:
- case StackInst::CatchAll:
- case StackInst::Delegate:
- case StackInst::TryEnd:
- case StackInst::TryTableEnd: {
- return true;
- }
- default: { return false; }
- }
- }
-
- // A control flow beginning.
- bool isControlFlowBegin(StackInst* inst) {
- switch (inst->op) {
- case StackInst::BlockBegin:
- case StackInst::IfBegin:
- case StackInst::LoopBegin:
- case StackInst::TryBegin:
- case StackInst::TryTableBegin: {
- return true;
- }
- default: { return false; }
- }
- }
-
- // A control flow ending.
- bool isControlFlowEnd(StackInst* inst) {
- switch (inst->op) {
- case StackInst::BlockEnd:
- case StackInst::IfEnd:
- case StackInst::LoopEnd:
- case StackInst::TryEnd:
- case StackInst::Delegate:
- case StackInst::TryTableEnd: {
- return true;
- }
- default: { return false; }
- }
- }
-
- bool isControlFlow(StackInst* inst) { return inst->op != StackInst::Basic; }
-
- // Remove the instruction at index i. If the instruction
- // is control flow, and so has been expanded to multiple
- // instructions, remove them as well.
- void removeAt(Index i) {
- auto* inst = insts[i];
- insts[i] = nullptr;
- if (inst->op == StackInst::Basic) {
- return; // that was it
- }
- auto* origin = inst->origin;
- while (1) {
- i++;
- assert(i < insts.size());
- inst = insts[i];
- insts[i] = nullptr;
- if (inst && inst->origin == origin && isControlFlowEnd(inst)) {
- return; // that's it, we removed it all
- }
- }
- }
-
- Index getNumConsumedValues(StackInst* inst) {
- if (isControlFlow(inst)) {
- // If consumes 1; that's it.
- if (inst->op == StackInst::IfBegin) {
- return 1;
- }
- return 0;
- }
- // Otherwise, for basic instructions, just count the expression children.
- return ChildIterator(inst->origin).children.size();
- }
-
- // Given a pair of a local.set and local.get, see if we can remove them
- // without breaking validation. Specifically, we must keep sets of non-
- // nullable locals that dominate a get until the end of the block, such as
- // here:
- //
- // local.set 0 ;; Can we remove
- // local.get 0 ;; this pair?
- // if
- // local.set 0
- // else
- // local.set 0
- // end
- // local.get 0 ;; This get poses a problem.
- //
- // Logically the 2nd&3rd sets ensure a value is applied to the local before we
- // read it, but the validation rules only track each set until the end of its
- // scope, so the 1st set (before the if, in the pair) is necessary.
- //
- // The logic below is related to LocalStructuralDominance, but sharing code
- // with it is difficult as this uses StackIR and not BinaryenIR, and it checks
- // a particular set/get pair.
- //
- // We are given the indexes of the set and get instructions in |insts|.
- bool canRemoveSetGetPair(Index setIndex, Index getIndex) {
- // The set must be before the get.
- assert(setIndex < getIndex);
-
- auto* set = insts[setIndex]->origin->cast<LocalSet>();
- auto localType = func->getLocalType(set->index);
- // Note we do not need to handle tuples here, as the parent ignores them
- // anyhow (hence we can check non-nullability instead of non-
- // defaultability).
- assert(localType.isSingle());
- if (func->isParam(set->index) || !localType.isNonNullable()) {
- // This local cannot pose a problem for validation (params are always
- // initialized, and it is ok if nullable locals are uninitialized).
- return true;
- }
-
- // Track the depth (in block/if/loop/etc. scopes) relative to our starting
- // point. Anything less deep than that is not interesting, as we can only
- // help things at our depth or deeper to validate.
- Index currDepth = 0;
-
- // Look for a different get than the one in getIndex (since that one is
- // being removed) which would stop validating without the set. While doing
- // so, note other sets that ensure validation even if our set is removed. We
- // track those in this stack of booleans, one for each scope, which is true
- // if another sets covers us and ours is not needed.
- //
- // We begin in the current scope and with no other set covering us.
- std::vector<bool> coverStack = {false};
-
- // Track the total number of covers as well, for quick checking below.
- Index covers = 0;
-
- // TODO: We could look before us as well, but then we might end up scanning
- // much of the function every time.
- for (Index i = setIndex + 1; i < insts.size(); i++) {
- auto* inst = insts[i];
- if (!inst) {
- continue;
- }
- if (isControlFlowBegin(inst)) {
- // A new scope begins.
- currDepth++;
- coverStack.push_back(false);
- } else if (isControlFlowEnd(inst)) {
- if (currDepth == 0) {
- // Less deep than the start, so we found no problem.
- return true;
- }
- currDepth--;
-
- if (coverStack.back()) {
- // A cover existed in the scope which ended.
- covers--;
- }
- coverStack.pop_back();
- } else if (isControlFlowBarrier(inst)) {
- // A barrier, like the else in an if-else, not only ends a scope but
- // opens a new one.
- if (currDepth == 0) {
- // Another scope with the same depth begins, but ours ended, so stop.
- return true;
- }
-
- if (coverStack.back()) {
- // A cover existed in the scope which ended.
- covers--;
- }
- coverStack.back() = false;
- } else if (auto* otherSet = inst->origin->dynCast<LocalSet>()) {
- // We are covered in this scope henceforth.
- if (otherSet->index == set->index) {
- if (!coverStack.back()) {
- covers++;
- if (currDepth == 0) {
- // We have a cover at depth 0, so everything from here on out
- // will be covered.
- return true;
- }
- coverStack.back() = true;
- }
- }
- } else if (auto* otherGet = inst->origin->dynCast<LocalGet>()) {
- if (otherGet->index == set->index && i != getIndex && !covers) {
- // We found a get that might be a problem: it uses the same index, but
- // is not the get we were told about, and no other set covers us.
- return false;
- }
- }
- }
-
- // No problem.
- return true;
- }
-};
-
-struct OptimizeStackIR : public WalkerPass<PostWalker<OptimizeStackIR>> {
- bool isFunctionParallel() override { return true; }
-
- std::unique_ptr<Pass> create() override {
- return std::make_unique<OptimizeStackIR>();
- }
-
- bool modifiesBinaryenIR() override { return false; }
-
- void doWalkFunction(Function* func) {
- if (!func->stackIR) {
- return;
- }
- StackIROptimizer(func, getPassOptions(), getModule()->features).run();
- }
-};
-
-Pass* createOptimizeStackIRPass() { return new OptimizeStackIR(); }
-
-} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 4a4a9c558..c5a2bcc55 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -179,8 +179,6 @@ void PassRegistry::registerPasses() {
"generate global effect info (helps later passes)",
createGenerateGlobalEffectsPass);
registerPass(
- "generate-stack-ir", "generate Stack IR", createGenerateStackIRPass);
- registerPass(
"global-refining", "refine the types of globals", createGlobalRefiningPass);
registerPass(
"gsi", "globally optimize struct values", createGlobalStructInferencePass);
@@ -321,8 +319,6 @@ void PassRegistry::registerPasses() {
registerPass("optimize-instructions",
"optimizes instruction combinations",
createOptimizeInstructionsPass);
- registerPass(
- "optimize-stack-ir", "optimize Stack IR", createOptimizeStackIRPass);
// Outlining currently relies on LLVM's SuffixTree, which we can't rely upon
// when building Binaryen for Emscripten.
#ifndef SKIP_OUTLINING
@@ -369,9 +365,6 @@ void PassRegistry::registerPasses() {
registerPass(
"symbolmap", "(alias for print-function-map)", createPrintFunctionMapPass);
- registerPass("print-stack-ir",
- "print out Stack IR (useful for internal debugging)",
- createPrintStackIRPass);
registerPass("propagate-globals-globally",
"propagate global values to other globals (useful for tests)",
createPropagateGlobalsGloballyPass);
@@ -736,15 +729,9 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() {
}
// may allow more inlining/dae/etc., need --converge for that
addIfNoDWARFIssues("directize");
- // perform Stack IR optimizations here, at the very end of the
- // optimization pipeline
- if (options.optimizeLevel >= 2 || options.shrinkLevel >= 1) {
- addIfNoDWARFIssues("generate-stack-ir");
- addIfNoDWARFIssues("optimize-stack-ir");
- }
}
-static void dumpWasm(Name name, Module* wasm) {
+static void dumpWasm(Name name, Module* wasm, const PassOptions& options) {
static int counter = 0;
std::string numstr = std::to_string(counter++);
while (numstr.size() < 3) {
@@ -757,7 +744,7 @@ static void dumpWasm(Name name, Module* wasm) {
#endif
fullName += numstr + "-" + name.toString();
Colors::setEnabled(false);
- ModuleWriter writer;
+ ModuleWriter writer(options);
writer.setDebugInfo(true);
writer.writeBinary(*wasm, fullName + ".wasm");
}
@@ -780,7 +767,7 @@ void PassRunner::run() {
padding = std::max(padding, pass->name.size());
}
if (passDebug >= 3 && !isNested) {
- dumpWasm("before", wasm);
+ dumpWasm("before", wasm, options);
}
for (auto& pass : passes) {
// ignoring the time, save a printout of the module before, in case this
@@ -824,7 +811,7 @@ void PassRunner::run() {
}
}
if (passDebug >= 3) {
- dumpWasm(pass->name, wasm);
+ dumpWasm(pass->name, wasm, options);
}
}
std::cerr << "[PassRunner] " << what << " took " << totalTime.count()
@@ -908,100 +895,6 @@ void PassRunner::doAdd(std::unique_ptr<Pass> pass) {
void PassRunner::clear() { passes.clear(); }
-// Checks that the state is valid before and after a
-// pass runs on a function. We run these extra checks when
-// pass-debug mode is enabled.
-struct AfterEffectFunctionChecker {
- Function* func;
- Name name;
-
- // Check Stack IR state: if the main IR changes, there should be no
- // stack IR, as the stack IR would be wrong.
- bool beganWithStackIR;
- size_t originalFunctionHash;
-
- // In the creator we can scan the state of the module and function before the
- // pass runs.
- AfterEffectFunctionChecker(Function* func) : func(func), name(func->name) {
- beganWithStackIR = func->stackIR != nullptr;
- if (beganWithStackIR) {
- originalFunctionHash = FunctionHasher::hashFunction(func);
- }
- }
-
- // This is called after the pass is run, at which time we can check things.
- void check() {
- assert(func->name == name); // no global module changes should have occurred
- if (beganWithStackIR && func->stackIR) {
- auto after = FunctionHasher::hashFunction(func);
- if (after != originalFunctionHash) {
- Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before "
- "and after the pass ran, and the pass modified the main IR, "
- "which invalidates Stack IR - pass should have been marked "
- "'modifiesBinaryenIR'";
- }
- }
- }
-};
-
-// Runs checks on the entire module, in a non-function-parallel pass.
-// In particular, in such a pass functions may be removed or renamed, track
-// that.
-struct AfterEffectModuleChecker {
- Module* module;
-
- std::vector<AfterEffectFunctionChecker> checkers;
-
- bool beganWithAnyStackIR;
-
- AfterEffectModuleChecker(Module* module) : module(module) {
- for (auto& func : module->functions) {
- checkers.emplace_back(func.get());
- }
- beganWithAnyStackIR = hasAnyStackIR();
- }
-
- void check() {
- if (beganWithAnyStackIR && hasAnyStackIR()) {
- // If anything changed to the functions, that's not good.
- if (checkers.size() != module->functions.size()) {
- error();
- }
- for (Index i = 0; i < checkers.size(); i++) {
- // Did a pointer change? (a deallocated function could cause that)
- if (module->functions[i].get() != checkers[i].func ||
- module->functions[i]->body != checkers[i].func->body) {
- error();
- }
- // Did a name change?
- if (module->functions[i]->name != checkers[i].name) {
- error();
- }
- }
- // Global function state appears to not have been changed: the same
- // functions are there. Look into their contents.
- for (auto& checker : checkers) {
- checker.check();
- }
- }
- }
-
- void error() {
- Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before and "
- "after the pass ran, and the pass modified global function "
- "state - pass should have been marked 'modifiesBinaryenIR'";
- }
-
- bool hasAnyStackIR() {
- for (auto& func : module->functions) {
- if (func->stackIR) {
- return true;
- }
- }
- return false;
- }
-};
-
void PassRunner::runPass(Pass* pass) {
assert(!pass->isFunctionParallel());
@@ -1009,20 +902,12 @@ void PassRunner::runPass(Pass* pass) {
return;
}
- std::unique_ptr<AfterEffectModuleChecker> checker;
- if (getPassDebug()) {
- checker = std::unique_ptr<AfterEffectModuleChecker>(
- new AfterEffectModuleChecker(wasm));
- }
// Passes can only be run once and we deliberately do not clear the pass
// runner after running the pass, so there must not already be a runner here.
assert(!pass->getPassRunner());
pass->setPassRunner(this);
pass->run(wasm);
handleAfterEffects(pass);
- if (getPassDebug()) {
- checker->check();
- }
}
void PassRunner::runPassOnFunction(Pass* pass, Function* func) {
@@ -1051,21 +936,12 @@ void PassRunner::runPassOnFunction(Pass* pass, Function* func) {
bodyBefore << *func->body << '\n';
}
- std::unique_ptr<AfterEffectFunctionChecker> checker;
- if (passDebug) {
- checker = std::make_unique<AfterEffectFunctionChecker>(func);
- }
-
// Function-parallel passes get a new instance per function
auto instance = pass->create();
instance->setPassRunner(this);
instance->runOnFunction(wasm, func);
handleAfterEffects(pass, func);
- if (passDebug) {
- checker->check();
- }
-
if (extraFunctionValidation) {
if (!WasmValidator().validate(func, *wasm, WasmValidator::Minimal)) {
Fatal() << "Last nested function-parallel pass (" << pass->name
@@ -1095,10 +971,6 @@ void PassRunner::handleAfterEffects(Pass* pass, Function* func) {
return;
}
- // If Binaryen IR is modified, Stack IR must be cleared - it would
- // be out of sync in a potentially dangerous way.
- func->stackIR.reset(nullptr);
-
if (pass->requiresNonNullableLocalFixups()) {
TypeUpdating::handleNonDefaultableLocals(func, *wasm);
}
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 8695539b3..ac3e1ef29 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -54,7 +54,6 @@ Pass* createFunctionMetricsPass();
Pass* createGenerateDynCallsPass();
Pass* createGenerateI64DynCallsPass();
Pass* createGenerateGlobalEffectsPass();
-Pass* createGenerateStackIRPass();
Pass* createGlobalRefiningPass();
Pass* createGlobalStructInferencePass();
Pass* createGlobalTypeOptimizationPass();
@@ -103,7 +102,6 @@ Pass* createOptimizeAddedConstantsPropagatePass();
Pass* createOptimizeInstructionsPass();
Pass* createOptimizeCastsPass();
Pass* createOptimizeForJSPass();
-Pass* createOptimizeStackIRPass();
// Outlining currently relies on LLVM's SuffixTree, which we can't rely upon
// when building Binaryen for Emscripten.
#ifdef __EMSCRIPTEN__
@@ -123,7 +121,6 @@ Pass* createPrinterPass();
Pass* createPrintCallGraphPass();
Pass* createPrintFeaturesPass();
Pass* createPrintFunctionMapPass();
-Pass* createPrintStackIRPass();
Pass* createPropagateGlobalsGloballyPass();
Pass* createRemoveNonJSOpsPass();
Pass* createRemoveImportsPass();
diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h
index 0a47d9f70..8772edd29 100644
--- a/src/tools/optimization-options.h
+++ b/src/tools/optimization-options.h
@@ -26,6 +26,17 @@
namespace wasm {
struct OptimizationOptions : public ToolOptions {
+ void parse(int argc, const char* argv[]) {
+ ToolOptions::parse(argc, argv);
+
+ // After parsing the arguments, update defaults based on the optimize/shrink
+ // levels.
+ if (passOptions.optimizeLevel >= 2 || passOptions.shrinkLevel >= 1) {
+ passOptions.generateStackIR = true;
+ passOptions.optimizeStackIR = true;
+ }
+ }
+
static constexpr const char* DEFAULT_OPT_PASSES = "O";
static constexpr const int OS_OPTIMIZE_LEVEL = 2;
static constexpr const int OS_SHRINK_LEVEL = 1;
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index 6d68ff3c1..599b3b22c 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -151,7 +151,35 @@ struct ToolOptions : public Options {
Options::Arguments::Zero,
[this](Options*, const std::string&) {
passOptions.closedWorld = true;
- });
+ })
+ .add("--generate-stack-ir",
+ "",
+ "generate StackIR during writing",
+ ToolOptionsCategory,
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ passOptions.generateStackIR = true;
+ })
+ .add("--optimize-stack-ir",
+ "",
+ "optimize StackIR during writing",
+ ToolOptionsCategory,
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ // Also generate StackIR, to have something to optimize.
+ passOptions.generateStackIR = true;
+ passOptions.optimizeStackIR = true;
+ })
+ .add("--print-stack-ir",
+ "",
+ "print StackIR during writing",
+ ToolOptionsCategory,
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ // Also generate StackIR, to have something to print.
+ passOptions.generateStackIR = true;
+ passOptions.printStackIR = &std::cout;
+ });
}
ToolOptions& addFeature(FeatureSet::Feature feature,
diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp
index 311605326..a767e6908 100644
--- a/src/tools/wasm-as.cpp
+++ b/src/tools/wasm-as.cpp
@@ -129,7 +129,7 @@ int main(int argc, const char* argv[]) {
if (options.debug) {
std::cerr << "writing..." << std::endl;
}
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(true);
writer.setDebugInfo(debugInfo);
if (sourceMapFilename.size()) {
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 4018be0e7..d3f798084 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -1491,7 +1491,7 @@ int main(int argc, const char* argv[]) {
if (options.debug) {
std::cout << "writing..." << std::endl;
}
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
writer.setDebugInfo(debugInfo);
writer.write(wasm, options.extra["output"]);
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index a19d27328..6b4e994ac 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -279,7 +279,7 @@ int main(int argc, const char* argv[]) {
if (writeOutput) {
Output output(outfile, emitBinary ? Flags::Binary : Flags::Text);
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setDebugInfo(debugInfo);
// writer.setSymbolMap(symbolMap);
writer.setBinary(emitBinary);
diff --git a/src/tools/wasm-merge.cpp b/src/tools/wasm-merge.cpp
index 8e8e2d80d..449f0cfdb 100644
--- a/src/tools/wasm-merge.cpp
+++ b/src/tools/wasm-merge.cpp
@@ -752,7 +752,7 @@ Input source maps can be specified by adding an -ism option right after the modu
// Output.
if (options.extra.count("output") > 0) {
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
writer.setDebugInfo(debugInfo);
if (outputSourceMapFilename.size()) {
diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp
index 1b429a723..301470206 100644
--- a/src/tools/wasm-metadce.cpp
+++ b/src/tools/wasm-metadce.cpp
@@ -600,7 +600,7 @@ int main(int argc, const char* argv[]) {
graph.apply();
if (options.extra.count("output") > 0) {
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
writer.setDebugInfo(debugInfo);
if (outputSourceMapFilename.size()) {
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index 32f9f1aad..cb6d0bcec 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -35,6 +35,7 @@
#include "wasm-interpreter.h"
#include "wasm-io.h"
#include "wasm-s-parser.h"
+#include "wasm-stack.h"
#include "wasm-validator.h"
#include "wasm2c-wrapper.h"
@@ -347,7 +348,7 @@ int main(int argc, const char* argv[]) {
if (extraFuzzCommand.size() > 0 && options.extra.count("output") > 0) {
BYN_TRACE("writing binary before opts, for extra fuzz command...\n");
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
writer.setDebugInfo(options.passOptions.debugInfo);
writer.write(wasm, options.extra["output"]);
@@ -379,7 +380,7 @@ int main(int argc, const char* argv[]) {
// size no longer decreasing.
auto getSize = [&]() {
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer(&wasm, buffer);
+ WasmBinaryWriter writer(&wasm, buffer, options.passOptions);
writer.write();
return buffer.size();
};
@@ -402,11 +403,6 @@ int main(int argc, const char* argv[]) {
runner.add("translate-to-new-eh");
// Perform Stack IR optimizations here, at the very end of the
// optimization pipeline.
- if (options.passOptions.optimizeLevel >= 2 ||
- options.passOptions.shrinkLevel >= 1) {
- runner.addIfNoDWARFIssues("generate-stack-ir");
- runner.addIfNoDWARFIssues("optimize-stack-ir");
- }
runner.run();
if (options.passOptions.validate) {
bool valid = WasmValidator().validate(wasm, options.passOptions);
@@ -420,6 +416,10 @@ int main(int argc, const char* argv[]) {
results.check(wasm);
}
+ if (options.passOptions.printStackIR) {
+ printStackIR(std::cout, &wasm, options.passOptions);
+ }
+
if (options.extra.count("output") == 0) {
if (!options.quiet) {
std::cerr << "warning: no output file specified, not emitting output\n";
@@ -428,7 +428,7 @@ int main(int argc, const char* argv[]) {
}
BYN_TRACE("writing...\n");
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
writer.setDebugInfo(options.passOptions.debugInfo);
if (outputSourceMapFilename.size()) {
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 094338bea..ac5b7722b 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -401,7 +401,7 @@ struct Reducer
bool writeAndTestReduction(ProgramResult& out) {
// write the module out
- ModuleWriter writer;
+ ModuleWriter writer(toolOptions.passOptions);
writer.setBinary(binary);
writer.setDebugInfo(debugInfo);
writer.write(*getModule(), test);
@@ -1368,7 +1368,7 @@ int main(int argc, const char* argv[]) {
if (resultOnInvalid == expected) {
// Try it on a valid input.
Module emptyModule;
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(true);
writer.write(emptyModule, test);
ProgramResult resultOnValid(command);
diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp
index bea3ddce7..d7dc19d67 100644
--- a/src/tools/wasm-split/wasm-split.cpp
+++ b/src/tools/wasm-split/wasm-split.cpp
@@ -91,7 +91,7 @@ void adjustTableSize(Module& wasm, int initialSize) {
void writeModule(Module& wasm,
std::string filename,
const WasmSplitOptions& options) {
- ModuleWriter writer;
+ ModuleWriter writer(options.passOptions);
writer.setBinary(options.emitBinary);
writer.setDebugInfo(options.passOptions.debugInfo);
if (options.emitModuleNames) {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 6c1230b7b..b5aa83960 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1270,8 +1270,10 @@ class WasmBinaryWriter {
};
public:
- WasmBinaryWriter(Module* input, BufferWithRandomAccess& o)
- : wasm(input), o(o), indexes(*input) {
+ WasmBinaryWriter(Module* input,
+ BufferWithRandomAccess& o,
+ const PassOptions& options)
+ : wasm(input), o(o), options(options), indexes(*input) {
prepare();
}
@@ -1384,6 +1386,8 @@ public:
private:
Module* wasm;
BufferWithRandomAccess& o;
+ const PassOptions& options;
+
BinaryIndexes indexes;
ModuleUtils::IndexedHeapTypes indexedTypes;
std::unordered_map<Signature, uint32_t> signatureIndexes;
diff --git a/src/wasm-io.h b/src/wasm-io.h
index ae66c3932..e5cad9a23 100644
--- a/src/wasm-io.h
+++ b/src/wasm-io.h
@@ -22,6 +22,7 @@
#define wasm_wasm_io_h
#include "parsing.h"
+#include "pass.h"
#include "support/file.h"
#include "wasm.h"
@@ -85,6 +86,8 @@ private:
};
class ModuleWriter : public ModuleIOBase {
+ const PassOptions& options;
+
bool binary = true;
// TODO: Remove `emitModuleName`. See the comment in wasm-binary.h
@@ -97,7 +100,9 @@ class ModuleWriter : public ModuleIOBase {
public:
// Writing defaults to not storing the names section. Storing it is a user-
// observable fact that must be opted into.
- ModuleWriter() { setDebugInfo(false); }
+ ModuleWriter(const PassOptions& options) : options(options) {
+ setDebugInfo(false);
+ }
void setBinary(bool binary_) { binary = binary_; }
void setSymbolMap(std::string symbolMap_) { symbolMap = symbolMap_; }
diff --git a/src/wasm-stack.h b/src/wasm-stack.h
index 5858d6f88..ae453f9de 100644
--- a/src/wasm-stack.h
+++ b/src/wasm-stack.h
@@ -18,6 +18,7 @@
#define wasm_stack_h
#include "ir/branch-utils.h"
+#include "ir/module-utils.h"
#include "ir/properties.h"
#include "pass.h"
#include "support/insert_ordered.h"
@@ -85,6 +86,8 @@ public:
Type type;
};
+using StackIR = std::vector<StackInst*>;
+
class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
public:
BinaryInstWriter(WasmBinaryWriter& parent,
@@ -468,44 +471,24 @@ private:
bool sourceMap;
};
-// Binaryen IR to stack IR converter
-// Queues the expressions linearly in Stack IR (SIR)
-class StackIRGenerator : public BinaryenIRWriter<StackIRGenerator> {
-public:
- StackIRGenerator(Module& module, Function* func)
- : BinaryenIRWriter<StackIRGenerator>(func), module(module) {}
-
- void emit(Expression* curr);
- void emitScopeEnd(Expression* curr);
- void emitHeader() {}
- void emitIfElse(If* curr) {
- stackIR.push_back(makeStackInst(StackInst::IfElse, curr));
- }
- void emitCatch(Try* curr, Index i) {
- stackIR.push_back(makeStackInst(StackInst::Catch, curr));
- }
- void emitCatchAll(Try* curr) {
- stackIR.push_back(makeStackInst(StackInst::CatchAll, curr));
- }
- void emitDelegate(Try* curr) {
- stackIR.push_back(makeStackInst(StackInst::Delegate, curr));
- }
- void emitFunctionEnd() {}
- void emitUnreachable() {
- stackIR.push_back(makeStackInst(Builder(module).makeUnreachable()));
- }
- void emitDebugLocation(Expression* curr) {}
-
- StackIR& getStackIR() { return stackIR; }
+// Binaryen IR to stack IR converter for an entire module. Generates all the
+// StackIR in parallel, and then allows querying for the StackIR of individual
+// functions.
+class ModuleStackIR {
+ ModuleUtils::ParallelFunctionAnalysis<StackIR> analysis;
-private:
- StackInst* makeStackInst(StackInst::Op op, Expression* origin);
- StackInst* makeStackInst(Expression* origin) {
- return makeStackInst(StackInst::Basic, origin);
+public:
+ ModuleStackIR(Module& wasm, const PassOptions& options);
+
+ // Get StackIR for a function, if it exists. (This allows some functions to
+ // have it and others not, if we add such capability in the future.)
+ StackIR* getStackIROrNull(Function* func) {
+ auto iter = analysis.map.find(func);
+ if (iter == analysis.map.end()) {
+ return nullptr;
+ }
+ return &iter->second;
}
-
- Module& module;
- StackIR stackIR; // filled in write()
};
// Stack IR to binary writer
@@ -514,10 +497,11 @@ public:
StackIRToBinaryWriter(WasmBinaryWriter& parent,
BufferWithRandomAccess& o,
Function* func,
+ StackIR& stackIR,
bool sourceMap = false,
bool DWARF = false)
: parent(parent), writer(parent, o, func, sourceMap, DWARF), func(func),
- sourceMap(sourceMap) {}
+ stackIR(stackIR), sourceMap(sourceMap) {}
void write();
@@ -527,11 +511,47 @@ private:
WasmBinaryWriter& parent;
BinaryInstWriter writer;
Function* func;
+ StackIR& stackIR;
bool sourceMap;
};
-std::ostream& printStackIR(std::ostream& o, Module* module, bool optimize);
+// Stack IR optimizer
+class StackIROptimizer {
+ Function* func;
+ StackIR& insts;
+ const PassOptions& passOptions;
+ FeatureSet features;
+
+public:
+ StackIROptimizer(Function* func,
+ StackIR& insts,
+ const PassOptions& passOptions,
+ FeatureSet features);
+
+ void run();
+
+private:
+ void dce();
+ void vacuum();
+ void local2Stack();
+ void removeUnneededBlocks();
+ bool isControlFlowBarrier(StackInst* inst);
+ bool isControlFlowBegin(StackInst* inst);
+ bool isControlFlowEnd(StackInst* inst);
+ bool isControlFlow(StackInst* inst);
+ void removeAt(Index i);
+ Index getNumConsumedValues(StackInst* inst);
+ bool canRemoveSetGetPair(Index setIndex, Index getIndex);
+};
+
+// Generate and emit StackIR.
+std::ostream&
+printStackIR(std::ostream& o, Module* module, const PassOptions& options);
} // namespace wasm
+namespace std {
+std::ostream& operator<<(std::ostream& o, wasm::StackInst& inst);
+} // namespace std
+
#endif // wasm_stack_h
diff --git a/src/wasm.h b/src/wasm.h
index d852950e8..ebc7b908d 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2136,14 +2136,6 @@ struct BinaryLocations {
std::unordered_map<Function*, FunctionLocations> functions;
};
-// Forward declarations of Stack IR, as functions can contain it, see
-// the stackIR property.
-// Stack IR is a secondary IR to the main IR defined in this file (Binaryen
-// IR). See wasm-stack.h.
-class StackInst;
-
-using StackIR = std::vector<StackInst*>;
-
class Function : public Importable {
public:
HeapType type = HeapType(Signature()); // parameters and return value
@@ -2153,16 +2145,6 @@ public:
// The body of the function
Expression* body = nullptr;
- // If present, this stack IR was generated from the main Binaryen IR body,
- // and possibly optimized. If it is present when writing to wasm binary,
- // it will be emitted instead of the main Binaryen IR.
- //
- // Note that no special care is taken to synchronize the two IRs - if you
- // emit stack IR and then optimize the main IR, you need to recompute the
- // stack IR. The Pass system will throw away Stack IR if a pass is run
- // that declares it may modify Binaryen IR.
- std::unique_ptr<StackIR> stackIR;
-
// local names. these are optional.
std::unordered_map<Index, Name> localNames;
std::unordered_map<Name, Index> localIndices;
@@ -2511,8 +2493,6 @@ std::ostream& operator<<(std::ostream& o, wasm::Function& func);
std::ostream& operator<<(std::ostream& o, wasm::Expression& expression);
std::ostream& operator<<(std::ostream& o, wasm::ModuleExpression pair);
std::ostream& operator<<(std::ostream& o, wasm::ShallowExpression expression);
-std::ostream& operator<<(std::ostream& o, wasm::StackInst& inst);
-std::ostream& operator<<(std::ostream& o, wasm::StackIR& ir);
} // namespace std
diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt
index d5b4f6747..e01be3b6a 100644
--- a/src/wasm/CMakeLists.txt
+++ b/src/wasm/CMakeLists.txt
@@ -11,6 +11,7 @@ set(wasm_SOURCES
wasm-ir-builder.cpp
wasm-s-parser.cpp
wasm-stack.cpp
+ wasm-stack-opts.cpp
wasm-type.cpp
wasm-validator.cpp
${wasm_HEADERS}
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index d4fda5915..7e90dfde4 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -392,6 +392,12 @@ void WasmBinaryWriter::writeFunctions() {
if (importInfo->getNumDefinedFunctions() == 0) {
return;
}
+
+ std::optional<ModuleStackIR> moduleStackIR;
+ if (options.generateStackIR) {
+ moduleStackIR.emplace(*wasm, options);
+ }
+
BYN_TRACE("== writeFunctions\n");
auto sectionStart = startSection(BinaryConsts::Section::Code);
o << U32LEB(importInfo->getNumDefinedFunctions());
@@ -405,10 +411,14 @@ void WasmBinaryWriter::writeFunctions() {
size_t sizePos = writeU32LEBPlaceholder();
size_t start = o.size();
BYN_TRACE("writing" << func->name << std::endl);
- // Emit Stack IR if present, and if we can
- if (func->stackIR) {
+ // Emit Stack IR if present.
+ StackIR* stackIR = nullptr;
+ if (moduleStackIR) {
+ stackIR = moduleStackIR->getStackIROrNull(func);
+ }
+ if (stackIR) {
BYN_TRACE("write Stack IR\n");
- StackIRToBinaryWriter writer(*this, o, func, sourceMap, DWARF);
+ StackIRToBinaryWriter writer(*this, o, func, *stackIR, sourceMap, DWARF);
writer.write();
if (debugInfo) {
funcMappedLocals[func->name] = std::move(writer.getMappedLocals());
diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp
index a24122bd8..10b84bb4d 100644
--- a/src/wasm/wasm-io.cpp
+++ b/src/wasm/wasm-io.cpp
@@ -149,7 +149,7 @@ void ModuleWriter::writeText(Module& wasm, std::string filename) {
void ModuleWriter::writeBinary(Module& wasm, Output& output) {
BufferWithRandomAccess buffer;
- WasmBinaryWriter writer(&wasm, buffer);
+ WasmBinaryWriter writer(&wasm, buffer, options);
// if debug info is used, then we want to emit the names section
writer.setNamesSection(debugInfo);
if (emitModuleName) {
diff --git a/src/wasm/wasm-stack-opts.cpp b/src/wasm/wasm-stack-opts.cpp
new file mode 100644
index 000000000..eac423fbd
--- /dev/null
+++ b/src/wasm/wasm-stack-opts.cpp
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//
+// Operations on Stack IR.
+//
+
+#include "ir/iteration.h"
+#include "ir/local-graph.h"
+#include "pass.h"
+#include "wasm-stack.h"
+#include "wasm.h"
+
+namespace wasm {
+
+StackIROptimizer::StackIROptimizer(Function* func,
+ StackIR& insts,
+ const PassOptions& passOptions,
+ FeatureSet features)
+ : func(func), insts(insts), passOptions(passOptions), features(features) {}
+
+void StackIROptimizer::run() {
+ dce();
+ // FIXME: local2Stack is currently rather slow (due to localGraph),
+ // so for now run it only when really optimizing
+ if (passOptions.optimizeLevel >= 3 || passOptions.shrinkLevel >= 1) {
+ local2Stack();
+ }
+ removeUnneededBlocks();
+ dce();
+ vacuum();
+}
+
+// Remove unreachable code.
+void StackIROptimizer::dce() {
+ bool inUnreachableCode = false;
+ for (Index i = 0; i < insts.size(); i++) {
+ auto* inst = insts[i];
+ if (!inst) {
+ continue;
+ }
+ if (inUnreachableCode) {
+ // Does the unreachable code end here?
+ if (isControlFlowBarrier(inst)) {
+ inUnreachableCode = false;
+ } else {
+ // We can remove this.
+ removeAt(i);
+ }
+ } else if (inst->type == Type::unreachable) {
+ inUnreachableCode = true;
+ }
+ }
+}
+
+// Remove obviously-unneeded code.
+void StackIROptimizer::vacuum() {
+ // In the wasm binary format a nop is never needed. (In Binaryen IR, in
+ // comparison, it is necessary e.g. in a function body or an if arm.)
+ //
+ // It is especially important to remove nops because we add nops when we
+ // read wasm into Binaryen IR. That is, this avoids a potential increase in
+ // code size.
+ for (Index i = 0; i < insts.size(); i++) {
+ auto*& inst = insts[i];
+ if (inst && inst->origin->is<Nop>()) {
+ inst = nullptr;
+ }
+ }
+}
+
+// If ordered properly, we can avoid a local.set/local.get pair,
+// and use the value directly from the stack, for example
+// [..produce a value on the stack..]
+// local.set $x
+// [..much code..]
+// local.get $x
+// call $foo ;; use the value, foo(value)
+// As long as the code in between does not modify $x, and has
+// no control flow branching out, we can remove both the set
+// and the get.
+void StackIROptimizer::local2Stack() {
+ // We use the localGraph to tell us if a get-set pair is indeed
+ // a set that is read by that get, and only that get. Note that we run
+ // this on the Binaryen IR, so we are assuming that no previous opt
+ // has changed the interaction of local operations.
+ // TODO: we can do this a lot faster, as we just care about linear
+ // control flow.
+ LocalGraph localGraph(func);
+ localGraph.computeSetInfluences();
+ // We maintain a stack of relevant values. This contains:
+ // * a null for each actual value that the value stack would have
+ // * an index of each LocalSet that *could* be on the value
+ // stack at that location.
+ const Index null = -1;
+ std::vector<Index> values;
+ // We also maintain a stack of values vectors for control flow,
+ // saving the stack as we enter and restoring it when we exit.
+ std::vector<std::vector<Index>> savedValues;
+#ifdef STACK_OPT_DEBUG
+ std::cout << "func: " << func->name << '\n' << insts << '\n';
+#endif
+ for (Index instIndex = 0; instIndex < insts.size(); instIndex++) {
+ auto* inst = insts[instIndex];
+ if (!inst) {
+ continue;
+ }
+ // First, consume values from the stack as required.
+ auto consumed = getNumConsumedValues(inst);
+#ifdef STACK_OPT_DEBUG
+ std::cout << " " << instIndex << " : " << *inst << ", " << values.size()
+ << " on stack, will consume " << consumed << "\n ";
+ for (auto s : values)
+ std::cout << s << ' ';
+ std::cout << '\n';
+#endif
+ // TODO: currently we run dce before this, but if we didn't, we'd need
+ // to handle unreachable code here - it's ok to pop multiple values
+ // there even if the stack is at size 0.
+ while (consumed > 0) {
+ assert(values.size() > 0);
+ // Whenever we hit a possible stack value, kill it - it would
+ // be consumed here, so we can never optimize to it.
+ while (values.back() != null) {
+ values.pop_back();
+ assert(values.size() > 0);
+ }
+ // Finally, consume the actual value that is consumed here.
+ values.pop_back();
+ consumed--;
+ }
+ // After consuming, we can see what to do with this. First, handle
+ // control flow.
+ if (isControlFlowBegin(inst)) {
+ // Save the stack for when we end this control flow.
+ savedValues.push_back(values); // TODO: optimize copies
+ values.clear();
+ } else if (isControlFlowEnd(inst)) {
+ assert(!savedValues.empty());
+ values = savedValues.back();
+ savedValues.pop_back();
+ } else if (isControlFlow(inst)) {
+ // Otherwise, in the middle of control flow, just clear it
+ values.clear();
+ }
+ // This is something we should handle, look into it.
+ if (inst->type.isConcrete()) {
+ bool optimized = false;
+ // Do not optimize multivalue locals, since those will be better
+ // optimized when they are visited in the binary writer and this
+ // optimization would intefere with that one.
+ if (auto* get = inst->origin->dynCast<LocalGet>();
+ get && inst->type.isSingle()) {
+ // Use another local to clarify what instIndex means in this scope.
+ auto getIndex = instIndex;
+
+ // This is a potential optimization opportunity! See if we
+ // can reach the set.
+ if (values.size() > 0) {
+ Index j = values.size() - 1;
+ while (1) {
+ // If there's an actual value in the way, we've failed.
+ auto setIndex = values[j];
+ if (setIndex == null) {
+ break;
+ }
+ auto* set = insts[setIndex]->origin->cast<LocalSet>();
+ if (set->index == get->index) {
+ // This might be a proper set-get pair, where the set is
+ // used by this get and nothing else, check that.
+ auto& sets = localGraph.getSetses[get];
+ if (sets.size() == 1 && *sets.begin() == set) {
+ auto& setInfluences = localGraph.setInfluences[set];
+ // If this has the proper value of 1, also do the potentially-
+ // expensive check of whether we can remove this pair at all.
+ if (setInfluences.size() == 1 &&
+ canRemoveSetGetPair(setIndex, getIndex)) {
+ assert(*setInfluences.begin() == get);
+ // Do it! The set and the get can go away, the proper
+ // value is on the stack.
+#ifdef STACK_OPT_DEBUG
+ std::cout << " stackify the get\n";
+#endif
+ insts[setIndex] = nullptr;
+ insts[getIndex] = nullptr;
+ // Continuing on from here, replace this on the stack
+ // with a null, representing a regular value. We
+ // keep possible values above us active - they may
+ // be optimized later, as they would be pushed after
+ // us, and used before us, so there is no conflict.
+ values[j] = null;
+ optimized = true;
+ break;
+ }
+ }
+ }
+ // We failed here. Can we look some more?
+ if (j == 0) {
+ break;
+ }
+ j--;
+ }
+ }
+ }
+ if (!optimized) {
+ // This is an actual regular value on the value stack.
+ values.push_back(null);
+ }
+ } else if (inst->origin->is<LocalSet>() && inst->type == Type::none) {
+ // This set is potentially optimizable later, add to stack.
+ values.push_back(instIndex);
+ }
+ }
+}
+
+// There may be unnecessary blocks we can remove: blocks
+// without branches to them are always ok to remove.
+// TODO: a branch to a block in an if body can become
+// a branch to that if body
+void StackIROptimizer::removeUnneededBlocks() {
+ for (auto*& inst : insts) {
+ if (!inst) {
+ continue;
+ }
+ if (auto* block = inst->origin->dynCast<Block>()) {
+ if (!BranchUtils::BranchSeeker::has(block, block->name)) {
+ // TODO optimize, maybe run remove-unused-names
+ inst = nullptr;
+ }
+ }
+ }
+}
+
+// A control flow "barrier" - a point where stack machine
+// unreachability ends.
+bool StackIROptimizer::isControlFlowBarrier(StackInst* inst) {
+ switch (inst->op) {
+ case StackInst::BlockEnd:
+ case StackInst::IfElse:
+ case StackInst::IfEnd:
+ case StackInst::LoopEnd:
+ case StackInst::Catch:
+ case StackInst::CatchAll:
+ case StackInst::Delegate:
+ case StackInst::TryEnd:
+ case StackInst::TryTableEnd: {
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+// A control flow beginning.
+bool StackIROptimizer::isControlFlowBegin(StackInst* inst) {
+ switch (inst->op) {
+ case StackInst::BlockBegin:
+ case StackInst::IfBegin:
+ case StackInst::LoopBegin:
+ case StackInst::TryBegin:
+ case StackInst::TryTableBegin: {
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+// A control flow ending.
+bool StackIROptimizer::isControlFlowEnd(StackInst* inst) {
+ switch (inst->op) {
+ case StackInst::BlockEnd:
+ case StackInst::IfEnd:
+ case StackInst::LoopEnd:
+ case StackInst::TryEnd:
+ case StackInst::Delegate:
+ case StackInst::TryTableEnd: {
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+bool StackIROptimizer::isControlFlow(StackInst* inst) {
+ return inst->op != StackInst::Basic;
+}
+
+// Remove the instruction at index i. If the instruction
+// is control flow, and so has been expanded to multiple
+// instructions, remove them as well.
+void StackIROptimizer::removeAt(Index i) {
+ auto* inst = insts[i];
+ insts[i] = nullptr;
+ if (inst->op == StackInst::Basic) {
+ return; // that was it
+ }
+ auto* origin = inst->origin;
+ while (1) {
+ i++;
+ assert(i < insts.size());
+ inst = insts[i];
+ insts[i] = nullptr;
+ if (inst && inst->origin == origin && isControlFlowEnd(inst)) {
+ return; // that's it, we removed it all
+ }
+ }
+}
+
+Index StackIROptimizer::getNumConsumedValues(StackInst* inst) {
+ if (isControlFlow(inst)) {
+ // If consumes 1; that's it.
+ if (inst->op == StackInst::IfBegin) {
+ return 1;
+ }
+ return 0;
+ }
+ // Otherwise, for basic instructions, just count the expression children.
+ return ChildIterator(inst->origin).children.size();
+}
+
+// Given a pair of a local.set and local.get, see if we can remove them
+// without breaking validation. Specifically, we must keep sets of non-
+// nullable locals that dominate a get until the end of the block, such as
+// here:
+//
+// local.set 0 ;; Can we remove
+// local.get 0 ;; this pair?
+// if
+// local.set 0
+// else
+// local.set 0
+// end
+// local.get 0 ;; This get poses a problem.
+//
+// Logically the 2nd&3rd sets ensure a value is applied to the local before we
+// read it, but the validation rules only track each set until the end of its
+// scope, so the 1st set (before the if, in the pair) is necessary.
+//
+// The logic below is related to LocalStructuralDominance, but sharing code
+// with it is difficult as this uses StackIR and not BinaryenIR, and it checks
+// a particular set/get pair.
+//
+// We are given the indexes of the set and get instructions in |insts|.
+bool StackIROptimizer::canRemoveSetGetPair(Index setIndex, Index getIndex) {
+ // The set must be before the get.
+ assert(setIndex < getIndex);
+
+ auto* set = insts[setIndex]->origin->cast<LocalSet>();
+ auto localType = func->getLocalType(set->index);
+ // Note we do not need to handle tuples here, as the parent ignores them
+ // anyhow (hence we can check non-nullability instead of non-
+ // defaultability).
+ assert(localType.isSingle());
+ if (func->isParam(set->index) || !localType.isNonNullable()) {
+ // This local cannot pose a problem for validation (params are always
+ // initialized, and it is ok if nullable locals are uninitialized).
+ return true;
+ }
+
+ // Track the depth (in block/if/loop/etc. scopes) relative to our starting
+ // point. Anything less deep than that is not interesting, as we can only
+ // help things at our depth or deeper to validate.
+ Index currDepth = 0;
+
+ // Look for a different get than the one in getIndex (since that one is
+ // being removed) which would stop validating without the set. While doing
+ // so, note other sets that ensure validation even if our set is removed. We
+ // track those in this stack of booleans, one for each scope, which is true
+ // if another sets covers us and ours is not needed.
+ //
+ // We begin in the current scope and with no other set covering us.
+ std::vector<bool> coverStack = {false};
+
+ // Track the total number of covers as well, for quick checking below.
+ Index covers = 0;
+
+ // TODO: We could look before us as well, but then we might end up scanning
+ // much of the function every time.
+ for (Index i = setIndex + 1; i < insts.size(); i++) {
+ auto* inst = insts[i];
+ if (!inst) {
+ continue;
+ }
+ if (isControlFlowBegin(inst)) {
+ // A new scope begins.
+ currDepth++;
+ coverStack.push_back(false);
+ } else if (isControlFlowEnd(inst)) {
+ if (currDepth == 0) {
+ // Less deep than the start, so we found no problem.
+ return true;
+ }
+ currDepth--;
+
+ if (coverStack.back()) {
+ // A cover existed in the scope which ended.
+ covers--;
+ }
+ coverStack.pop_back();
+ } else if (isControlFlowBarrier(inst)) {
+ // A barrier, like the else in an if-else, not only ends a scope but
+ // opens a new one.
+ if (currDepth == 0) {
+ // Another scope with the same depth begins, but ours ended, so stop.
+ return true;
+ }
+
+ if (coverStack.back()) {
+ // A cover existed in the scope which ended.
+ covers--;
+ }
+ coverStack.back() = false;
+ } else if (auto* otherSet = inst->origin->dynCast<LocalSet>()) {
+ // We are covered in this scope henceforth.
+ if (otherSet->index == set->index) {
+ if (!coverStack.back()) {
+ covers++;
+ if (currDepth == 0) {
+ // We have a cover at depth 0, so everything from here on out
+ // will be covered.
+ return true;
+ }
+ coverStack.back() = true;
+ }
+ }
+ } else if (auto* otherGet = inst->origin->dynCast<LocalGet>()) {
+ if (otherGet->index == set->index && i != getIndex && !covers) {
+ // We found a get that might be a problem: it uses the same index, but
+ // is not the get we were told about, and no other set covers us.
+ return false;
+ }
+ }
+ }
+
+ // No problem.
+ return true;
+}
+
+} // namespace wasm
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 1ed60abed..3cdd1f718 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2737,6 +2737,45 @@ int32_t BinaryInstWriter::getBreakIndex(Name name) { // -1 if not found
WASM_UNREACHABLE("break index not found");
}
+// Queues the expressions linearly in Stack IR (SIR)
+class StackIRGenerator : public BinaryenIRWriter<StackIRGenerator> {
+public:
+ StackIRGenerator(Module& module, Function* func)
+ : BinaryenIRWriter<StackIRGenerator>(func), module(module) {}
+
+ void emit(Expression* curr);
+ void emitScopeEnd(Expression* curr);
+ void emitHeader() {}
+ void emitIfElse(If* curr) {
+ stackIR.push_back(makeStackInst(StackInst::IfElse, curr));
+ }
+ void emitCatch(Try* curr, Index i) {
+ stackIR.push_back(makeStackInst(StackInst::Catch, curr));
+ }
+ void emitCatchAll(Try* curr) {
+ stackIR.push_back(makeStackInst(StackInst::CatchAll, curr));
+ }
+ void emitDelegate(Try* curr) {
+ stackIR.push_back(makeStackInst(StackInst::Delegate, curr));
+ }
+ void emitFunctionEnd() {}
+ void emitUnreachable() {
+ stackIR.push_back(makeStackInst(Builder(module).makeUnreachable()));
+ }
+ void emitDebugLocation(Expression* curr) {}
+
+ StackIR& getStackIR() { return stackIR; }
+
+private:
+ StackInst* makeStackInst(StackInst::Op op, Expression* origin);
+ StackInst* makeStackInst(Expression* origin) {
+ return makeStackInst(StackInst::Basic, origin);
+ }
+
+ Module& module;
+ StackIR stackIR; // filled in write()
+};
+
void StackIRGenerator::emit(Expression* curr) {
StackInst* stackInst = nullptr;
if (curr->is<Block>()) {
@@ -2798,6 +2837,22 @@ StackInst* StackIRGenerator::makeStackInst(StackInst::Op op,
return ret;
}
+ModuleStackIR::ModuleStackIR(Module& wasm, const PassOptions& options)
+ : analysis(wasm, [&](Function* func, StackIR& stackIR) {
+ if (func->imported()) {
+ return;
+ }
+
+ StackIRGenerator stackIRGen(wasm, func);
+ stackIRGen.write();
+ stackIR = std::move(stackIRGen.getStackIR());
+
+ if (options.optimizeStackIR) {
+ StackIROptimizer optimizer(func, stackIR, options, wasm.features);
+ optimizer.run();
+ }
+ }) {}
+
void StackIRToBinaryWriter::write() {
if (func->prologLocation.size()) {
parent.writeDebugLocation(*func->prologLocation.begin());
@@ -2805,7 +2860,7 @@ void StackIRToBinaryWriter::write() {
writer.mapLocalsAndEmitHeader();
// Stack to track indices of catches within a try
SmallVector<Index, 4> catchIndexStack;
- for (auto* inst : *func->stackIR) {
+ for (auto* inst : stackIR) {
if (!inst) {
continue; // a nullptr is just something we can skip
}
diff --git a/test/binaryen.js/hello-world.js.txt b/test/binaryen.js/hello-world.js.txt
index 3f9296adf..70c94764a 100644
--- a/test/binaryen.js/hello-world.js.txt
+++ b/test/binaryen.js/hello-world.js.txt
@@ -16,7 +16,7 @@ optimized:
(module
(type $0 (func (param i32 i32) (result i32)))
(export "adder" (func $adder))
- (func $adder (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ (func $adder (param $0 i32) (param $1 i32) (result i32)
(i32.add
(local.get $0)
(local.get $1)
diff --git a/test/binaryen.js/low-memory-unused.js.txt b/test/binaryen.js/low-memory-unused.js.txt
index 99c682259..d9b10a3f6 100644
--- a/test/binaryen.js/low-memory-unused.js.txt
+++ b/test/binaryen.js/low-memory-unused.js.txt
@@ -32,7 +32,7 @@
(type $0 (func (param i32) (result i32)))
(memory $0 1)
(export "test" (func $test))
- (func $test (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $test (param $0 i32) (result i32)
(i32.load
(i32.add
(local.get $0)
@@ -48,7 +48,7 @@
(type $0 (func (param i32) (result i32)))
(memory $0 1)
(export "test" (func $test))
- (func $test (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $test (param $0 i32) (result i32)
(i32.load offset=128
(local.get $0)
)
diff --git a/test/binaryen.js/optimize-levels.js.txt b/test/binaryen.js/optimize-levels.js.txt
index ed928fbef..4e2902079 100644
--- a/test/binaryen.js/optimize-levels.js.txt
+++ b/test/binaryen.js/optimize-levels.js.txt
@@ -38,7 +38,7 @@ shrinkLevel=1
(module
(type $i (func (param i32) (result i32)))
(export "test" (func $test))
- (func $test (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $test (param $0 i32) (result i32)
(local.get $0)
)
)
@@ -60,7 +60,7 @@ shrinkLevel=1
(module
(type $i (func (param i32) (result i32)))
(export "test" (func $test))
- (func $test (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $test (param $0 i32) (result i32)
(local.get $0)
)
)
diff --git a/test/binaryen.js/sieve.js.txt b/test/binaryen.js/sieve.js.txt
index 46e5d3b39..e04b0bfe1 100644
--- a/test/binaryen.js/sieve.js.txt
+++ b/test/binaryen.js/sieve.js.txt
@@ -62,7 +62,7 @@ optimized:
(type $0 (func (param i32) (result i32)))
(memory $0 1 100)
(export "sieve" (func $sieve))
- (func $sieve (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $sieve (param $0 i32) (result i32)
(local $1 i32)
(if
(i32.lt_u
diff --git a/test/binaryen.js/stackir.js b/test/binaryen.js/stackir.js
index d797aa6d8..dd503a53d 100644
--- a/test/binaryen.js/stackir.js
+++ b/test/binaryen.js/stackir.js
@@ -26,4 +26,5 @@ console.log("=== default ===");
console.log(module.emitStackIR());
console.log("=== optimize ==="); // should omit the second block
-console.log(module.emitStackIR(true));
+binaryen.setOptimizeLevel(2);
+console.log(module.emitStackIR());
diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c
index e5b614bec..66cf43290 100644
--- a/test/example/c-api-kitchen-sink.c
+++ b/test/example/c-api-kitchen-sink.c
@@ -2008,12 +2008,17 @@ void test_binaries() {
BinaryenModuleWriteText(module, buffer, 1024);
printf("module s-expr printed (in memory):\n%s\n", buffer);
- // writ the s-expr representation to a pointer which is managed by the
+ // write the s-expr representation to a pointer which is managed by the
// caller
char* text = BinaryenModuleAllocateAndWriteText(module);
printf("module s-expr printed (in memory, caller-owned):\n%s\n", text);
free(text);
+ // write StackIR
+ text = BinaryenModuleAllocateAndWriteStackIR(module);
+ printf("module s-expr printed (StackIR):\n%s\n", text);
+ free(text);
+
BinaryenModuleDispose(module);
}
diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt
index 8bdf1ba52..fe784a453 100644
--- a/test/example/c-api-kitchen-sink.txt
+++ b/test/example/c-api-kitchen-sink.txt
@@ -3140,6 +3140,16 @@ module s-expr printed (in memory, caller-owned):
)
)
+module s-expr printed (StackIR):
+(module
+ (type $0 (func (param i32 i32) (result i32)))
+ (func $adder (param $0 i32) (param $1 i32) (result i32)
+ local.get $0
+ local.get $1
+ i32.add
+ )
+)
+
(module
(type $0 (func (param i32)))
(type $1 (func))
diff --git a/test/example/relooper-fuzz.txt b/test/example/relooper-fuzz.txt
index 1030fac32..e4c3f6db4 100644
--- a/test/example/relooper-fuzz.txt
+++ b/test/example/relooper-fuzz.txt
@@ -311,7 +311,7 @@
(memory $0 1 1)
(export "mem" (memory $0))
(start $main)
- (func $check (; has Stack IR ;) (result i32)
+ (func $check (result i32)
(if
(i32.eq
(i32.load
@@ -348,7 +348,7 @@
)
)
)
- (func $main (; has Stack IR ;)
+ (func $main
(local $0 i32)
(local $1 i32)
(i32.store
diff --git a/test/example/relooper-fuzz1.txt b/test/example/relooper-fuzz1.txt
index 3cdb12e69..675d51042 100644
--- a/test/example/relooper-fuzz1.txt
+++ b/test/example/relooper-fuzz1.txt
@@ -293,7 +293,7 @@
(memory $0 1 1)
(export "mem" (memory $0))
(start $main)
- (func $check (; has Stack IR ;) (result i32)
+ (func $check (result i32)
(if
(i32.eq
(i32.load
@@ -330,7 +330,7 @@
)
)
)
- (func $main (; has Stack IR ;)
+ (func $main
(local $0 i32)
(i32.store
(i32.const 8)
diff --git a/test/lit/help/wasm-as.test b/test/lit/help/wasm-as.test
index bf12fc1cf..09e5b7c9d 100644
--- a/test/lit/help/wasm-as.test
+++ b/test/lit/help/wasm-as.test
@@ -126,6 +126,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-ctor-eval.test b/test/lit/help/wasm-ctor-eval.test
index bbf8e1daa..e511ef0b9 100644
--- a/test/lit/help/wasm-ctor-eval.test
+++ b/test/lit/help/wasm-ctor-eval.test
@@ -133,6 +133,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-dis.test b/test/lit/help/wasm-dis.test
index f1f8283c8..2dffcc945 100644
--- a/test/lit/help/wasm-dis.test
+++ b/test/lit/help/wasm-dis.test
@@ -119,6 +119,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-emscripten-finalize.test b/test/lit/help/wasm-emscripten-finalize.test
index 8666c2ea1..a4ba48b16 100644
--- a/test/lit/help/wasm-emscripten-finalize.test
+++ b/test/lit/help/wasm-emscripten-finalize.test
@@ -161,6 +161,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-merge.test b/test/lit/help/wasm-merge.test
index 997923256..c87be5edf 100644
--- a/test/lit/help/wasm-merge.test
+++ b/test/lit/help/wasm-merge.test
@@ -149,6 +149,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-metadce.test b/test/lit/help/wasm-metadce.test
index 4d706dc0f..f0889eb81 100644
--- a/test/lit/help/wasm-metadce.test
+++ b/test/lit/help/wasm-metadce.test
@@ -174,6 +174,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index f7ee47c09..f6497618e 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -182,8 +182,6 @@
;; CHECK-NEXT: without JavaScript BigInt
;; CHECK-NEXT: support).
;; CHECK-NEXT:
-;; CHECK-NEXT: --generate-stack-ir generate Stack IR
-;; CHECK-NEXT:
;; CHECK-NEXT: --global-refining refine the types of globals
;; CHECK-NEXT:
;; CHECK-NEXT: --gsi globally optimize struct values
@@ -333,8 +331,6 @@
;; CHECK-NEXT: --optimize-j2cl optimizes J2CL specific
;; CHECK-NEXT: constructs.
;; CHECK-NEXT:
-;; CHECK-NEXT: --optimize-stack-ir optimize Stack IR
-;; CHECK-NEXT:
;; CHECK-NEXT: --outlining outline instructions
;; CHECK-NEXT:
;; CHECK-NEXT: --pick-load-signs pick load signs based on their
@@ -369,9 +365,6 @@
;; CHECK-NEXT: --print-minified print in minified s-expression
;; CHECK-NEXT: format
;; CHECK-NEXT:
-;; CHECK-NEXT: --print-stack-ir print out Stack IR (useful for
-;; CHECK-NEXT: internal debugging)
-;; CHECK-NEXT:
;; CHECK-NEXT: --propagate-debug-locs propagate debug location from
;; CHECK-NEXT: parents or previous siblings to
;; CHECK-NEXT: child nodes
@@ -746,6 +739,12 @@
;; CHECK-NEXT: in, but not inspect their
;; CHECK-NEXT: contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-reduce.test b/test/lit/help/wasm-reduce.test
index e28894e4a..a98b8a199 100644
--- a/test/lit/help/wasm-reduce.test
+++ b/test/lit/help/wasm-reduce.test
@@ -155,6 +155,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm-split.test b/test/lit/help/wasm-split.test
index e7b287fcc..fcd919ea8 100644
--- a/test/lit/help/wasm-split.test
+++ b/test/lit/help/wasm-split.test
@@ -235,6 +235,12 @@
;; CHECK-NEXT: them and pass them back in, but not
;; CHECK-NEXT: inspect their contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 4b0330db6..13e93c33b 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -132,8 +132,6 @@
;; CHECK-NEXT: without JavaScript BigInt
;; CHECK-NEXT: support).
;; CHECK-NEXT:
-;; CHECK-NEXT: --generate-stack-ir generate Stack IR
-;; CHECK-NEXT:
;; CHECK-NEXT: --global-refining refine the types of globals
;; CHECK-NEXT:
;; CHECK-NEXT: --gsi globally optimize struct values
@@ -283,8 +281,6 @@
;; CHECK-NEXT: --optimize-j2cl optimizes J2CL specific
;; CHECK-NEXT: constructs.
;; CHECK-NEXT:
-;; CHECK-NEXT: --optimize-stack-ir optimize Stack IR
-;; CHECK-NEXT:
;; CHECK-NEXT: --outlining outline instructions
;; CHECK-NEXT:
;; CHECK-NEXT: --pick-load-signs pick load signs based on their
@@ -319,9 +315,6 @@
;; CHECK-NEXT: --print-minified print in minified s-expression
;; CHECK-NEXT: format
;; CHECK-NEXT:
-;; CHECK-NEXT: --print-stack-ir print out Stack IR (useful for
-;; CHECK-NEXT: internal debugging)
-;; CHECK-NEXT:
;; CHECK-NEXT: --propagate-debug-locs propagate debug location from
;; CHECK-NEXT: parents or previous siblings to
;; CHECK-NEXT: child nodes
@@ -696,6 +689,12 @@
;; CHECK-NEXT: in, but not inspect their
;; CHECK-NEXT: contents or call them.
;; CHECK-NEXT:
+;; CHECK-NEXT: --generate-stack-ir generate StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --optimize-stack-ir optimize StackIR during writing
+;; CHECK-NEXT:
+;; CHECK-NEXT: --print-stack-ir print StackIR during writing
+;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: General options:
;; CHECK-NEXT: ----------------
diff --git a/test/lit/passes/O.wast b/test/lit/passes/O.wast
index 7affbb8b7..baba79ee0 100644
--- a/test/lit/passes/O.wast
+++ b/test/lit/passes/O.wast
@@ -20,7 +20,7 @@
;; CHECK: (export "end-if-else-call" (func $end-if-else-call))
- ;; CHECK: (func $ret (; has Stack IR ;) (result i32)
+ ;; CHECK: (func $ret (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $ret)
;; CHECK-NEXT: )
@@ -50,7 +50,7 @@
(unreachable)
)
)
- ;; CHECK: (func $if-0-unreachable-to-none (; has Stack IR ;) (param $0 i64)
+ ;; CHECK: (func $if-0-unreachable-to-none (param $0 i64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $if-0-unreachable-to-none (export "waka") (param $var$0 i64)
@@ -68,7 +68,7 @@
)
)
)
- ;; CHECK: (func $many-selects (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $many-selects (param $0 i32) (result i32)
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const -1073741824)
;; CHECK-NEXT: (select
@@ -112,7 +112,7 @@
)
(local.get $0)
)
- ;; CHECK: (func $end-if-else (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $end-if-else (param $0 i32) (result i32)
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $0)
@@ -130,7 +130,7 @@
)
(local.get $x)
)
- ;; CHECK: (func $end-if-else-call (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $end-if-else-call (param $0 i32) (result i32)
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (then
diff --git a/test/lit/passes/O1_skip.wast b/test/lit/passes/O1_skip.wast
index 2cefecdcf..54d6a6843 100644
--- a/test/lit/passes/O1_skip.wast
+++ b/test/lit/passes/O1_skip.wast
@@ -46,7 +46,7 @@
)
;; CHECK: (export "foo" (func $foo))
-;; CHECK: (func $foo (; has Stack IR ;) (param $p i32)
+;; CHECK: (func $foo (param $p i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (call $log
diff --git a/test/lit/passes/O3_Oz.wast b/test/lit/passes/O3_Oz.wast
index 48345663e..cc129c964 100644
--- a/test/lit/passes/O3_Oz.wast
+++ b/test/lit/passes/O3_Oz.wast
@@ -14,7 +14,7 @@
;; CHECK: (export "export" (func $export))
- ;; CHECK: (func $export (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $export (param $0 i32) (result i32)
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.tee $0
;; CHECK-NEXT: (i32.add
diff --git a/test/lit/passes/O3_inline-functions-with-loops_flexible-inline-max-function-size=30.wast b/test/lit/passes/O3_inline-functions-with-loops_flexible-inline-max-function-size=30.wast
index 7037baf7d..830008e2f 100644
--- a/test/lit/passes/O3_inline-functions-with-loops_flexible-inline-max-function-size=30.wast
+++ b/test/lit/passes/O3_inline-functions-with-loops_flexible-inline-max-function-size=30.wast
@@ -22,7 +22,7 @@
;; CHECK: (export "memory" (memory $memory))
- ;; CHECK: (func $fib (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $fib (param $0 i32) (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $0)
@@ -76,7 +76,7 @@
)
)
)
- ;; CHECK: (func $looped (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $looped (param $0 i32) (result i32)
;; CHECK-NEXT: (loop $L0
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
@@ -123,7 +123,7 @@
)
)
- ;; CHECK: (func $t1 (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $t1 (param $0 i32) (result i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $0)
@@ -157,7 +157,7 @@
)
)
)
- ;; CHECK: (func $t2 (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $t2 (param $0 i32) (result i32)
;; CHECK-NEXT: (call $fib
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -168,7 +168,7 @@
)
)
- ;; CHECK: (func $t3 (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $t3 (param $0 i32) (result i32)
;; CHECK-NEXT: (call $fib
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $0)
diff --git a/test/lit/passes/O4_disable-bulk-memory.wast b/test/lit/passes/O4_disable-bulk-memory.wast
index 0cafb1d5d..d75258a96 100644
--- a/test/lit/passes/O4_disable-bulk-memory.wast
+++ b/test/lit/passes/O4_disable-bulk-memory.wast
@@ -9,7 +9,7 @@
(global $global$0 (mut i32) (i32.const 10))
;; CHECK: (export "func_59_invoker" (func $0))
(export "func_59_invoker" (func $0))
- ;; CHECK: (func $0 (; has Stack IR ;)
+ ;; CHECK: (func $0
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $0 (; 0 ;) (type $0)
@@ -226,7 +226,7 @@
)
(local.get $0)
)
- ;; CHECK: (func $~lib/allocator/arena/__memory_allocate (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $~lib/allocator/arena/__memory_allocate (param $0 i32) (result i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
@@ -569,7 +569,7 @@
)
(local.get $0)
)
- ;; CHECK: (func $assembly/index/Body#constructor (; has Stack IR ;) (param $0 f64) (param $1 f64) (param $2 f64) (param $3 f64) (param $4 f64) (param $5 f64) (param $6 f64) (result i32)
+ ;; CHECK: (func $assembly/index/Body#constructor (param $0 f64) (param $1 f64) (param $2 f64) (param $3 f64) (param $4 f64) (param $5 f64) (param $6 f64) (result i32)
;; CHECK-NEXT: (local $7 i32)
;; CHECK-NEXT: (f64.store
;; CHECK-NEXT: (local.tee $7
@@ -1299,7 +1299,7 @@
(local.get $5)
)
)
- ;; CHECK: (func $assembly/index/init (; has Stack IR ;)
+ ;; CHECK: (func $assembly/index/init
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
@@ -1873,7 +1873,7 @@
)
)
)
- ;; CHECK: (func $assembly/index/NBodySystem#advance (; has Stack IR ;) (param $0 i32)
+ ;; CHECK: (func $assembly/index/NBodySystem#advance (param $0 i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (local $3 i32)
@@ -2723,7 +2723,7 @@
)
(local.get $7)
)
- ;; CHECK: (func $assembly/index/step (; has Stack IR ;) (result f64)
+ ;; CHECK: (func $assembly/index/step (result f64)
;; CHECK-NEXT: (local $0 f64)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
@@ -2933,7 +2933,7 @@
(global.get $global$5)
)
)
- ;; CHECK: (func $assembly/index/bench (; has Stack IR ;) (param $0 i32)
+ ;; CHECK: (func $assembly/index/bench (param $0 i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (block $label$1
;; CHECK-NEXT: (loop $label$2
@@ -2985,7 +2985,7 @@
)
)
)
- ;; CHECK: (func $assembly/index/getBody (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $assembly/index/getBody (param $0 i32) (result i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.lt_u
@@ -3065,7 +3065,7 @@
)
)
)
- ;; CHECK: (func $start (; has Stack IR ;)
+ ;; CHECK: (func $start
;; CHECK-NEXT: (global.set $global$0
;; CHECK-NEXT: (i32.const 104)
;; CHECK-NEXT: )
@@ -3076,7 +3076,7 @@
(func $start (; 26 ;) (type $0)
(call $start:assembly/index)
)
- ;; CHECK: (func $null (; has Stack IR ;)
+ ;; CHECK: (func $null
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $null (; 27 ;) (type $0)
diff --git a/test/lit/passes/O_fast-math.wast b/test/lit/passes/O_fast-math.wast
index 722f44a05..a964edc99 100644
--- a/test/lit/passes/O_fast-math.wast
+++ b/test/lit/passes/O_fast-math.wast
@@ -37,7 +37,7 @@
;; CHECK: (export "abs_sub_zero2" (func $abs_sub_zero2))
- ;; CHECK: (func $div (; has Stack IR ;) (result f32)
+ ;; CHECK: (func $div (result f32)
;; CHECK-NEXT: (f32.const -nan:0x23017a)
;; CHECK-NEXT: )
(func $div (export "div") (result f32)
@@ -46,7 +46,7 @@
(f32.const 1)
)
)
- ;; CHECK: (func $mul1 (; has Stack IR ;) (result f32)
+ ;; CHECK: (func $mul1 (result f32)
;; CHECK-NEXT: (f32.const -nan:0x34546d)
;; CHECK-NEXT: )
(func $mul1 (export "mul1") (result f32)
@@ -55,7 +55,7 @@
(f32.const 1)
)
)
- ;; CHECK: (func $mul2 (; has Stack IR ;) (result f32)
+ ;; CHECK: (func $mul2 (result f32)
;; CHECK-NEXT: (f32.const nan:0x400000)
;; CHECK-NEXT: )
(func $mul2 (export "mul2") (result f32)
@@ -100,7 +100,7 @@
(f32.const -0)
)
)
- ;; CHECK: (func $mul_neg_one1 (; has Stack IR ;) (param $0 f32) (result f32)
+ ;; CHECK: (func $mul_neg_one1 (param $0 f32) (result f32)
;; CHECK-NEXT: (f32.neg
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -111,7 +111,7 @@
(f32.const -1)
)
)
- ;; CHECK: (func $mul_neg_one2 (; has Stack IR ;) (param $0 f64) (result f64)
+ ;; CHECK: (func $mul_neg_one2 (param $0 f64) (result f64)
;; CHECK-NEXT: (f64.neg
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -122,7 +122,7 @@
(f64.const -1)
)
)
- ;; CHECK: (func $abs_sub_zero1 (; has Stack IR ;) (param $0 f32) (result f32)
+ ;; CHECK: (func $abs_sub_zero1 (param $0 f32) (result f32)
;; CHECK-NEXT: (f32.abs
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
@@ -136,7 +136,7 @@
)
)
)
- ;; CHECK: (func $abs_sub_zero2 (; has Stack IR ;) (param $0 f64) (result f64)
+ ;; CHECK: (func $abs_sub_zero2 (param $0 f64) (result f64)
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
diff --git a/test/lit/passes/Oz.wast b/test/lit/passes/Oz.wast
index 8c1db3083..eab5a95af 100644
--- a/test/lit/passes/Oz.wast
+++ b/test/lit/passes/Oz.wast
@@ -37,7 +37,7 @@
;; CHECK: (export "precompute-simd" (func $precompute-simd))
- ;; CHECK: (func $basics (type $0) (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ ;; CHECK: (func $basics (type $0) (param $0 i32) (param $1 i32) (result i32)
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.tee $0
;; CHECK-NEXT: (i32.add
@@ -59,7 +59,7 @@
)
(i32.add (local.get $x2) (local.get $y2))
)
- ;; CHECK: (func $8 (type $2) (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32)
+ ;; CHECK: (func $8 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32)
;; CHECK-NEXT: (i32.store
;; CHECK-NEXT: (local.tee $0
;; CHECK-NEXT: (i32.add
@@ -131,7 +131,7 @@
)
)
- ;; CHECK: (func $9 (type $1) (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $9 (type $1) (param $0 i32) (result i32)
;; CHECK-NEXT: (i32.mul
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (i32.const -4)
@@ -147,7 +147,7 @@
)
)
- ;; CHECK: (func $10 (type $1) (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $10 (type $1) (param $0 i32) (result i32)
;; CHECK-NEXT: (i32.shl
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (i32.const 31)
@@ -163,7 +163,7 @@
)
)
- ;; CHECK: (func $11 (type $1) (; has Stack IR ;) (param $0 i32) (result i32)
+ ;; CHECK: (func $11 (type $1) (param $0 i32) (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (then
@@ -201,7 +201,7 @@
)
(i32.const 0)
)
- ;; CHECK: (func $12 (type $0) (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ ;; CHECK: (func $12 (type $0) (param $0 i32) (param $1 i32) (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (then
@@ -239,7 +239,7 @@
)
(i32.const 0)
)
- ;; CHECK: (func $13 (type $0) (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ ;; CHECK: (func $13 (type $0) (param $0 i32) (param $1 i32) (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (select
;; CHECK-NEXT: (local.get $1)
@@ -268,7 +268,7 @@
(i32.const 0)
)
- ;; CHECK: (func $14 (type $0) (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ ;; CHECK: (func $14 (type $0) (param $0 i32) (param $1 i32) (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 0)
@@ -297,7 +297,7 @@
(i32.const 0)
)
- ;; CHECK: (func $precompute-simd (type $3) (; has Stack IR ;) (result v128)
+ ;; CHECK: (func $precompute-simd (type $3) (result v128)
;; CHECK-NEXT: (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
;; CHECK-NEXT: )
(func $precompute-simd (export "precompute-simd") (result v128)
diff --git a/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast b/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast
index 99ac2c05f..ec31a4118 100644
--- a/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast
+++ b/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast
@@ -42,7 +42,7 @@
;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state))
- ;; CHECK: (func $calls-import (; has Stack IR ;)
+ ;; CHECK: (func $calls-import
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: (i32.store
@@ -76,7 +76,7 @@
(drop (i32.eqz (i32.const 17)))
)
)
-;; CHECK: (func $asyncify_start_unwind (; has Stack IR ;) (param $0 i32)
+;; CHECK: (func $asyncify_start_unwind (param $0 i32)
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
@@ -98,7 +98,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_stop_unwind (; has Stack IR ;)
+;; CHECK: (func $asyncify_stop_unwind
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
@@ -117,7 +117,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_start_rewind (; has Stack IR ;) (param $0 i32)
+;; CHECK: (func $asyncify_start_rewind (param $0 i32)
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
@@ -139,6 +139,6 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_get_state (; has Stack IR ;) (result i32)
+;; CHECK: (func $asyncify_get_state (result i32)
;; CHECK-NEXT: (global.get $__asyncify_state)
;; CHECK-NEXT: )
diff --git a/test/lit/passes/asyncify_mod-asyncify-never-unwind_O.wast b/test/lit/passes/asyncify_mod-asyncify-never-unwind_O.wast
index 38a54ea80..eefdabe34 100644
--- a/test/lit/passes/asyncify_mod-asyncify-never-unwind_O.wast
+++ b/test/lit/passes/asyncify_mod-asyncify-never-unwind_O.wast
@@ -41,7 +41,7 @@
;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state))
- ;; CHECK: (func $calls-import (; has Stack IR ;)
+ ;; CHECK: (func $calls-import
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.or
;; CHECK-NEXT: (i32.eqz
@@ -95,7 +95,7 @@
(drop (i32.eqz (i32.const 17)))
)
)
-;; CHECK: (func $asyncify_start_unwind (; has Stack IR ;) (param $0 i32)
+;; CHECK: (func $asyncify_start_unwind (param $0 i32)
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
@@ -117,7 +117,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_stop_unwind (; has Stack IR ;)
+;; CHECK: (func $asyncify_stop_unwind
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
@@ -136,7 +136,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_start_rewind (; has Stack IR ;) (param $0 i32)
+;; CHECK: (func $asyncify_start_rewind (param $0 i32)
;; CHECK-NEXT: (global.set $__asyncify_state
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
@@ -158,6 +158,6 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $asyncify_get_state (; has Stack IR ;) (result i32)
+;; CHECK: (func $asyncify_get_state (result i32)
;; CHECK-NEXT: (global.get $__asyncify_state)
;; CHECK-NEXT: )
diff --git a/test/lit/passes/flatten_dfo_O3_enable-threads.wast b/test/lit/passes/flatten_dfo_O3_enable-threads.wast
index 34142b73f..3e2dfb539 100644
--- a/test/lit/passes/flatten_dfo_O3_enable-threads.wast
+++ b/test/lit/passes/flatten_dfo_O3_enable-threads.wast
@@ -28,7 +28,7 @@
;; CHECK: (export "dfo-tee-get" (func $dfo-tee-get))
- ;; CHECK: (func $one (; has Stack IR ;)
+ ;; CHECK: (func $one
;; CHECK-NEXT: (block $label$3
;; CHECK-NEXT: (br_if $label$3
;; CHECK-NEXT: (i32.load
@@ -56,7 +56,7 @@
)
(unreachable)
)
- ;; CHECK: (func $two (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ ;; CHECK: (func $two (param $0 i32) (param $1 i32) (result i32)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
(func $two (export "two") (param $var$0 i32) (param $var$1 i32) (result i32)
@@ -110,7 +110,7 @@
(nop)
(i32.const 0)
)
- ;; CHECK: (func $use-var (; has Stack IR ;) (param $0 i64) (param $1 i32) (result f64)
+ ;; CHECK: (func $use-var (param $0 i64) (param $1 i32) (result f64)
;; CHECK-NEXT: (loop $label$8
;; CHECK-NEXT: (br_if $label$8
;; CHECK-NEXT: (local.get $1)
@@ -184,7 +184,7 @@
)
(unreachable)
)
- ;; CHECK: (func $bad1 (; has Stack IR ;)
+ ;; CHECK: (func $bad1
;; CHECK-NEXT: (i32.store
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const -16384)
@@ -236,7 +236,7 @@
)
)
)
- ;; CHECK: (func $only-dfo (; has Stack IR ;) (param $0 f64) (result f64)
+ ;; CHECK: (func $only-dfo (param $0 f64) (result f64)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (loop $label$1
;; CHECK-NEXT: (br $label$1)
@@ -270,7 +270,7 @@
(br $label$1)
)
)
- ;; CHECK: (func $dfo-tee-get (; has Stack IR ;) (result i32)
+ ;; CHECK: (func $dfo-tee-get (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
(func $dfo-tee-get (export "dfo-tee-get") (result i32)
diff --git a/test/lit/passes/flatten_simplify-locals-nonesting_dfo_O3.wast b/test/lit/passes/flatten_simplify-locals-nonesting_dfo_O3.wast
index 142fabeea..2c033e0fe 100644
--- a/test/lit/passes/flatten_simplify-locals-nonesting_dfo_O3.wast
+++ b/test/lit/passes/flatten_simplify-locals-nonesting_dfo_O3.wast
@@ -25,7 +25,7 @@
;; CHECK: (export "only-dfo" (func $only-dfo))
- ;; CHECK: (func $if-select (; has Stack IR ;)
+ ;; CHECK: (func $if-select
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $if-select (export "if-select")
@@ -47,7 +47,7 @@
)
)
)
- ;; CHECK: (func $unreachable-body-update-zext (; has Stack IR ;) (result f64)
+ ;; CHECK: (func $unreachable-body-update-zext (result f64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $unreachable-body-update-zext (export "unreachable-body-update-zext") (result f64)
@@ -61,7 +61,7 @@
)
(f64.const -9223372036854775808)
)
- ;; CHECK: (func $ssa-const (; has Stack IR ;) (param $0 i32) (param $1 f64) (param $2 f64) (result i32)
+ ;; CHECK: (func $ssa-const (param $0 i32) (param $1 f64) (param $2 f64) (result i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $ssa-const (export "ssa-const") (param $var$0 i32) (param $var$1 f64) (param $var$2 f64) (result i32)
@@ -99,7 +99,7 @@
)
)
)
- ;; CHECK: (func $if-nothing (; has Stack IR ;) (param $0 i64)
+ ;; CHECK: (func $if-nothing (param $0 i64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $if-nothing (export "if-nothing") (param $var$0 i64)
@@ -130,7 +130,7 @@
(unreachable)
)
)
- ;; CHECK: (func $only-dfo (; has Stack IR ;) (param $0 f64) (result i32)
+ ;; CHECK: (func $only-dfo (param $0 f64) (result i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (loop $label$1
;; CHECK-NEXT: (if
diff --git a/test/lit/passes/global-effects-O.wast b/test/lit/passes/global-effects-O.wast
index c24159eac..c7ba06781 100644
--- a/test/lit/passes/global-effects-O.wast
+++ b/test/lit/passes/global-effects-O.wast
@@ -56,13 +56,13 @@
;; CHECK_1: (func $main (type $0)
;; CHECK_1-NEXT: (nop)
;; CHECK_1-NEXT: )
- ;; CHECK_3: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_3: (func $main (type $0)
;; CHECK_3-NEXT: (nop)
;; CHECK_3-NEXT: )
- ;; CHECK_s: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_s: (func $main (type $0)
;; CHECK_s-NEXT: (nop)
;; CHECK_s-NEXT: )
- ;; CHECK_O: (func $main (type $0) (; has Stack IR ;)
+ ;; CHECK_O: (func $main (type $0)
;; CHECK_O-NEXT: (nop)
;; CHECK_O-NEXT: )
(func $main
@@ -119,7 +119,7 @@
;; CHECK_1-NEXT: (local.get $0)
;; CHECK_1-NEXT: )
;; CHECK_1-NEXT: )
- ;; CHECK_3: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_3: (func $pointless-work (type $1) (result i32)
;; CHECK_3-NEXT: (local $0 i32)
;; CHECK_3-NEXT: (loop $loop (result i32)
;; CHECK_3-NEXT: (br_if $loop
@@ -136,7 +136,7 @@
;; CHECK_3-NEXT: (local.get $0)
;; CHECK_3-NEXT: )
;; CHECK_3-NEXT: )
- ;; CHECK_s: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_s: (func $pointless-work (type $1) (result i32)
;; CHECK_s-NEXT: (local $0 i32)
;; CHECK_s-NEXT: (loop $loop (result i32)
;; CHECK_s-NEXT: (br_if $loop
@@ -153,7 +153,7 @@
;; CHECK_s-NEXT: (local.get $0)
;; CHECK_s-NEXT: )
;; CHECK_s-NEXT: )
- ;; CHECK_O: (func $pointless-work (type $1) (; has Stack IR ;) (result i32)
+ ;; CHECK_O: (func $pointless-work (type $1) (result i32)
;; CHECK_O-NEXT: (local $0 i32)
;; CHECK_O-NEXT: (loop $loop (result i32)
;; CHECK_O-NEXT: (br_if $loop
diff --git a/test/lit/passes/gto_and_cfp_in_O.wast b/test/lit/passes/gto_and_cfp_in_O.wast
index a723038eb..b1e9a47df 100644
--- a/test/lit/passes/gto_and_cfp_in_O.wast
+++ b/test/lit/passes/gto_and_cfp_in_O.wast
@@ -26,7 +26,7 @@
;; OPEN_WORLD: (export "main" (func $main))
- ;; OPEN_WORLD: (func $by-ref (type $1) (; has Stack IR ;)
+ ;; OPEN_WORLD: (func $by-ref (type $1)
;; OPEN_WORLD-NEXT: (struct.set $struct 1
;; OPEN_WORLD-NEXT: (global.get $glob)
;; OPEN_WORLD-NEXT: (i32.const 200)
@@ -49,10 +49,10 @@
;; CHECK: (export "main" (func $main))
- ;; CHECK: (func $main (type $0) (; has Stack IR ;) (result i32)
+ ;; CHECK: (func $main (type $0) (result i32)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
- ;; OPEN_WORLD: (func $main (type $2) (; has Stack IR ;) (result i32)
+ ;; OPEN_WORLD: (func $main (type $2) (result i32)
;; OPEN_WORLD-NEXT: (struct.get $struct 1
;; OPEN_WORLD-NEXT: (global.get $glob)
;; OPEN_WORLD-NEXT: )
diff --git a/test/lit/passes/type-ssa_and_merging.wast b/test/lit/passes/type-ssa_and_merging.wast
index 670de31ac..8928820bf 100644
--- a/test/lit/passes/type-ssa_and_merging.wast
+++ b/test/lit/passes/type-ssa_and_merging.wast
@@ -33,7 +33,7 @@
;; NOP: (export "main2" (func $main2))
- ;; NOP: (func $main1 (type $2) (; has Stack IR ;) (result i32)
+ ;; NOP: (func $main1 (type $2) (result i32)
;; NOP-NEXT: (call $get-a-1
;; NOP-NEXT: (struct.new $A
;; NOP-NEXT: (i32.const 42)
@@ -57,7 +57,7 @@
)
)
- ;; NOP: (func $main2 (type $2) (; has Stack IR ;) (result i32)
+ ;; NOP: (func $main2 (type $2) (result i32)
;; NOP-NEXT: (call $get-a-2
;; NOP-NEXT: (struct.new $A
;; NOP-NEXT: (i32.const 1337)
@@ -77,7 +77,7 @@
)
)
- ;; NOP: (func $get-a-1 (type $0) (; has Stack IR ;) (param $0 (ref $A)) (result i32)
+ ;; NOP: (func $get-a-1 (type $0) (param $0 (ref $A)) (result i32)
;; NOP-NEXT: (if
;; NOP-NEXT: (call $import)
;; NOP-NEXT: (then
@@ -119,7 +119,7 @@
(struct.get $A 0 (local.get 0))
)
- ;; NOP: (func $get-a-2 (type $0) (; has Stack IR ;) (param $0 (ref $A)) (result i32)
+ ;; NOP: (func $get-a-2 (type $0) (param $0 (ref $A)) (result i32)
;; NOP-NEXT: (if
;; NOP-NEXT: (call $import)
;; NOP-NEXT: (then
diff --git a/test/passes/O.bin.txt b/test/passes/O.bin.txt
index d35a4d11c..3302ebcc7 100644
--- a/test/passes/O.bin.txt
+++ b/test/passes/O.bin.txt
@@ -5,7 +5,7 @@
(export "fac-iter" (func $2))
(export "fac-iter-named" (func $3))
(export "fac-opt" (func $4))
- (func $0 (; has Stack IR ;) (param $0 i64) (result i64)
+ (func $0 (param $0 i64) (result i64)
(if (result i64)
(i64.eqz
(local.get $0)
@@ -26,7 +26,7 @@
)
)
)
- (func $1 (; has Stack IR ;) (param $0 i64) (result i64)
+ (func $1 (param $0 i64) (result i64)
(if (result i64)
(i64.eqz
(local.get $0)
@@ -47,10 +47,10 @@
)
)
)
- (func $2 (; has Stack IR ;) (param $0 i64) (result i64)
+ (func $2 (param $0 i64) (result i64)
(unreachable)
)
- (func $3 (; has Stack IR ;) (param $0 i64) (result i64)
+ (func $3 (param $0 i64) (result i64)
(local $1 i64)
(local.set $1
(i64.const 1)
@@ -81,7 +81,7 @@
)
(local.get $1)
)
- (func $4 (; has Stack IR ;) (param $0 i64) (result i64)
+ (func $4 (param $0 i64) (result i64)
(local $1 i64)
(local.set $1
(i64.const 1)
diff --git a/test/passes/O1_print-stack-ir.txt b/test/passes/O1_print-stack-ir.txt
index 0a64713d3..d6cececfc 100644
--- a/test/passes/O1_print-stack-ir.txt
+++ b/test/passes/O1_print-stack-ir.txt
@@ -28,25 +28,16 @@
(type $0 (func (param i32) (result i32)))
(export "stacky-help" (func $stacky-help))
(func $stacky-help (param $0 i32) (result i32)
- (i32.add
- (call $stacky-help
- (i32.const 0)
- )
- (block (result i32)
- (local.set $0
- (call $stacky-help
- (i32.const 1)
- )
- )
- (drop
- (call $stacky-help
- (i32.const 2)
- )
- )
- (i32.eqz
- (local.get $0)
- )
- )
- )
+ i32.const 0
+ call $stacky-help
+ i32.const 1
+ call $stacky-help
+ local.set $0
+ i32.const 2
+ call $stacky-help
+ drop
+ local.get $0
+ i32.eqz
+ i32.add
)
)
diff --git a/test/passes/O2_precompute-propagate_print-stack-ir.txt b/test/passes/O2_precompute-propagate_print-stack-ir.txt
index b0789141a..119d1cafb 100644
--- a/test/passes/O2_precompute-propagate_print-stack-ir.txt
+++ b/test/passes/O2_precompute-propagate_print-stack-ir.txt
@@ -15,10 +15,8 @@
(export "func" (func $func))
(func $func (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i64) (result i64)
(local $4 i32)
- (local.set $3
- (i64.const 2147483647)
- )
- (nop)
- (i64.const 2147483647)
+ i64.const 2147483647
+ local.set $3
+ i64.const 2147483647
)
)
diff --git a/test/passes/O2_print-stack-ir.txt b/test/passes/O2_print-stack-ir.txt
index cdfccc3d4..d6cececfc 100644
--- a/test/passes/O2_print-stack-ir.txt
+++ b/test/passes/O2_print-stack-ir.txt
@@ -2,23 +2,6 @@
(type $0 (func (param i32) (result i32)))
(export "stacky-help" (func $stacky-help))
(func $stacky-help (param $0 i32) (result i32)
- i32.const 0
- call $stacky-help
- i32.const 1
- call $stacky-help
- local.set $0
- i32.const 2
- call $stacky-help
- drop
- local.get $0
- i32.eqz
- i32.add
- )
-)
-(module
- (type $0 (func (param i32) (result i32)))
- (export "stacky-help" (func $stacky-help))
- (func $stacky-help (; has Stack IR ;) (param $0 i32) (result i32)
(i32.add
(call $stacky-help
(i32.const 0)
@@ -41,3 +24,20 @@
)
)
)
+(module
+ (type $0 (func (param i32) (result i32)))
+ (export "stacky-help" (func $stacky-help))
+ (func $stacky-help (param $0 i32) (result i32)
+ i32.const 0
+ call $stacky-help
+ i32.const 1
+ call $stacky-help
+ local.set $0
+ i32.const 2
+ call $stacky-help
+ drop
+ local.get $0
+ i32.eqz
+ i32.add
+ )
+)
diff --git a/test/passes/O3_low-memory-unused_metrics.txt b/test/passes/O3_low-memory-unused_metrics.txt
index 547eb1673..fd9245adc 100644
--- a/test/passes/O3_low-memory-unused_metrics.txt
+++ b/test/passes/O3_low-memory-unused_metrics.txt
@@ -42,7 +42,7 @@ total
(import "env" "memset" (func $fimport$97 (param i32 i32 i32) (result i32)))
(import "env" "memcpy" (func $fimport$98 (param i32 i32 i32) (result i32)))
(export "deflate" (func $0))
- (func $0 (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ (func $0 (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
diff --git a/test/passes/O3_print-stack-ir.txt b/test/passes/O3_print-stack-ir.txt
index 569c49966..18aa4053f 100644
--- a/test/passes/O3_print-stack-ir.txt
+++ b/test/passes/O3_print-stack-ir.txt
@@ -2,21 +2,6 @@
(type $0 (func (param i32) (result i32)))
(export "stacky-help" (func $stacky-help))
(func $stacky-help (param $0 i32) (result i32)
- i32.const 0
- call $stacky-help
- i32.const 1
- call $stacky-help
- i32.const 2
- call $stacky-help
- drop
- i32.eqz
- i32.add
- )
-)
-(module
- (type $0 (func (param i32) (result i32)))
- (export "stacky-help" (func $stacky-help))
- (func $stacky-help (; has Stack IR ;) (param $0 i32) (result i32)
(i32.add
(call $stacky-help
(i32.const 0)
@@ -39,3 +24,18 @@
)
)
)
+(module
+ (type $0 (func (param i32) (result i32)))
+ (export "stacky-help" (func $stacky-help))
+ (func $stacky-help (param $0 i32) (result i32)
+ i32.const 0
+ call $stacky-help
+ i32.const 1
+ call $stacky-help
+ i32.const 2
+ call $stacky-help
+ drop
+ i32.eqz
+ i32.add
+ )
+)
diff --git a/test/passes/Os_print-stack-ir_all-features_disable-gc.txt b/test/passes/Os_print-stack-ir_all-features_disable-gc.txt
index 569c49966..18aa4053f 100644
--- a/test/passes/Os_print-stack-ir_all-features_disable-gc.txt
+++ b/test/passes/Os_print-stack-ir_all-features_disable-gc.txt
@@ -2,21 +2,6 @@
(type $0 (func (param i32) (result i32)))
(export "stacky-help" (func $stacky-help))
(func $stacky-help (param $0 i32) (result i32)
- i32.const 0
- call $stacky-help
- i32.const 1
- call $stacky-help
- i32.const 2
- call $stacky-help
- drop
- i32.eqz
- i32.add
- )
-)
-(module
- (type $0 (func (param i32) (result i32)))
- (export "stacky-help" (func $stacky-help))
- (func $stacky-help (; has Stack IR ;) (param $0 i32) (result i32)
(i32.add
(call $stacky-help
(i32.const 0)
@@ -39,3 +24,18 @@
)
)
)
+(module
+ (type $0 (func (param i32) (result i32)))
+ (export "stacky-help" (func $stacky-help))
+ (func $stacky-help (param $0 i32) (result i32)
+ i32.const 0
+ call $stacky-help
+ i32.const 1
+ call $stacky-help
+ i32.const 2
+ call $stacky-help
+ drop
+ i32.eqz
+ i32.add
+ )
+)
diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt
index c46085be1..b69b9728c 100644
--- a/test/passes/Oz_fuzz-exec_all-features.txt
+++ b/test/passes/Oz_fuzz-exec_all-features.txt
@@ -81,7 +81,7 @@
(export "static-casts" (func $static-casts))
(export "static-br_on_cast" (func $br_on_cast))
(export "static-br_on_cast_fail" (func $static-br_on_cast_fail))
- (func $structs (type $void_func) (; has Stack IR ;)
+ (func $structs (type $void_func)
(local $0 i32)
(call $log
(i32.const 0)
@@ -98,7 +98,7 @@
(i32.const 100)
)
)
- (func $arrays (type $void_func) (; has Stack IR ;)
+ (func $arrays (type $void_func)
(local $0 (ref $bytes))
(local.set $0
(array.new $bytes
@@ -139,12 +139,12 @@
)
)
)
- (func $br_on_cast (type $void_func) (; has Stack IR ;)
+ (func $br_on_cast (type $void_func)
(call $log
(i32.const 3)
)
)
- (func $br_on_failed_cast-1 (type $void_func) (; has Stack IR ;)
+ (func $br_on_failed_cast-1 (type $void_func)
(local $0 (ref $struct))
(local.set $0
(struct.new_default $struct)
@@ -166,7 +166,7 @@
)
)
)
- (func $br_on_failed_cast-2 (type $void_func) (; has Stack IR ;)
+ (func $br_on_failed_cast-2 (type $void_func)
(call $log
(i32.const 1)
)
@@ -174,15 +174,15 @@
(i32.const 999)
)
)
- (func $cast-null-anyref-to-gc (type $void_func) (; has Stack IR ;)
+ (func $cast-null-anyref-to-gc (type $void_func)
(call $log
(i32.const 0)
)
)
- (func $br-on_non_null (type $void_func) (; has Stack IR ;)
+ (func $br-on_non_null (type $void_func)
(nop)
)
- (func $br-on_non_null-2 (type $void_func) (; has Stack IR ;)
+ (func $br-on_non_null-2 (type $void_func)
(drop
(block
(call $log
@@ -192,7 +192,7 @@
)
)
)
- (func $cast-on-func (type $void_func) (; has Stack IR ;)
+ (func $cast-on-func (type $void_func)
(call $log
(i32.const 0)
)
@@ -204,7 +204,7 @@
)
(unreachable)
)
- (func $init-array-packed (type $int_func) (; has Stack IR ;) (result i32)
+ (func $init-array-packed (type $int_func) (result i32)
(array.get_u $bytes
(array.new $bytes
(i32.const -43)
@@ -213,7 +213,7 @@
(i32.const 10)
)
)
- (func $array-copy (type $void_func) (; has Stack IR ;)
+ (func $array-copy (type $void_func)
(local $0 (ref $bytes))
(local $1 (ref $bytes))
(array.set $bytes
@@ -268,7 +268,7 @@
)
)
)
- (func $array.new_fixed (type $void_func) (; has Stack IR ;)
+ (func $array.new_fixed (type $void_func)
(local $0 i32)
(local $1 i32)
(local.set $0
@@ -287,12 +287,12 @@
(i32.const 50)
)
)
- (func $array.new_fixed-packed (type $void_func) (; has Stack IR ;)
+ (func $array.new_fixed-packed (type $void_func)
(call $log
(i32.const 8)
)
)
- (func $static-casts (type $void_func) (; has Stack IR ;)
+ (func $static-casts (type $void_func)
(call $log
(i32.const 1)
)
@@ -312,7 +312,7 @@
(i32.const 1)
)
)
- (func $static-br_on_cast_fail (type $void_func) (; has Stack IR ;)
+ (func $static-br_on_cast_fail (type $void_func)
(call $log
(i32.const -2)
)
@@ -380,7 +380,7 @@ ignoring comparison of ExecutionResults!
(module
(type $0 (func (result i32)))
(export "foo" (func $foo))
- (func $foo (type $0) (; has Stack IR ;) (result i32)
+ (func $foo (type $0) (result i32)
(i32.const 0)
)
)
diff --git a/test/passes/converge_O3_metrics.bin.txt b/test/passes/converge_O3_metrics.bin.txt
index bd977b342..312e67fe7 100644
--- a/test/passes/converge_O3_metrics.bin.txt
+++ b/test/passes/converge_O3_metrics.bin.txt
@@ -46,13 +46,13 @@ total
(elem $0 (i32.const 0) $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $___stdout_write $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE6xsputnEPKci $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE8overflowEi)
(export "_main" (func $_main))
(export "_malloc" (func $_malloc))
- (func $b0 (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
+ (func $b0 (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
(i32.const 0)
)
- (func $_malloc (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $_malloc (param $0 i32) (result i32)
(i32.const 0)
)
- (func $_main (; has Stack IR ;) (result i32)
+ (func $_main (result i32)
(local $0 i32)
(local $1 i32)
(local.set $0
@@ -157,7 +157,7 @@ total
)
(i32.const 0)
)
- (func $___stdout_write (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $___stdout_write (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(global.set $global$0
(i32.const 32)
)
@@ -187,7 +187,7 @@ total
)
(i32.const 1)
)
- (func $__ZNSt3__211__stdoutbufIcE8overflowEi (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE8overflowEi (param $0 i32) (param $1 i32) (result i32)
(i32.store8
(i32.const 0)
(local.get $1)
@@ -212,7 +212,7 @@ total
)
(i32.const 0)
)
- (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(drop
(call_indirect (type $0)
(i32.const 0)
@@ -277,13 +277,13 @@ total
(elem $0 (i32.const 0) $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $___stdout_write $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE6xsputnEPKci $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE8overflowEi)
(export "_main" (func $_main))
(export "_malloc" (func $_malloc))
- (func $b0 (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
+ (func $b0 (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
(i32.const 0)
)
- (func $_malloc (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $_malloc (param $0 i32) (result i32)
(i32.const 0)
)
- (func $_main (; has Stack IR ;) (result i32)
+ (func $_main (result i32)
(local $0 i32)
(local $1 i32)
(local.set $0
@@ -388,7 +388,7 @@ total
)
(i32.const 0)
)
- (func $___stdout_write (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $___stdout_write (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(i32.store
(i32.const 8)
(local.get $1)
@@ -413,7 +413,7 @@ total
)
(i32.const 1)
)
- (func $__ZNSt3__211__stdoutbufIcE8overflowEi (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE8overflowEi (param $0 i32) (param $1 i32) (result i32)
(i32.store8
(i32.const 0)
(local.get $1)
@@ -438,7 +438,7 @@ total
)
(i32.const 0)
)
- (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(drop
(call_indirect (type $0)
(i32.const 0)
@@ -503,13 +503,13 @@ total
(elem $0 (i32.const 0) $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $___stdout_write $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE6xsputnEPKci $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $b0 $__ZNSt3__211__stdoutbufIcE8overflowEi)
(export "_main" (func $_main))
(export "_malloc" (func $_malloc))
- (func $b0 (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
+ (func $b0 (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (result i32)
(i32.const 0)
)
- (func $_malloc (; has Stack IR ;) (param $0 i32) (result i32)
+ (func $_malloc (param $0 i32) (result i32)
(i32.const 0)
)
- (func $_main (; has Stack IR ;) (result i32)
+ (func $_main (result i32)
(local $0 i32)
(local $1 i32)
(local.set $0
@@ -614,7 +614,7 @@ total
)
(i32.const 0)
)
- (func $___stdout_write (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $___stdout_write (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(i32.store
(i32.const 8)
(local.get $1)
@@ -639,7 +639,7 @@ total
)
(i32.const 1)
)
- (func $__ZNSt3__211__stdoutbufIcE8overflowEi (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE8overflowEi (param $0 i32) (param $1 i32) (result i32)
(i32.store8
(i32.const 0)
(local.get $1)
@@ -664,7 +664,7 @@ total
)
(i32.const 0)
)
- (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (func $__ZNSt3__211__stdoutbufIcE6xsputnEPKci (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(drop
(call_indirect (type $0)
(i32.const 0)
diff --git a/test/passes/fuzz-exec_O.txt b/test/passes/fuzz-exec_O.txt
index 7bdf8d252..d636cc765 100644
--- a/test/passes/fuzz-exec_O.txt
+++ b/test/passes/fuzz-exec_O.txt
@@ -8,7 +8,7 @@
(memory $0 1 1)
(export "func_0" (func $func_0))
(export "func_1" (func $func_1))
- (func $func_0 (; has Stack IR ;) (result i64)
+ (func $func_0 (result i64)
(block $label$0 (result i64)
(br_if $label$0
(i64.const 1234)
@@ -18,7 +18,7 @@
)
)
)
- (func $func_1 (; has Stack IR ;) (result i32)
+ (func $func_1 (result i32)
(i32.load16_s offset=22 align=1
(i32.const -1)
)
@@ -59,7 +59,7 @@
(export "add4" (func $div))
(export "sub1" (func $div))
(export "sub2" (func $div))
- (func $div (; has Stack IR ;) (result f32)
+ (func $div (result f32)
(f32.const nan:0x400000)
)
)
diff --git a/test/passes/remove-unused-brs_generate-stack-ir_print-stack-ir.txt b/test/passes/remove-unused-brs_generate-stack-ir_print-stack-ir.txt
index c9cacca39..2eb5aa7da 100644
--- a/test/passes/remove-unused-brs_generate-stack-ir_print-stack-ir.txt
+++ b/test/passes/remove-unused-brs_generate-stack-ir_print-stack-ir.txt
@@ -1,23 +1,6 @@
(module
(type $0 (func (param i64)))
(func $0 (param $var$0 i64)
- block $label$1
- block $label$2
- loop $label$3
- block $label$4
- unreachable
- end
- unreachable
- end
- unreachable
- end
- unreachable
- end
- )
-)
-(module
- (type $0 (func (param i64)))
- (func $0 (; has Stack IR ;) (param $var$0 i64)
(block $label$1
(br_if $label$1
(block $label$2
@@ -34,3 +17,20 @@
)
)
)
+(module
+ (type $0 (func (param i64)))
+ (func $0 (param $var$0 i64)
+ block $label$1
+ block $label$2
+ loop $label$3
+ block $label$4
+ unreachable
+ end
+ unreachable
+ end
+ unreachable
+ end
+ unreachable
+ end
+ )
+)
diff --git a/test/unit/test_stack_ir.py b/test/unit/test_stack_ir.py
index 78086d0cc..191d7eaec 100644
--- a/test/unit/test_stack_ir.py
+++ b/test/unit/test_stack_ir.py
@@ -7,13 +7,16 @@ class StackIRTest(utils.BinaryenTestCase):
# test that stack IR opts make a difference.
def test_stack_ir_opts(self):
path = self.input_path('stack_ir.wat')
- opt = shared.run_process(shared.WASM_OPT + [path, '-O', '--generate-stack-ir', '--optimize-stack-ir', '--print-stack-ir', '-o', 'a.wasm'], capture_output=True).stdout
- nonopt = shared.run_process(shared.WASM_OPT + [path, '-O', '--generate-stack-ir', '--print-stack-ir', '-o', 'b.wasm'], capture_output=True).stdout
+ shared_args = shared.WASM_OPT + [path, '--print-stack-ir']
+ # optimize level 2 will only run some of the stack IR opts. level 3 does
+ # noticeably more.
+ nonopt = shared.run_process(shared_args + ['-o', 'nonopt.wasm', '--optimize-level=2'], capture_output=True).stdout
+ yesopt = shared.run_process(shared_args + ['-o', 'yesopt.wasm', '--optimize-level=3'], capture_output=True).stdout
# see a difference in the printed stack IR (the optimizations let us
# remove a pair of local.set/get)
- self.assertNotEqual(opt, nonopt)
- self.assertLess(len(opt), len(nonopt))
+ self.assertNotEqual(yesopt, nonopt)
+ self.assertLess(len(yesopt), len(nonopt))
# see a difference in the actual emitted wasm binary.
- opt_size = os.path.getsize('a.wasm')
- nonopt_size = os.path.getsize('b.wasm')
- self.assertLess(opt_size, nonopt_size)
+ yesopt_size = os.path.getsize('yesopt.wasm')
+ nonopt_size = os.path.getsize('nonopt.wasm')
+ self.assertLess(yesopt_size, nonopt_size)