summaryrefslogtreecommitdiff
path: root/src/passes/SimplifyLocals.cpp
blob: 5368701d7d1fd69aa89be7af43171d7817b7d49e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 * Copyright 2015 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.
 */

//
// Locals-related optimizations
//
// This "sinks" set_locals, pushing them to the next get_local where possible

#include <wasm.h>
#include <wasm-traversal.h>
#include <pass.h>
#include <ast_utils.h>

namespace wasm {

struct SimplifyLocals : public WalkerPass<FastExecutionWalker<SimplifyLocals>> {
  struct SinkableInfo {
    Expression** item;
    EffectAnalyzer effects;

    SinkableInfo(Expression** item) : item(item) {
      effects.walk(*item);
    }
  };

  // locals in current linear execution trace, which we try to sink
  std::map<Name, SinkableInfo> sinkables;

  void noteNonLinear() {
    sinkables.clear();
  }

  void visitBlock(Block *curr) {
    // note locals, we can sink them from here TODO sink from elsewhere?
    derecurseBlocks(curr, [&](Block* block) {}, [&](Block* block, Expression*& child) {
      walk(child);
      if (child->is<SetLocal>()) {
        Name name = child->cast<SetLocal>()->name;
        assert(sinkables.count(name) == 0);
        sinkables.emplace(std::make_pair(name, SinkableInfo(&child)));
      }
    }, [&](Block* block) {});
  }

  void visitGetLocal(GetLocal *curr) {
    auto found = sinkables.find(curr->name);
    if (found != sinkables.end()) {
      // sink it, and nop the origin TODO: clean up nops
      replaceCurrent(*found->second.item);
      // reuse the getlocal that is dying
      *found->second.item = curr;
      ExpressionManipulator::nop(curr);
      sinkables.erase(found);
    }
  }

  void visitSetLocal(SetLocal *curr) {
    walk(curr->value);
    // if we are a potentially-sinkable thing, forget it - this
    // write overrides the last TODO: optimizable
    // TODO: if no get_locals left, can remove the set as well (== expressionizer in emscripten optimizer)
    auto found = sinkables.find(curr->name);
    if (found != sinkables.end()) {
      sinkables.erase(found);
    }
  }

  void checkInvalidations(EffectAnalyzer& effects) {
    // TODO: this is O(bad)
    std::vector<Name> invalidated;
    for (auto& sinkable : sinkables) {
      if (effects.invalidates(sinkable.second.effects)) {
        invalidated.push_back(sinkable.first);
      }
    }
    for (auto name : invalidated) {
      sinkables.erase(name);
    }
  }

  void checkPre(Expression* curr) {
    EffectAnalyzer effects;
    if (effects.checkPre(curr)) {
      checkInvalidations(effects);
    }
  }

  void checkPost(Expression* curr) {
    EffectAnalyzer effects;
    if (effects.checkPost(curr)) {
      checkInvalidations(effects);
    }
  }

  void walk(Expression*& curr) override {
    if (!curr) return;

    checkPre(curr);

    FastExecutionWalker::walk(curr);

    checkPost(curr);
  }
};

static RegisterPass<SimplifyLocals> registerPass("simplify-locals", "miscellaneous locals-related optimizations");

} // namespace wasm