Пример #1
0
 void visitFunction(Function* curr) {
   auto* optimized = optimize(curr->body, curr->result != none);
   if (optimized) {
     curr->body = optimized;
   } else {
     ExpressionManipulator::nop(curr->body);
   }
   if (curr->result == none && !EffectAnalyzer(getPassOptions(), curr->body).hasSideEffects()) {
     ExpressionManipulator::nop(curr->body);
   }
 }
Пример #2
0
 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);
     }
   }
 }
Пример #3
0
 void visitLoop(Loop* loop) {
   // We accumulate all the code we can move out, and will place it
   // in a block just preceding the loop.
   std::vector<Expression*> movedCode;
   // Accumulate effects of things we can't move out - things
   // we move out later must cross them, so we must verify it
   // is ok to do so.
   EffectAnalyzer effectsSoFar(getPassOptions());
   // The loop's total effects also matter. For example, a store
   // in the loop means we can't move a load outside.
   // FIXME: we look at the loop "tail" area too, after the last
   //        possible branch back, which can cause false positives
   //        for bad effect interactions.
   EffectAnalyzer loopEffects(getPassOptions(), loop);
   // Note all the sets in each loop, and how many per index. Currently
   // EffectAnalyzer can't do that, and we need it to know if we
   // can move a set out of the loop (if there is another set
   // still there, we can't). Another possible option here is for
   // LocalGraph to track interfering sets. TODO
   // FIXME: also the loop tail issue from above.
   auto numLocals = getFunction()->getNumLocals();
   std::vector<Index> numSetsForIndex(numLocals);
   std::fill(numSetsForIndex.begin(), numSetsForIndex.end(), 0);
   LoopSets loopSets;
   {
     FindAll<SetLocal> finder(loop);
     for (auto* set : finder.list) {
       numSetsForIndex[set->index]++;
       loopSets.insert(set);
     }
   }
   // Walk along the loop entrance, while all the code there
   // is executed unconditionally. That is the code we want to
   // move out - anything that might or might not be executed
   // may be best left alone anyhow.
   std::vector<Expression**> work;
   work.push_back(&loop->body);
   while (!work.empty()) {
     auto** currp = work.back();
     work.pop_back();
     auto* curr = *currp;
     // Look into blocks.
     if (auto* block = curr->dynCast<Block>()) {
       auto& list = block->list;
       Index i = list.size();
       while (i > 0) {
         i--;
         work.push_back(&list[i]);
       }
       continue;
       // Note that if the block had a merge at the end, we would have seen
       // a branch to it anyhow, so we would stop before that point anyhow.
     }
     // If this may branch, we are done.
     EffectAnalyzer effects(getPassOptions(), curr);
     if (effects.branches) {
       break;
     }
     if (interestingToMove(curr)) {
       // Let's see if we can move this out.
       // Global side effects would prevent this - we might end up
       // executing them just once.
       // And we must also move across anything not moved out already,
       // so check for issues there too.
       // The rest of the loop's effects matter too, we must also
       // take into account global state like interacting loads and
       // stores.
       bool unsafeToMove = effects.hasGlobalSideEffects() ||
                           effectsSoFar.invalidates(effects) ||
                           (effects.noticesGlobalSideEffects() &&
                            loopEffects.hasGlobalSideEffects());
       if (!unsafeToMove) {
         // So far so good. Check if our local dependencies are all
         // outside of the loop, in which case everything is good -
         // either they are before the loop and constant for us, or
         // they are after and don't matter.
         if (effects.localsRead.empty() || !hasGetDependingOnLoopSet(curr, loopSets)) {
           // We have checked if our gets are influenced by sets in the loop, and
           // must also check if our sets interfere with them. To do so, assume
           // temporarily that we are moving curr out; see if any sets remain for
           // its indexes.
           FindAll<SetLocal> currSets(curr);
           for (auto* set : currSets.list) {
             assert(numSetsForIndex[set->index] > 0);
             numSetsForIndex[set->index]--;
           }
           bool canMove = true;
           for (auto* set : currSets.list) {
             if (numSetsForIndex[set->index] > 0) {
               canMove = false;
               break;
             }
           }
           if (!canMove) {
             // We failed to move the code, undo those changes.
             for (auto* set : currSets.list) {
               numSetsForIndex[set->index]++;
             }
           } else {
             // We can move it! Leave the changes, move the code, and update
             // loopSets.
             movedCode.push_back(curr);
             *currp = Builder(*getModule()).makeNop();
             for (auto* set : currSets.list) {
               loopSets.erase(set);
             }
             continue;
           }
         }
       }
     }
     // We did not move this item. Accumulate its effects.
     effectsSoFar.mergeIn(effects);
   }
   // If we moved the code out, finish up by emitting it
   // outside of the loop.
   // Note that this works with nested loops - after moving outside
   // of an inner loop, we can encounter it again in an outer loop,
   // and move it further outside, without requiring any extra pass.
   if (!movedCode.empty()) {
     // Finish the moving by emitting the code outside.
     Builder builder(*getModule());
     auto* ret = builder.makeBlock(movedCode);
     ret->list.push_back(loop);
     ret->finalize(loop->type);
     replaceCurrent(ret);
     // Note that we do not need to modify the localGraph - we keep
     // each get in a position to be influenced by exactly the same
     // sets as before.
   }
 }
Пример #4
0
  // returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced
  Expression* optimize(Expression* curr, bool resultUsed) {
    while (1) {
      switch (curr->_id) {
        case Expression::Id::NopId: return nullptr; // never needed

        case Expression::Id::BlockId: return curr; // not always needed, but handled in visitBlock()
        case Expression::Id::IfId: return curr; // not always needed, but handled in visitIf()
        case Expression::Id::LoopId: return curr; // not always needed, but handled in visitLoop()
        case Expression::Id::DropId: return curr; // not always needed, but handled in visitDrop()

        case Expression::Id::BreakId:
        case Expression::Id::SwitchId:
        case Expression::Id::CallId:
        case Expression::Id::CallImportId:
        case Expression::Id::CallIndirectId:
        case Expression::Id::SetLocalId:
        case Expression::Id::StoreId:
        case Expression::Id::ReturnId:
        case Expression::Id::SetGlobalId:
        case Expression::Id::HostId:
        case Expression::Id::UnreachableId: return curr; // always needed

        case Expression::Id::LoadId: {
          if (!resultUsed) {
            return curr->cast<Load>()->ptr;
          }
          return curr;
        }
        case Expression::Id::ConstId:
        case Expression::Id::GetLocalId:
        case Expression::Id::GetGlobalId: {
          if (!resultUsed) return nullptr;
          return curr;
        }

        case Expression::Id::UnaryId:
        case Expression::Id::BinaryId:
        case Expression::Id::SelectId: {
          if (resultUsed) {
            return curr; // used, keep it
          }
          // for unary, binary, and select, we need to check their arguments for side effects
          if (auto* unary = curr->dynCast<Unary>()) {
            if (EffectAnalyzer(getPassOptions(), unary->value).hasSideEffects()) {
              curr = unary->value;
              continue;
            } else {
              return nullptr;
            }
          } else if (auto* binary = curr->dynCast<Binary>()) {
            if (EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) {
              if (EffectAnalyzer(getPassOptions(), binary->right).hasSideEffects()) {
                return curr; // leave them
              } else {
                curr = binary->left;
                continue;
              }
            } else {
              if (EffectAnalyzer(getPassOptions(), binary->right).hasSideEffects()) {
                curr = binary->right;
                continue;
              } else {
                return nullptr;
              }
            }
          } else {
            // TODO: if two have side effects, we could replace the select with say an add?
            auto* select = curr->cast<Select>();
            if (EffectAnalyzer(getPassOptions(), select->ifTrue).hasSideEffects()) {
              if (EffectAnalyzer(getPassOptions(), select->ifFalse).hasSideEffects()) {
                return curr; // leave them
              } else {
                if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) {
                  return curr; // leave them
                } else {
                  curr = select->ifTrue;
                  continue;
                }
              }
            } else {
              if (EffectAnalyzer(getPassOptions(), select->ifFalse).hasSideEffects()) {
                if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) {
                  return curr; // leave them
                } else {
                  curr = select->ifFalse;
                  continue;
                }
              } else {
                if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) {
                  curr = select->condition;
                  continue;
                } else {
                  return nullptr;
                }
              }
            }
          }
        }

        default: WASM_UNREACHABLE();
      }
    }
  }