OLD | NEW |
---|---|
(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/js-graph.h" | |
8 #include "src/compiler/node-properties.h" | |
9 #include "src/compiler/simplified-operator.h" | |
10 | |
11 namespace v8 { | |
12 namespace internal { | |
13 namespace compiler { | |
14 | |
15 // A pretty dumb store-store elimination. When the effect chain contains the | |
Jarin
2016/06/21 13:28:33
"pretty dumb" --> simple
bgeron
2016/06/21 17:48:58
Much better.. Would be good to keep the codebase f
| |
16 // following sequence, | |
17 // | |
18 // - StoreField[[+off_1]](x1, y1) | |
19 // - StoreField[[+off_2]](x2, y2) | |
20 // - StoreField[[+off_3]](x3, y3) | |
21 // ... | |
22 // - StoreField[[+off_n]](xn, yn) | |
23 // | |
24 // where the xes are the objects and the ys are the values to be stored, then | |
25 // we are going to assume that those stores can only overlap if they have | |
26 // exactly the same offset. If off_i = off_j and xi=xj and i < j, then we | |
27 // optimize the i'th StoreField away. | |
28 | |
29 // This optimization should be initiated on the last StoreField in such a | |
30 // sequence. | |
31 // | |
32 // The algorithm works by walking the effect chain from the last StoreField | |
33 // upwards. While walking, we maintain a map {nextStore} from offsets to | |
34 // nodes; initially it is empty. As we walk the effect chain upwards, if | |
35 // nextStore[off] = n, then next StoreField in the effect chain will have | |
36 // object input n. For example, for this effect chain | |
37 // | |
38 // 71: StoreField(60, 0) | |
39 // 72: StoreField(65, 8) | |
40 // 73: StoreField(63, 8) | |
41 // 74: StoreField(65, 16) | |
42 // 75: StoreField(62, 8) | |
43 // | |
44 // just before we get to 72, we will have nextStore = {8: 63, 16: 65}. | |
45 | |
46 // Here is the complete process. | |
47 // | |
48 // - We are at the end of a sequence of consecutive StoreFields. | |
49 // - We start out with nextStore = empty. | |
50 // - We then walk the effect chain upwards to find the next StoreField [1]. | |
51 // | |
52 // - If the offset is not a key of {nextStore} yet, we put it in. | |
53 | |
54 // - If the offset is a key of {nextStore}, but nextStore[offset] is a | |
55 // different node, we overwrite nextStore[offset] with the current node. | |
56 // - If the offset is a key of {nextStore} and nextStore[offset] equals this | |
57 // node, we eliminate this StoreField. | |
58 // - As long as the current effect input points to a node with a single | |
59 // effect output, and as long as its opcode is StoreField, we keep | |
60 // traversing upwards. | |
61 // | |
62 // [1] We make sure that we only traverse the linear part, that is, the part | |
63 // where every node has exactly one incoming and one outgoing effect edge. | |
64 // Also, we only keep walking upwards as long as we keep finding consecutive | |
65 // StoreFields on the same node. | |
66 | |
67 StoreStoreElimination::StoreStoreElimination(JSGraph* js_graph, Zone* zone) | |
68 : jsgraph_(js_graph), zone_(zone) {} | |
69 | |
70 StoreStoreElimination::~StoreStoreElimination() {} | |
71 | |
72 // 16 bits was chosen fairly arbitrarily; it seems enough now. 8 bits is too | |
73 // few. | |
74 typedef uint16_t Offset; | |
Jarin
2016/06/21 13:28:32
Please, put the file-local definitions into an ano
bgeron
2016/06/21 17:48:58
Done.
| |
75 | |
76 // To safely cast an offset from a FieldAccess, which has a wider range | |
77 // (namely int). | |
78 Offset toOffset(int offset) { | |
Jarin
2016/06/21 13:28:33
toOffset -> ToOffset
(link to style guide: https:
bgeron
2016/06/21 17:48:58
Done.
| |
79 CHECK(0 <= offset && offset < (1 << 8 * sizeof(Offset))); | |
80 return (Offset)offset; | |
81 } | |
82 | |
83 Offset toOffset(const FieldAccess& access) { return toOffset(access.offset); } | |
84 | |
85 // If node has a single effect use, return that node. If node has no or | |
86 // multiple effect uses, return nullptr. | |
87 static Node* SingleEffectUse(Node* node) { | |
88 Node* last_use = nullptr; | |
89 for (Edge edge : node->use_edges()) { | |
90 if (!NodeProperties::IsEffectEdge(edge)) { | |
91 continue; | |
92 } | |
93 if (last_use != nullptr) { | |
94 // more than one | |
95 return nullptr; | |
96 } | |
97 last_use = edge.from(); | |
98 DCHECK_NOT_NULL(last_use); | |
99 } | |
100 return last_use; | |
101 } | |
102 | |
103 // Return true if node is the last consecutive StoreField node in a linear | |
104 // part of the effect chain. | |
105 static bool IsEndOfStoreFieldChain(Node* node) { | |
106 Node* next_on_chain = SingleEffectUse(node); | |
107 return (next_on_chain == nullptr || | |
108 next_on_chain->op()->opcode() != IrOpcode::kStoreField); | |
109 } | |
110 | |
111 // The argument must be a StoreField node. If there is a node before it in the | |
112 // effect chain, and if this part of the effect chain is linear (no other | |
113 // effect uses of that previous node), then return that previous node. | |
114 // Otherwise, return nullptr. | |
115 // | |
116 // The returned node need not be a StoreField. | |
117 Node* PreviousEffectBeforeStoreField(Node* node) { | |
118 DCHECK_EQ(node->op()->opcode(), IrOpcode::kStoreField); | |
119 DCHECK_EQ(node->op()->EffectInputCount(), 1); | |
120 | |
121 Node* previous = NodeProperties::GetEffectInput(node); | |
122 if (previous != nullptr && node == SingleEffectUse(previous)) { | |
123 return previous; | |
124 } else { | |
125 return nullptr; | |
126 } | |
127 } | |
128 | |
129 bool StoreStoreElimination::IsEligibleNode(Node* node) { | |
130 return (node->op()->opcode() == IrOpcode::kStoreField) && | |
131 IsEndOfStoreFieldChain(node); | |
132 } | |
133 | |
134 void StoreStoreElimination::ReduceEligibleNode(Node* node) { | |
135 DCHECK(IsEligibleNode(node)); | |
136 | |
137 if (FLAG_trace_turbo_ssel) { | |
138 printf("** StoreStoreElimination::ReduceEligibleNode activated: #%d\n", | |
139 node->id()); | |
140 } | |
141 | |
142 // Initialize empty nextStore. | |
143 ZoneMap<Offset, Node*> nextStore = ZoneMap<Offset, Node*>(zone()); | |
144 | |
145 Node* current_node = node; | |
146 | |
147 while (current_node->op()->opcode() == IrOpcode::kStoreField) { | |
148 Offset offset = toOffset(OpParameter<FieldAccess>(current_node->op())); | |
149 Node* object_input = current_node->InputAt(0); | |
150 | |
151 // Try to insert. If it was present, this will preserve the original value. | |
Jarin
2016/06/21 13:28:33
You should handle field aliasing in some way. Perh
bgeron
2016/06/21 17:48:58
Is there more information on field aliasing? I cou
bgeron
2016/06/22 18:00:58
Done.
| |
152 auto insert_result = nextStore.insert(std::make_pair(offset, object_input)); | |
153 if (insert_result.second) { | |
154 // Key was not present. This means that there is no matching StoreField | |
155 // to this offset in the future, so we cannot optimize current_node away. | |
156 // However, we will record the current StoreField in nextStore, and | |
157 // continue ascending up the chain. | |
158 if (FLAG_trace_turbo_ssel) { | |
159 printf( | |
160 " StoreStoreElimination::ReduceEligibleNode: " | |
161 "#%d[[+%d]] -- key not present\n", | |
162 current_node->id(), offset); | |
163 } | |
164 } else if (insert_result.first->second != object_input) { | |
165 // Key was present, and the value did not equal object_input. This means | |
166 // that there is a StoreField to this offset in the future, but the | |
167 // object instance comes from a different Node. We pessimistically | |
168 // assume that we cannot optimize current_node away. However, we will | |
169 // record the current StoreField in nextStore, and continue ascending up | |
170 // the chain. | |
Jarin
2016/06/21 13:28:33
Note: Actually, I believe you could have a multima
bgeron
2016/06/21 17:48:58
I think you're right!
| |
171 insert_result.first->second = object_input; | |
172 if (FLAG_trace_turbo_ssel) { | |
173 printf( | |
174 " StoreStoreElimination::ReduceEligibleNode: " | |
175 "#%d[[+%d]] -- diff object\n", | |
176 current_node->id(), offset); | |
177 } | |
178 } else { | |
179 // Key was present, and the value equalled object_input. This means that | |
180 // soon after in the effect chain, we will do a StoreField to the same | |
181 // object with the same offset, therefore current_node can be optimized | |
182 // away. We don't need to update nextStore. | |
183 | |
184 Node* previous = PreviousEffectBeforeStoreField(current_node); | |
Jarin
2016/06/21 13:28:32
Can't you move this line to the beginning of the l
bgeron
2016/06/21 17:48:58
Great idea, I missed that.
| |
185 Node* previous_effect = NodeProperties::GetEffectInput(current_node); | |
186 | |
187 NodeProperties::ReplaceUses(current_node, nullptr, previous_effect, | |
188 nullptr, nullptr); | |
189 current_node->Kill(); | |
190 if (FLAG_trace_turbo_ssel) { | |
191 printf( | |
192 " StoreStoreElimination::ReduceEligibleNode: " | |
193 "#%d[[+%d]] -- eliminated\n", | |
194 current_node->id(), offset); | |
195 } | |
196 | |
197 // {current_node} is now invalid. If {previous} exists, that is, if the | |
198 // chain continues above, the next loop iteration will resume there. | |
199 | |
200 current_node = previous; | |
201 if (current_node != nullptr) { | |
202 continue; | |
203 } else { | |
204 break; | |
205 } | |
206 } | |
207 | |
208 // We did not eliminate node {current}, but we want to continue walking up | |
209 // the effect chain. | |
210 | |
211 current_node = PreviousEffectBeforeStoreField(current_node); | |
212 if (current_node == nullptr || | |
213 current_node->op()->opcode() != IrOpcode::kStoreField) { | |
214 break; | |
215 } | |
216 } | |
217 | |
218 if (FLAG_trace_turbo_ssel) { | |
219 printf( | |
220 " StoreStoreElimination::ReduceEligibleNode: " | |
221 "stop chain traversal\n"); | |
222 } | |
223 } | |
224 | |
225 } // namespace compiler | |
226 } // namespace internal | |
227 } // namespace v8 | |
OLD | NEW |