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

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

Issue 2120253002: [turbofan] Initial version of the new LoadElimination. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@TurboFan_StackCheck_NoWrite
Patch Set: REBASE. Created 4 years, 5 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
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler/load-elimination.h" 5 #include "src/compiler/load-elimination.h"
6 6
7 #include "src/compiler/graph.h" 7 #include "src/compiler/graph.h"
8 #include "src/compiler/node-marker.h"
8 #include "src/compiler/node-properties.h" 9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/node.h"
9 #include "src/compiler/simplified-operator.h" 11 #include "src/compiler/simplified-operator.h"
10 #include "src/types.h"
11 12
12 namespace v8 { 13 namespace v8 {
13 namespace internal { 14 namespace internal {
14 namespace compiler { 15 namespace compiler {
15 16
16 LoadElimination::~LoadElimination() {} 17 #ifdef DEBUG
17 18 #define TRACE(...) \
18 Reduction LoadElimination::Reduce(Node* node) { 19 do { \
20 if (FLAG_trace_turbo_load_elimination) PrintF(__VA_ARGS__); \
21 } while (false)
22 #else
23 #define TRACE(...)
24 #endif
25
26 namespace {
27
28 const size_t kMaxTrackedFields = 16;
29
30 Node* ActualValue(Node* node) {
19 switch (node->opcode()) { 31 switch (node->opcode()) {
20 case IrOpcode::kLoadField: 32 case IrOpcode::kCheckBounds:
21 return ReduceLoadField(node); 33 case IrOpcode::kCheckNumber:
34 case IrOpcode::kCheckTaggedPointer:
35 case IrOpcode::kCheckTaggedSigned:
36 case IrOpcode::kFinishRegion:
37 return ActualValue(NodeProperties::GetValueInput(node, 0));
22 default: 38 default:
23 break; 39 return node;
24 } 40 }
25 return NoChange();
26 } 41 }
27 42
28 Reduction LoadElimination::ReduceLoadField(Node* node) { 43 enum Aliasing { kNoAlias, kMayAlias, kMustAlias };
29 DCHECK_EQ(IrOpcode::kLoadField, node->opcode()); 44
30 FieldAccess const access = FieldAccessOf(node->op()); 45 Aliasing QueryAlias(Node* a, Node* b) {
31 Node* object = NodeProperties::GetValueInput(node, 0); 46 if (a == b) return kMustAlias;
32 for (Node* effect = NodeProperties::GetEffectInput(node);; 47 if (b->opcode() == IrOpcode::kAllocate) {
33 effect = NodeProperties::GetEffectInput(effect)) { 48 switch (a->opcode()) {
34 switch (effect->opcode()) { 49 case IrOpcode::kAllocate:
35 case IrOpcode::kLoadField: { 50 case IrOpcode::kHeapConstant:
36 FieldAccess const effect_access = FieldAccessOf(effect->op()); 51 case IrOpcode::kParameter:
37 if (object == NodeProperties::GetValueInput(effect, 0) && 52 return kNoAlias;
38 access == effect_access && effect_access.type->Is(access.type)) { 53 default:
39 Node* const value = effect; 54 break;
40 ReplaceWithValue(node, value); 55 }
41 return Replace(value); 56 }
57 if (a->opcode() == IrOpcode::kAllocate) {
58 switch (b->opcode()) {
59 case IrOpcode::kHeapConstant:
60 case IrOpcode::kParameter:
61 return kNoAlias;
62 default:
63 break;
64 }
65 }
66 return kMayAlias;
67 }
68
69 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; }
70
71 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
72
73 // Abstract state to approximate the current state of a certain field along the
74 // effect paths through the graph.
75 class AbstractField final : public ZoneObject {
76 public:
77 explicit AbstractField(Zone* zone) : info_for_node_(zone) {}
78 AbstractField(Node* object, Node* value, Zone* zone) : info_for_node_(zone) {
79 info_for_node_.insert(std::make_pair(object, value));
80 }
81
82 AbstractField const* Extend(Node* object, Node* value, Zone* zone) const {
83 AbstractField* that = new (zone) AbstractField(zone);
84 that->info_for_node_ = this->info_for_node_;
85 that->info_for_node_.insert(std::make_pair(object, value));
86 return that;
87 }
88 Node* Lookup(Node* object) const {
89 for (auto pair : info_for_node_) {
90 if (MustAlias(object, pair.first)) return pair.second;
91 }
92 return nullptr;
93 }
94 AbstractField const* Kill(Node* object, Zone* zone) const {
95 for (auto pair : this->info_for_node_) {
96 if (MayAlias(object, pair.first)) {
97 AbstractField* that = new (zone) AbstractField(zone);
98 for (auto pair : this->info_for_node_) {
99 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
42 } 100 }
43 break; 101 return that;
44 } 102 }
45 case IrOpcode::kStoreField: { 103 }
46 if (access == FieldAccessOf(effect->op())) { 104 return this;
47 if (object == NodeProperties::GetValueInput(effect, 0)) { 105 }
48 Node* const value = NodeProperties::GetValueInput(effect, 1); 106 bool Equals(AbstractField const* that) const {
49 Type* value_type = NodeProperties::GetType(value); 107 return this == that || this->info_for_node_ == that->info_for_node_;
50 Type* node_type = NodeProperties::GetType(node); 108 }
51 // Make sure the replacement's type is a subtype of the node's 109 AbstractField const* Merge(AbstractField const* that, Zone* zone) const {
52 // type. Otherwise we could confuse optimizations that were 110 if (this->Equals(that)) return this;
53 // based on the original type. 111 AbstractField* copy = new (zone) AbstractField(zone);
54 if (value_type->Is(node_type)) { 112 for (auto this_it : this->info_for_node_) {
55 ReplaceWithValue(node, value); 113 Node* this_object = this_it.first;
56 return Replace(value); 114 Node* this_value = this_it.second;
115 auto that_it = that->info_for_node_.find(this_object);
116 if (that_it != that->info_for_node_.end() &&
117 that_it->second == this_value) {
118 copy->info_for_node_.insert(this_it);
119 }
120 }
121 return copy;
122 }
123
124 private:
125 ZoneMap<Node*, Node*> info_for_node_;
126 };
127
128 // Abstract state to track the state of all fields along the effect paths
129 // through the graph.
130 class AbstractState final : public ZoneObject {
131 public:
132 AbstractState() {
133 for (size_t i = 0; i < kMaxTrackedFields; ++i) fields_[i] = nullptr;
134 }
135
136 AbstractState const* Extend(Node* object, size_t index, Node* value,
137 Zone* zone) const {
138 AbstractState* that = new (zone) AbstractState(*this);
139 AbstractField const* that_field = that->fields_[index];
140 if (that_field) {
141 that_field = that_field->Extend(object, value, zone);
142 } else {
143 that_field = new (zone) AbstractField(object, value, zone);
144 }
145 that->fields_[index] = that_field;
146 return that;
147 }
148 AbstractState const* Kill(Node* object, size_t index, Zone* zone) const {
149 if (!this->fields_[index]) return this;
150 AbstractState* that = new (zone) AbstractState(*this);
151 that->fields_[index] = nullptr;
152 return that;
153 }
154 AbstractState const* Merge(AbstractState const* that, Zone* zone) const {
155 if (this->Equals(that)) return this;
156 AbstractState* copy = new (zone) AbstractState();
157 for (size_t i = 0; i < kMaxTrackedFields; ++i) {
158 AbstractField const* this_field = this->fields_[i];
159 AbstractField const* that_field = that->fields_[i];
160 if (this_field && that_field) {
161 copy->fields_[i] = this_field->Merge(that_field, zone);
162 }
163 }
164 return copy;
165 }
166 Node* Lookup(Node* object, size_t index) const {
167 AbstractField const* this_field = this->fields_[index];
168 if (this_field) return this_field->Lookup(object);
169 return nullptr;
170 }
171 bool Equals(AbstractState const* that) const {
172 if (this == that) return true;
173 for (size_t i = 0; i < kMaxTrackedFields; ++i) {
174 AbstractField const* this_field = this->fields_[i];
175 AbstractField const* that_field = that->fields_[i];
176 if (this_field) {
177 if (!that_field || !this_field->Equals(that_field)) return false;
178 } else if (that_field) {
179 return false;
180 }
181 DCHECK(this_field == that_field || this_field->Equals(that_field));
182 }
183 return true;
184 }
185
186 private:
187 AbstractField const* fields_[kMaxTrackedFields];
188 };
189
190 class LoadEliminationAnalysis final {
191 public:
192 LoadEliminationAnalysis(Graph* graph, Zone* zone)
193 : candidates_(zone),
194 empty_state_(),
195 queue_(zone),
196 node_states_(graph->NodeCount(), zone) {}
197
198 void Run(Node* start) {
199 TRACE("--{Analysis phase}--\n");
200 UpdateState(start, empty_state());
201 while (!queue_.empty()) {
202 Node* const node = queue_.top();
203 queue_.pop();
204 VisitNode(node);
205 }
206 TRACE("--{Replacement phase}--\n");
207 ZoneMap<Node*, Node*> replacements(zone());
208 for (Node* const node : candidates_) {
209 switch (node->opcode()) {
210 case IrOpcode::kLoadField: {
211 FieldAccess const& access = FieldAccessOf(node->op());
212 Node* const object =
213 ActualValue(NodeProperties::GetValueInput(node, 0));
214 Node* const effect = NodeProperties::GetEffectInput(node);
215 AbstractState const* state = GetState(effect);
216 int field_index = FieldIndexOf(access);
217 DCHECK_LE(0, field_index);
218 if (Node* value = state->Lookup(object, field_index)) {
219 auto it = replacements.find(value);
220 if (it != replacements.end()) value = it->second;
221 Type* const value_type = NodeProperties::GetType(value);
222 if (value_type->Is(access.type)) {
223 replacements.insert(std::make_pair(node, value));
224 TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n",
225 node->id(), value->id(), value->op()->mnemonic());
226 NodeProperties::ReplaceUses(node, value, effect);
227 node->Kill();
57 } else { 228 } else {
58 // This LoadField has stronger guarantees than the stored value 229 TRACE(
59 // can give us, which suggests that we are probably in unreachable 230 " - Cannot replace redundant #%d:LoadField with #%d:%s,"
60 // code, guarded by some Check, so don't bother trying to optimize 231 " because types don't agree",
61 // this LoadField {node}. 232 node->id(), value->id(), value->op()->mnemonic());
62 return NoChange();
63 } 233 }
64 } 234 }
65 // TODO(turbofan): Alias analysis to the rescue? 235 break;
66 return NoChange();
67 } 236 }
237 case IrOpcode::kStoreField: {
238 FieldAccess const& access = FieldAccessOf(node->op());
239 Node* const object =
240 ActualValue(NodeProperties::GetValueInput(node, 0));
241 Node* const value =
242 ActualValue(NodeProperties::GetValueInput(node, 1));
243 Node* const effect = NodeProperties::GetEffectInput(node);
244 AbstractState const* state = GetState(effect);
245 int field_index = FieldIndexOf(access);
246 DCHECK_LE(0, field_index);
247 if (value == state->Lookup(object, field_index)) {
248 TRACE(" - Killing redundant #%d:StoreField\n", node->id());
249 NodeProperties::ReplaceUses(node, value, effect);
250 node->Kill();
251 }
252 break;
253 }
254 default:
255 UNREACHABLE();
256 }
257 }
258 }
259
260 private:
261 void VisitNode(Node* node) {
262 TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic());
263 switch (node->opcode()) {
264 case IrOpcode::kEffectPhi:
265 return VisitEffectPhi(node);
266 case IrOpcode::kLoadField:
267 return VisitLoadField(node);
268 case IrOpcode::kStoreElement:
269 return VisitStoreElement(node);
270 case IrOpcode::kStoreField:
271 return VisitStoreField(node);
272 case IrOpcode::kDeoptimize:
273 case IrOpcode::kReturn:
274 case IrOpcode::kTerminate:
275 case IrOpcode::kThrow:
68 break; 276 break;
69 } 277 default:
70 case IrOpcode::kBeginRegion: 278 return VisitOtherNode(node);
71 case IrOpcode::kStoreBuffer: 279 }
72 case IrOpcode::kStoreElement: { 280 }
73 // These can never interfere with field loads. 281
282 void VisitEffectPhi(Node* node) {
283 int const input_count = node->InputCount() - 1;
284 DCHECK_LT(0, input_count);
285 Node* const control = NodeProperties::GetControlInput(node);
286 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
287 AbstractState const* state = GetState(effect0);
288 if (state == nullptr) return;
289 if (control->opcode() == IrOpcode::kMerge) {
290 for (int i = 1; i < input_count; ++i) {
291 Node* const effecti = NodeProperties::GetEffectInput(node, i);
292 if (GetState(effecti) == nullptr) return;
293 }
294 }
295 for (int i = 1; i < input_count; ++i) {
296 Node* const effecti = NodeProperties::GetEffectInput(node, i);
297 if (AbstractState const* statei = GetState(effecti)) {
298 state = state->Merge(statei, zone());
299 }
300 }
301 UpdateState(node, state);
302 }
303
304 void VisitLoadField(Node* node) {
305 FieldAccess const& access = FieldAccessOf(node->op());
306 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
307 Node* const effect = NodeProperties::GetEffectInput(node);
308 AbstractState const* state = GetState(effect);
309 int field_index = FieldIndexOf(access);
310 if (field_index >= 0) {
311 Node* const value = state->Lookup(object, field_index);
312 if (!value) {
313 TRACE(" Node #%d:LoadField is not redundant\n", node->id());
314 state = state->Extend(object, field_index, node, zone());
315 } else if (!NodeProperties::GetType(value)->Is(access.type)) {
316 TRACE(
317 " Node #%d:LoadField is redundant for #%d:%s, but"
318 " types don't agree\n",
319 node->id(), value->id(), value->op()->mnemonic());
320 state = state->Extend(object, field_index, node, zone());
321 } else if (value) {
322 TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n",
323 node->id(), value->id(), value->op()->mnemonic());
324 candidates_.insert(node);
325 }
326 } else {
327 TRACE(" Node #%d:LoadField is unsupported\n", node->id());
328 }
329 UpdateState(node, state);
330 }
331
332 void VisitStoreField(Node* node) {
333 FieldAccess const& access = FieldAccessOf(node->op());
334 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
335 Node* const new_value = NodeProperties::GetValueInput(node, 1);
336 Node* const effect = NodeProperties::GetEffectInput(node);
337 AbstractState const* state = GetState(effect);
338 int field_index = FieldIndexOf(access);
339 if (field_index >= 0) {
340 Node* const old_value = state->Lookup(object, field_index);
341 if (old_value == new_value) {
342 TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n",
343 node->id(), new_value->id(), new_value->op()->mnemonic());
344 candidates_.insert(node);
345 }
346 TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n",
347 field_index, object->id(), object->op()->mnemonic());
348 state = state->Kill(object, field_index, zone());
349 TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n",
350 node->id(), new_value->id(), new_value->op()->mnemonic(),
351 field_index, object->id(), object->op()->mnemonic());
352 state = state->Extend(object, field_index, new_value, zone());
353 } else {
354 TRACE(" Node #%d:StoreField is unsupported\n", node->id());
355 state = empty_state();
356 }
357 UpdateState(node, state);
358 }
359
360 void VisitStoreElement(Node* node) {
361 Node* const effect = NodeProperties::GetEffectInput(node);
362 AbstractState const* state = GetState(effect);
363 UpdateState(node, state);
364 }
365
366 void VisitOtherNode(Node* node) {
367 DCHECK_EQ(1, node->op()->EffectInputCount());
368 DCHECK_EQ(1, node->op()->EffectOutputCount());
369 Node* const effect = NodeProperties::GetEffectInput(node);
370 AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite)
371 ? GetState(effect)
372 : empty_state();
373 UpdateState(node, state);
374 }
375
376 int FieldIndexOf(FieldAccess const& access) const {
377 switch (access.machine_type.representation()) {
378 case MachineRepresentation::kNone:
379 case MachineRepresentation::kBit:
380 UNREACHABLE();
74 break; 381 break;
75 } 382 case MachineRepresentation::kWord8:
76 case IrOpcode::kFinishRegion: { 383 case MachineRepresentation::kWord16:
77 // "Look through" FinishRegion nodes to make LoadElimination capable 384 case MachineRepresentation::kWord32:
78 // of looking into atomic regions. 385 case MachineRepresentation::kWord64:
79 if (object == effect) object = NodeProperties::GetValueInput(effect, 0); 386 case MachineRepresentation::kFloat32:
387 return -1; // Currently untracked.
388 case MachineRepresentation::kFloat64:
389 case MachineRepresentation::kSimd128:
390 case MachineRepresentation::kTagged:
391 // TODO(bmeurer): Check that we never do overlapping load/stores of
392 // individual parts of Float64/Simd128 values.
80 break; 393 break;
81 } 394 }
82 case IrOpcode::kAllocate: { 395 DCHECK_EQ(kTaggedBase, access.base_is_tagged);
83 // Allocations don't interfere with field loads. In case we see the 396 DCHECK_EQ(0, access.offset % kPointerSize);
84 // actual allocation for the {object} we can abort. 397 int field_index = access.offset / kPointerSize;
85 if (object == effect) return NoChange(); 398 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
86 break; 399 return field_index;
87 } 400 }
88 default: { 401
89 if (!effect->op()->HasProperty(Operator::kNoWrite) || 402 AbstractState const* GetState(Node* node) const {
90 effect->op()->EffectInputCount() != 1) { 403 return node_states_[node->id()];
91 return NoChange(); 404 }
92 } 405 void SetState(Node* node, AbstractState const* state) {
93 break; 406 node_states_[node->id()] = state;
94 } 407 }
95 } 408
96 } 409 void UpdateState(Node* node, AbstractState const* new_state) {
97 UNREACHABLE(); 410 AbstractState const* old_state = GetState(node);
98 return NoChange(); 411 if (old_state && old_state->Equals(new_state)) return;
412 SetState(node, new_state);
413 EnqueueUses(node);
414 }
415
416 void EnqueueUses(Node* node) {
417 for (Edge const edge : node->use_edges()) {
418 if (NodeProperties::IsEffectEdge(edge)) {
419 queue_.push(edge.from());
420 }
421 }
422 }
423
424 AbstractState const* empty_state() const { return &empty_state_; }
425 Zone* zone() const { return node_states_.get_allocator().zone(); }
426
427 ZoneSet<Node*> candidates_;
428 AbstractState const empty_state_;
429 ZoneStack<Node*> queue_;
430 ZoneVector<AbstractState const*> node_states_;
431
432 DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis);
433 };
434
435 } // namespace
436
437 void LoadElimination::Run() {
438 LoadEliminationAnalysis analysis(graph(), zone());
439 analysis.Run(graph()->start());
99 } 440 }
100 441
101 } // namespace compiler 442 } // namespace compiler
102 } // namespace internal 443 } // namespace internal
103 } // namespace v8 444 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698