void visitBlock(Block *curr) { // compress out nops and other dead code int skip = 0; auto& list = curr->list; size_t size = list.size(); bool needResize = false; for (size_t z = 0; z < size; z++) { auto* optimized = optimize(list[z], z == size - 1 && isConcreteWasmType(curr->type)); if (!optimized) { skip++; needResize = true; } else { if (optimized != list[z]) { list[z] = optimized; } if (skip > 0) { list[z - skip] = list[z]; } // if this is an unconditional br, the rest is dead code Break* br = list[z - skip]->dynCast<Break>(); Switch* sw = list[z - skip]->dynCast<Switch>(); if ((br && !br->condition) || sw) { auto* last = list.back(); list.resize(z - skip + 1); // if we removed the last one, and it was a return value, it must be returned if (list.back() != last && isConcreteWasmType(last->type)) { list.push_back(last); } needResize = false; break; } } } if (needResize) { list.resize(size - skip); } if (!curr->name.is()) { if (list.size() == 1) { // just one element. replace the block, either with it or with a nop if it's not needed if (isConcreteWasmType(curr->type) || EffectAnalyzer(getPassOptions(), list[0]).hasSideEffects()) { replaceCurrent(list[0]); } else { if (curr->type == unreachable) { ExpressionManipulator::convert<Block, Unreachable>(curr); } else { ExpressionManipulator::nop(curr); } } } else if (list.size() == 0) { ExpressionManipulator::nop(curr); } } }
void visitExpression(Expression* curr) { if (curr->is<Const>()) return; // try to evaluate this into a const Flow flow; try { flow = StandaloneExpressionRunner().visit(curr); } catch (StandaloneExpressionRunner::NonstandaloneException& e) { return; } if (flow.breaking()) return; // TODO: can create a break as a replacement in some cases (not NONSTANDALONE) if (isConcreteWasmType(flow.value.type)) { replaceCurrent(Builder(*getModule()).makeConst(flow.value)); } }
void visitDrop(Drop* curr) { // optimize the dropped value, maybe leaving nothing curr->value = optimize(curr->value, false); if (curr->value == nullptr) { ExpressionManipulator::nop(curr); return; } // a drop of a tee is a set if (auto* set = curr->value->dynCast<SetLocal>()) { assert(set->isTee()); set->setTee(false); replaceCurrent(set); return; } // if we are dropping a block's return value, we might be able to remove it entirely if (auto* block = curr->value->dynCast<Block>()) { auto* last = block->list.back(); if (isConcreteWasmType(last->type)) { assert(block->type == last->type); last = optimize(last, false); if (!last) { // we may be able to remove this, if there are no brs bool canPop = true; if (block->name.is()) { BreakSeeker breakSeeker(block->name); Expression* temp = block; breakSeeker.walk(temp); if (breakSeeker.found && breakSeeker.valueType != none) { canPop = false; } } if (canPop) { block->list.back() = last; block->list.pop_back(); block->type = none; // we don't need the drop anymore, let's see what we have left in the block if (block->list.size() > 1) { replaceCurrent(block); } else if (block->list.size() == 1) { replaceCurrent(block->list[0]); } else { ExpressionManipulator::nop(curr); } return; } } } } // sink a drop into an arm of an if-else if the other arm ends in an unreachable, as it if is a branch, this can make that branch optimizable and more vaccuming possible auto* iff = curr->value->dynCast<If>(); if (iff && iff->ifFalse && isConcreteWasmType(iff->type)) { // reuse the drop in both cases if (iff->ifTrue->type == unreachable) { assert(isConcreteWasmType(iff->ifFalse->type)); curr->value = iff->ifFalse; iff->ifFalse = curr; iff->type = none; replaceCurrent(iff); } else if (iff->ifFalse->type == unreachable) { assert(isConcreteWasmType(iff->ifTrue->type)); curr->value = iff->ifTrue; iff->ifTrue = curr; iff->type = none; replaceCurrent(iff); } } }