Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(419)

Side by Side Diff: src/compiler/store-store-elimination.cc

Issue 2087483003: [compiler] Introduce a simple store-store elimination, disabled by default. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@p1
Patch Set: Pull, with no conflicts Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/compiler/store-store-elimination.h"
6
7 #include "src/compiler/all-nodes.h"
8 #include "src/compiler/js-graph.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/simplified-operator.h"
11
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15
16 // A simple store-store elimination. When the effect chain contains the
17 // following sequence,
18 //
19 // - StoreField[[+off_1]](x1, y1)
20 // - StoreField[[+off_2]](x2, y2)
21 // - StoreField[[+off_3]](x3, y3)
22 // ...
23 // - StoreField[[+off_n]](xn, yn)
24 //
25 // where the xes are the objects and the ys are the values to be stored, then
26 // we are going to say that a store is superfluous if the same offset of the
27 // same object will be stored to in the future. If off_i == off_j and xi == xj
28 // and i < j, then we optimize the i'th StoreField away.
29 //
30 // This optimization should be initiated on the last StoreField in such a
31 // sequence.
32 //
33 // The algorithm works by walking the effect chain from the last StoreField
34 // upwards. While walking, we maintain a map {futureStore} from offsets to
35 // nodes; initially it is empty. As we walk the effect chain upwards, if
36 // futureStore[off] = n, then any store to node {n} with offset {off} is
37 // guaranteed to be useless because we do a full-width[1] store to that offset
38 // of that object in the near future anyway. For example, for this effect
39 // chain
40 //
41 // 71: StoreField(60, 0)
42 // 72: StoreField(65, 8)
43 // 73: StoreField(63, 8)
44 // 74: StoreField(65, 16)
45 // 75: StoreField(62, 8)
46 //
47 // just before we get to 72, we will have futureStore = {8: 63, 16: 65}.
48 //
49 // Here is the complete process.
50 //
51 // - We are at the end of a sequence of consecutive StoreFields.
52 // - We start out with futureStore = empty.
53 // - We then walk the effect chain upwards to find the next StoreField [2].
54 //
55 // 1. If the offset is not a key of {futureStore} yet, we put it in.
56 // 2. If the offset is a key of {futureStore}, but futureStore[offset] is a
57 // different node, we overwrite futureStore[offset] with the current node.
58 // 3. If the offset is a key of {futureStore} and futureStore[offset] equals
59 // this node, we eliminate this StoreField.
60 //
61 // As long as the current effect input points to a node with a single effect
62 // output, and as long as its opcode is StoreField, we keep traversing
63 // upwards.
64 //
65 // [1] This optimization is unsound if we optimize away a store to an offset
66 // because we store to the same offset in the future, even though the future
67 // store is narrower than the store we optimize away. Therefore, in case (1)
68 // and (2) we only add/overwrite to the dictionary when the field access has
69 // maximal size. For simplicity of implementation, we do not try to detect
70 // case (3).
71 //
72 // [2] We make sure that we only traverse the linear part, that is, the part
73 // where every node has exactly one incoming and one outgoing effect edge.
74 // Also, we only keep walking upwards as long as we keep finding consecutive
75 // StoreFields on the same node.
76
77 StoreStoreElimination::StoreStoreElimination(JSGraph* js_graph, Zone* temp_zone)
78 : jsgraph_(js_graph), temp_zone_(temp_zone) {}
79
80 StoreStoreElimination::~StoreStoreElimination() {}
81
82 void StoreStoreElimination::Run() {
83 // The store-store elimination performs work on chains of certain types of
84 // nodes. The elimination must be invoked on the lowest node in such a
85 // chain; we have a helper function IsEligibleNode that returns true
86 // precisely on the lowest node in such a chain.
87 //
88 // Because the elimination removes nodes from the graph, even remove nodes
89 // that the elimination was not invoked on, we cannot use a normal
90 // AdvancedReducer but we manually find which nodes to invoke the
91 // elimination on. Then in a next step, we invoke the elimination for each
92 // node that was eligible.
93
94 NodeVector eligible(temp_zone()); // loops over all nodes
95 AllNodes all(temp_zone(), jsgraph()->graph());
96
97 for (Node* node : all.live) {
98 if (IsEligibleNode(node)) {
99 eligible.push_back(node);
100 }
101 }
102
103 for (Node* node : eligible) {
104 ReduceEligibleNode(node);
105 }
106 }
107
108 namespace {
109
110 // 16 bits was chosen fairly arbitrarily; it seems enough now. 8 bits is too
111 // few.
112 typedef uint16_t Offset;
113
114 // To safely cast an offset from a FieldAccess, which has a wider range
115 // (namely int).
116 Offset ToOffset(int offset) {
117 CHECK(0 <= offset && offset < (1 << 8 * sizeof(Offset)));
118 return (Offset)offset;
119 }
120
121 Offset ToOffset(const FieldAccess& access) { return ToOffset(access.offset); }
122
123 // If node has a single effect use, return that node. If node has no or
124 // multiple effect uses, return nullptr.
125 Node* SingleEffectUse(Node* node) {
126 Node* last_use = nullptr;
127 for (Edge edge : node->use_edges()) {
128 if (!NodeProperties::IsEffectEdge(edge)) {
129 continue;
130 }
131 if (last_use != nullptr) {
132 // more than one
133 return nullptr;
134 }
135 last_use = edge.from();
136 DCHECK_NOT_NULL(last_use);
137 }
138 return last_use;
139 }
140
141 // Return true if node is the last consecutive StoreField node in a linear
142 // part of the effect chain.
143 bool IsEndOfStoreFieldChain(Node* node) {
144 Node* next_on_chain = SingleEffectUse(node);
145 return (next_on_chain == nullptr ||
146 next_on_chain->op()->opcode() != IrOpcode::kStoreField);
147 }
148
149 // The argument must be a StoreField node. If there is a node before it in the
150 // effect chain, and if this part of the effect chain is linear (no other
151 // effect uses of that previous node), then return that previous node.
152 // Otherwise, return nullptr.
153 //
154 // The returned node need not be a StoreField.
155 Node* PreviousEffectBeforeStoreField(Node* node) {
156 DCHECK_EQ(node->op()->opcode(), IrOpcode::kStoreField);
157 DCHECK_EQ(node->op()->EffectInputCount(), 1);
158
159 Node* previous = NodeProperties::GetEffectInput(node);
160 if (previous != nullptr && node == SingleEffectUse(previous)) {
161 return previous;
162 } else {
163 return nullptr;
164 }
165 }
166
167 } // namespace
168
169 bool StoreStoreElimination::IsEligibleNode(Node* node) {
170 return (node->op()->opcode() == IrOpcode::kStoreField) &&
171 IsEndOfStoreFieldChain(node);
172 }
173
174 void StoreStoreElimination::ReduceEligibleNode(Node* node) {
175 DCHECK(IsEligibleNode(node));
176
177 if (FLAG_trace_store_elimination) {
178 printf("** StoreStoreElimination::ReduceEligibleNode activated: #%d\n",
Jarin 2016/06/23 09:58:29 printf -> PrintF (here and elsewhere)
bgeron 2016/06/23 12:03:05 Done.
179 node->id());
180 }
181 auto log = [](NodeId id, Offset off, const char* msg) {
Jarin 2016/06/23 09:58:28 This does not have to be a lambda, please put it i
bgeron 2016/06/23 12:03:05 Done.
182 if (FLAG_trace_store_elimination) {
183 printf(
184 " StoreStoreElimination::ReduceEligibleNode: "
185 "#%d[[+%d]] -- %s\n",
186 id, off, msg);
187 }
188 };
189
190 // Initialize empty futureStore.
191 ZoneMap<Offset, Node*> futureStore(temp_zone());
192
193 Node* current_node = node;
194
195 do {
196 FieldAccess access = OpParameter<FieldAccess>(current_node->op());
197 Offset offset = ToOffset(access);
198 Node* object_input = current_node->InputAt(0);
199
200 Node* previous = PreviousEffectBeforeStoreField(current_node);
201
202 if (access.machine_type.IsWidestSize()) {
Jarin 2016/06/23 09:58:28 I still think this is not a good idea. If someone
bgeron 2016/06/23 12:03:05 Done.
203 // Try to insert. If it was present, this will preserve the original
204 // value.
205 auto insert_result =
206 futureStore.insert(std::make_pair(offset, object_input));
207 if (insert_result.second) {
208 // Key was not present. This means that there is no matching
209 // StoreField to this offset in the future, so we cannot optimize
210 // current_node away. However, we will record the current StoreField
211 // in futureStore, and continue ascending up the chain.
212 log(current_node->id(), offset, "wide, key not present");
213 } else if (insert_result.first->second != object_input) {
214 // Key was present, and the value did not equal object_input. This
215 // means that there is a StoreField to this offset in the future, but
216 // the object instance comes from a different Node. We pessimistically
217 // assume that we cannot optimize current_node away. However, we will
218 // record the current StoreField in futureStore, and continue
219 // ascending up the chain.
220 insert_result.first->second = object_input;
221 log(current_node->id(), offset, "wide, diff object");
222 } else {
223 // Key was present, and the value equalled object_input. This means that
224 // soon after in the effect chain, we will do a StoreField to the same
225 // object with the same offset, therefore current_node can be optimized
226 // away. We don't need to update futureStore.
227
228 Node* previous_effect = NodeProperties::GetEffectInput(current_node);
229
230 NodeProperties::ReplaceUses(current_node, nullptr, previous_effect,
231 nullptr, nullptr);
232 current_node->Kill();
233 log(current_node->id(), offset, "wide, eliminated");
234 }
235 } else {
236 log(current_node->id(), offset, "narrow, not eliminated");
237 }
238
239 // Regardless of whether we eliminated node {current}, we want to
240 // continue walking up the effect chain.
241
242 current_node = previous;
243 } while (current_node != nullptr &&
244 current_node->op()->opcode() == IrOpcode::kStoreField);
245
246 if (FLAG_trace_store_elimination) {
247 printf(
248 " StoreStoreElimination::ReduceEligibleNode: "
249 "stop chain traversal\n");
250 }
251 }
252
253 } // namespace compiler
254 } // namespace internal
255 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698