diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 36 | ||||
-rw-r--r-- | src/tools/translate-to-fuzz.h | 18 | ||||
-rw-r--r-- | src/tools/wasm-opt.cpp | 69 | ||||
-rw-r--r-- | src/wasm-builder.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 4 |
5 files changed, 105 insertions, 26 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 8994c7fe2..e627ce138 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -31,6 +31,8 @@ namespace wasm { // condition and possible value, and the possible value must // not have side effects (as they would run unconditionally) static bool canTurnIfIntoBrIf(Expression* ifCondition, Expression* brValue, PassOptions& options) { + // if the if isn't even taken, this is all dead code anyhow + if (ifCondition->type == unreachable) return false; if (!brValue) return true; EffectAnalyzer value(options, brValue); if (value.hasSideEffects()) return false; @@ -72,7 +74,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { flows.push_back(currp); self->valueCanFlow = true; // start optimistic } else { - self->valueCanFlow = false; + self->stopValueFlow(); } } else if (curr->is<Return>()) { flows.clear(); @@ -80,6 +82,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { self->valueCanFlow = true; // start optimistic } else if (curr->is<If>()) { auto* iff = curr->cast<If>(); + if (iff->condition->type == unreachable) { + // avoid trying to optimize this, we never reach it anyhow + self->stopFlow(); + return; + } if (iff->ifFalse) { assert(self->ifStack.size() > 0); for (auto* flow : self->ifStack.back()) { @@ -88,7 +95,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { self->ifStack.pop_back(); } else { // if without else stops the flow of values - self->valueCanFlow = false; + self->stopValueFlow(); } } else if (curr->is<Block>()) { // any breaks flowing to here are unnecessary, as we get here anyhow @@ -126,16 +133,31 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } } else if (curr->is<Nop>()) { // ignore (could be result of a previous cycle) - self->valueCanFlow = false; + self->stopValueFlow(); } else if (curr->is<Loop>()) { // do nothing - it's ok for values to flow out } else { // anything else stops the flow - flows.clear(); - self->valueCanFlow = false; + self->stopFlow(); } } + void stopFlow() { + flows.clear(); + valueCanFlow = false; + } + + void stopValueFlow() { + flows.erase(std::remove_if(flows.begin(), flows.end(), [&](Expression** currp) { + auto* curr = *currp; + if (auto* ret = curr->dynCast<Return>()) { + return ret->value; + } + return curr->cast<Break>()->value; + }), flows.end()); + valueCanFlow = false; + } + static void clear(RemoveUnusedBrs* self, Expression** currp) { self->flows.clear(); } @@ -171,6 +193,10 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { auto* iff = (*currp)->dynCast<If>(); if (iff) { + if (iff->condition->type == unreachable) { + // avoid trying to optimize this, we never reach it anyhow + return; + } self->pushTask(doVisitIf, currp); if (iff->ifFalse) { // we need to join up if-else control flow, and clear after the condition diff --git a/src/tools/translate-to-fuzz.h b/src/tools/translate-to-fuzz.h index a7f86af13..307604af6 100644 --- a/src/tools/translate-to-fuzz.h +++ b/src/tools/translate-to-fuzz.h @@ -87,7 +87,7 @@ private: // the number of runtime iterations (function calls, loop backbranches) we // allow before we stop execution with a trap, to prevent hangs. 0 means // no hang protection. - static const int HANG_LIMIT = 25; + static const int HANG_LIMIT = 100; // Optionally remove NaNs, which are a source of nondeterminism (which makes // cross-VM comparisons harder) @@ -452,14 +452,20 @@ private: // make something with no chance of infinite recursion Expression* makeTrivial(WasmType type) { if (isConcreteWasmType(type)) { - return makeConst(type); + if (oneIn(2)) { + return makeGetLocal(type); + } else { + return makeConst(type); + } } else if (type == none) { return makeNop(type); } assert(type == unreachable); - return builder.makeReturn( - isConcreteWasmType(func->result) ? makeConst(func->result) : nullptr - ); + Expression* ret = nullptr; + if (isConcreteWasmType(func->result)) { + ret = makeTrivial(func->result); + } + return builder.makeReturn(ret); } // specific expression creators @@ -650,7 +656,7 @@ private: Expression* makeGetLocal(WasmType type) { auto& locals = typeLocals[type]; - if (locals.empty()) return makeTrivial(type); + if (locals.empty()) return makeConst(type); return builder.makeGetLocal(vectorPick(locals), type); } diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp index c34331366..a66bc612f 100644 --- a/src/tools/wasm-opt.cpp +++ b/src/tools/wasm-opt.cpp @@ -39,6 +39,23 @@ using namespace wasm; +// runs a command and returns its output TODO: portability, return code checking +std::string runCommand(std::string command) { +#ifdef __linux__ + std::string output; + const int MAX_BUFFER = 1024; + char buffer[MAX_BUFFER]; + FILE *stream = popen(command.c_str(), "r"); + while (fgets(buffer, MAX_BUFFER, stream) != NULL) { + output.append(buffer); + } + pclose(stream); + return output; +#else + Fatal() << "TODO: portability for wasm-opt runCommand"; +#endif +} + // // main // @@ -50,6 +67,7 @@ int main(int argc, const char* argv[]) { bool debugInfo = false; bool fuzzExec = false; bool fuzzBinary = false; + std::string extraFuzzCommand; bool translateToFuzz = false; std::string emitJSWrapper; std::string emitSpecWrapper; @@ -74,6 +92,9 @@ int main(int argc, const char* argv[]) { .add("--fuzz-binary", "-fb", "Convert to binary and back after optimizations and before fuzz-exec, helping fuzzing find binary format bugs", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { fuzzBinary = true; }) + .add("--extra-fuzz-command", "-efc", "An extra command to run on the output before and after optimizing. The output is compared between the two, and an error occurs if they are not equal", + Options::Arguments::One, + [&](Options *o, const std::string &arguments) { extraFuzzCommand = arguments; }) .add("--translate-to-fuzz", "-ttf", "Translate the input into a valid wasm module *somehow*, useful for fuzzing", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { translateToFuzz = true; }) @@ -123,6 +144,33 @@ int main(int argc, const char* argv[]) { results.get(wasm); } + if (emitJSWrapper.size() > 0) { + std::ofstream outfile; + outfile.open(emitJSWrapper, std::ofstream::out); + outfile << generateJSWrapper(wasm); + outfile.close(); + } + + if (emitSpecWrapper.size() > 0) { + std::ofstream outfile; + outfile.open(emitSpecWrapper, std::ofstream::out); + outfile << generateSpecWrapper(wasm); + outfile.close(); + } + + std::string firstOutput; + + if (extraFuzzCommand.size() > 0 && options.extra.count("output") > 0) { + if (options.debug) std::cerr << "writing binary before opts, for extra fuzz command..." << std::endl; + ModuleWriter writer; + writer.setDebug(options.debug); + writer.setBinary(emitBinary); + writer.setDebugInfo(debugInfo); + writer.write(wasm, options.extra["output"]); + firstOutput = runCommand(extraFuzzCommand); + std::cout << "[extra-fuzz-command first output:]\n" << firstOutput << '\n'; + } + if (options.runningPasses()) { if (options.debug) std::cerr << "running passes...\n"; PassRunner passRunner = options.getPassRunner(wasm); @@ -155,19 +203,14 @@ int main(int argc, const char* argv[]) { writer.setBinary(emitBinary); writer.setDebugInfo(debugInfo); writer.write(wasm, options.extra["output"]); - } - - if (emitJSWrapper.size() > 0) { - std::ofstream outfile; - outfile.open(emitJSWrapper, std::ofstream::out); - outfile << generateJSWrapper(wasm); - outfile.close(); - } - if (emitSpecWrapper.size() > 0) { - std::ofstream outfile; - outfile.open(emitSpecWrapper, std::ofstream::out); - outfile << generateSpecWrapper(wasm); - outfile.close(); + if (extraFuzzCommand.size() > 0) { + auto secondOutput = runCommand(extraFuzzCommand); + std::cout << "[extra-fuzz-command second output:]\n" << firstOutput << '\n'; + if (firstOutput != secondOutput) { + std::cerr << "extra fuzz command output differs\n"; + abort(); + } + } } } diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 4ef4b1e0b..7432562d1 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -164,14 +164,14 @@ public: auto* ret = allocator.alloc<SetLocal>(); ret->index = index; ret->value = value; - ret->type = none; + ret->finalize(); return ret; } SetLocal* makeTeeLocal(Index index, Expression* value) { auto* ret = allocator.alloc<SetLocal>(); ret->index = index; ret->value = value; - ret->type = value->type; + ret->setTee(true); return ret; } GetGlobal* makeGetGlobal(Name name, WasmType type) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index f82a06e68..ab2db6bd5 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -332,6 +332,10 @@ void SetLocal::setTee(bool is) { void SetLocal::finalize() { if (value->type == unreachable) { type = unreachable; + } else if (isTee()) { + type = value->type; + } else { + type = none; } } |