From a5d33e7faaa965565f9ca1d0c23c3077389f2389 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 8 May 2018 07:57:49 -0700 Subject: More reducer improvements (#1533) * Add a helper class to iterate over all a node's children, and use that when attempting to replace a node with its children. * If a child has a different type than the parent, try to replace the parent with a conversion + the child (for example, a call may receive two f32 inputs and return an i32; we can try to replace the call with one of those f32s and a conversion to an i32). * When possible, try to replace the function body with a child even if the child has a different type, by changing the function return value. --- src/tools/wasm-reduce.cpp | 137 +++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 45 deletions(-) (limited to 'src/tools/wasm-reduce.cpp') diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index 7b7ff9da3..0dfb97907 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -35,7 +35,9 @@ #include "wasm-io.h" #include "wasm-builder.h" #include "ir/branch-utils.h" +#include "ir/iteration.h" #include "ir/literal-utils.h" +#include "ir/properties.h" #include "wasm-validator.h" #ifdef _WIN32 #ifndef NOMINMAX @@ -403,8 +405,9 @@ struct Reducer : public WalkerPasstype == none) { - if (tryToReduceCurrentToNone()) return; + if (tryToReduceCurrentToNop()) return; } else if (isConcreteType(curr->type)) { if (tryToReduceCurrentToConst()) return; } else { @@ -419,8 +422,6 @@ struct Reducer : public WalkerPassifTrue)) return; - if (iff->ifFalse && tryToReplaceCurrent(iff->ifFalse)) return; handleCondition(iff->condition); } else if (auto* br = curr->dynCast()) { handleCondition(br->condition); @@ -428,26 +429,6 @@ struct Reducer : public WalkerPasscondition); } else if (auto* sw = curr->dynCast()) { handleCondition(sw->condition); - } else if (auto* set = curr->dynCast()) { - if (set->isTee()) { - // maybe we don't need the set - tryToReplaceCurrent(set->value); - } - } else if (auto* unary = curr->dynCast()) { - // maybe we can pass through - tryToReplaceCurrent(unary->value); - } else if (auto* binary = curr->dynCast()) { - // maybe we can pass through - if (!tryToReplaceCurrent(binary->left)) { - tryToReplaceCurrent(binary->right); - } - } else if (auto* call = curr->dynCast()) { - handleCall(call); - } else if (auto* call = curr->dynCast()) { - handleCall(call); - } else if (auto* call = curr->dynCast()) { - if (tryToReplaceCurrent(call->target)) return; - handleCall(call); } else if (auto* block = curr->dynCast()) { if (!shouldTryToReduce()) return; // replace a singleton @@ -479,24 +460,66 @@ struct Reducer : public WalkerPassdynCast()) { if (shouldTryToReduce() && !BranchUtils::BranchSeeker::hasNamed(loop, loop->name)) { tryToReplaceCurrent(loop->body); } - } else if (auto* rmw = curr->dynCast()) { - if (tryToReplaceCurrent(rmw->ptr)) return; - if (tryToReplaceCurrent(rmw->value)) return; - } else if (auto* cmpx = curr->dynCast()) { - if (tryToReplaceCurrent(cmpx->ptr)) return; - if (tryToReplaceCurrent(cmpx->expected)) return; - if (tryToReplaceCurrent(cmpx->replacement)) return; - } else if (auto* wait = curr->dynCast()) { - if (tryToReplaceCurrent(wait->ptr)) return; - if (tryToReplaceCurrent(wait->expected)) return; - if (tryToReplaceCurrent(wait->timeout)) return; - } else if (auto* wake = curr->dynCast()) { - if (tryToReplaceCurrent(wake->ptr)) return; - if (tryToReplaceCurrent(wake->wakeCount)) return; + return; // nothing more to do + } + // Finally, try to replace with a child. + for (auto* child : ChildIterator(curr)) { + if (tryToReplaceCurrent(child)) return; + } + // If that didn't work, try to replace with a child + a unary conversion + if (isConcreteType(curr->type) && + !curr->is()) { // but not if it's already unary + for (auto* child : ChildIterator(curr)) { + if (child->type == curr->type) continue; // already tried + if (!isConcreteType(child->type)) continue; // no conversion + Expression* fixed; + switch (curr->type) { + case i32: { + switch (child->type) { + case i64: fixed = builder->makeUnary(WrapInt64, child); break; + case f32: fixed = builder->makeUnary(TruncSFloat32ToInt32, child); break; + case f64: fixed = builder->makeUnary(TruncSFloat64ToInt32, child); break; + default: WASM_UNREACHABLE(); + } + break; + } + case i64: { + switch (child->type) { + case i32: fixed = builder->makeUnary(ExtendSInt32, child); break; + case f32: fixed = builder->makeUnary(TruncSFloat32ToInt64, child); break; + case f64: fixed = builder->makeUnary(TruncSFloat64ToInt64, child); break; + default: WASM_UNREACHABLE(); + } + break; + } + case f32: { + switch (child->type) { + case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat32, child); break; + case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat32, child); break; + case f64: fixed = builder->makeUnary(DemoteFloat64, child); break; + default: WASM_UNREACHABLE(); + } + break; + } + case f64: { + switch (child->type) { + case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat64, child); break; + case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat64, child); break; + case f32: fixed = builder->makeUnary(PromoteFloat32, child); break; + default: WASM_UNREACHABLE(); + } + break; + } + default: WASM_UNREACHABLE(); + } + assert(fixed->type == curr->type); + if (tryToReplaceCurrent(fixed)) return; + } } } @@ -649,6 +672,37 @@ struct Reducer : public WalkerPassfunctions.size() == 1 && module->exports.empty() && module->table.segments.empty()) { + auto* func = module->functions[0].get(); + // We can't remove something that might have breaks to it. + if (!Properties::isNamedControlFlow(func->body)) { + auto funcType = func->type; + auto funcResult = func->result; + auto* funcBody = func->body; + for (auto* child : ChildIterator(func->body)) { + if (!(isConcreteType(child->type) || child->type == none)) { + continue; // not something a function can return + } + // Try to replace the body with the child, fixing up the function + // to accept it. + func->type = Name(); + func->result = child->type; + func->body = child; + if (writeAndTestReduction()) { + // great, we succeeded! + std::cerr << "| altered function result type\n"; + noteReduction(1); + break; + } + // Undo. + func->type = funcType; + func->result = funcResult; + func->body = funcBody; + } + } + } } bool tryToRemoveFunctions(std::vector names) { @@ -729,14 +783,7 @@ struct Reducer : public WalkerPass - void handleCall(T* call) { - for (auto* op : call->operands) { - if (tryToReplaceCurrent(op)) return; - } - } - - bool tryToReduceCurrentToNone() { + bool tryToReduceCurrentToNop() { auto* curr = getCurrent(); if (curr->is()) return false; // try to replace with a trivial value -- cgit v1.2.3