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

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: Fix DCHECK. Check types on substitution. 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
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/js-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) == kMayAlias; }
70
71 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
72
73 class AbstractField final : public ZoneObject {
74 public:
75 explicit AbstractField(Zone* zone) : info_for_node_(zone) {}
76 AbstractField(Node* object, Node* value, Zone* zone) : info_for_node_(zone) {
77 info_for_node_.insert(std::make_pair(object, value));
78 }
79
80 AbstractField const* Extend(Node* object, Node* value, Zone* zone) const {
81 AbstractField* that = new (zone) AbstractField(zone);
82 that->info_for_node_ = this->info_for_node_;
83 that->info_for_node_.insert(std::make_pair(object, value));
84 return that;
85 }
86 Node* Lookup(Node* object) const {
87 for (auto pair : info_for_node_) {
88 if (MustAlias(object, pair.first)) return pair.second;
89 }
90 return nullptr;
91 }
92 AbstractField const* Kill(Node* object, Zone* zone) const {
93 for (auto pair : this->info_for_node_) {
94 if (MayAlias(object, pair.first)) {
95 AbstractField* that = new (zone) AbstractField(zone);
96 for (auto pair : this->info_for_node_) {
97 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
42 } 98 }
43 break; 99 return that;
44 } 100 }
45 case IrOpcode::kStoreField: { 101 }
46 if (access == FieldAccessOf(effect->op())) { 102 return this;
47 if (object == NodeProperties::GetValueInput(effect, 0)) { 103 }
48 Node* const value = NodeProperties::GetValueInput(effect, 1); 104 bool Equals(AbstractField const* that) const {
49 Type* value_type = NodeProperties::GetType(value); 105 return this == that || this->info_for_node_ == that->info_for_node_;
50 Type* node_type = NodeProperties::GetType(node); 106 }
51 // Make sure the replacement's type is a subtype of the node's 107 AbstractField const* Merge(AbstractField const* that, Zone* zone) const {
52 // type. Otherwise we could confuse optimizations that were 108 if (this->Equals(that)) return this;
53 // based on the original type. 109 AbstractField* copy = new (zone) AbstractField(zone);
54 if (value_type->Is(node_type)) { 110 for (auto this_it : this->info_for_node_) {
55 ReplaceWithValue(node, value); 111 Node* this_object = this_it.first;
56 return Replace(value); 112 Node* this_value = this_it.second;
113 auto that_it = that->info_for_node_.find(this_object);
114 if (that_it != that->info_for_node_.end() &&
115 that_it->second == this_value) {
116 copy->info_for_node_.insert(this_it);
117 }
118 }
119 return copy;
120 }
121
122 private:
123 ZoneMap<Node*, Node*> info_for_node_;
124 };
125
126 class AbstractState final : public ZoneObject {
127 public:
128 AbstractState() {
129 for (size_t i = 0; i < kMaxTrackedFields; ++i) fields_[i] = nullptr;
130 }
131
132 AbstractState const* Extend(Node* object, size_t index, Node* value,
133 Zone* zone) const {
134 AbstractState* that = new (zone) AbstractState(*this);
135 AbstractField const* that_field = that->fields_[index];
136 if (that_field) {
137 that_field = that_field->Extend(object, value, zone);
138 } else {
139 that_field = new (zone) AbstractField(object, value, zone);
140 }
141 that->fields_[index] = that_field;
142 return that;
143 }
144 AbstractState const* Kill(Node* object, size_t index, Zone* zone) const {
145 if (!this->fields_[index]) return this;
146 AbstractState* that = new (zone) AbstractState(*this);
147 that->fields_[index] = nullptr;
148 return that;
149 }
150 AbstractState const* Merge(AbstractState const* that, Zone* zone) const {
151 if (this->Equals(that)) return this;
152 AbstractState* copy = new (zone) AbstractState();
153 for (size_t i = 0; i < kMaxTrackedFields; ++i) {
154 AbstractField const* this_field = this->fields_[i];
155 AbstractField const* that_field = that->fields_[i];
156 if (this_field && that_field) {
157 copy->fields_[i] = this_field->Merge(that_field, zone);
158 }
159 }
160 return copy;
161 }
162 Node* Lookup(Node* object, size_t index) const {
163 AbstractField const* this_field = this->fields_[index];
164 if (this_field) return this_field->Lookup(object);
165 return nullptr;
166 }
167 bool Equals(AbstractState const* that) const {
168 if (this == that) return true;
169 for (size_t i = 0; i < kMaxTrackedFields; ++i) {
170 AbstractField const* this_field = this->fields_[i];
171 AbstractField const* that_field = that->fields_[i];
172 if (this_field) {
173 if (!that_field || !this_field->Equals(that_field)) return false;
174 } else if (that_field) {
175 return false;
176 }
177 DCHECK(this_field == that_field || this_field->Equals(that_field));
178 }
179 return true;
180 }
181
182 private:
183 AbstractField const* fields_[kMaxTrackedFields];
184 };
185
186 class LoadEliminationAnalysis final {
187 public:
188 LoadEliminationAnalysis(Graph* graph, Zone* zone)
189 : candidates_(zone),
190 empty_state_(),
191 queue_(zone),
192 node_states_(graph->NodeCount(), zone) {}
193
194 void Run(Node* start) {
195 TRACE("--{Analysis phase}--\n");
196 UpdateState(start, empty_state());
197 while (!queue_.empty()) {
198 Node* const node = queue_.top();
199 queue_.pop();
200 VisitNode(node);
201 }
202 TRACE("--{Replacement phase}--\n");
203 ZoneMap<Node*, Node*> replacements(zone());
204 for (Node* const node : candidates_) {
205 switch (node->opcode()) {
206 case IrOpcode::kLoadField: {
207 FieldAccess const& access = FieldAccessOf(node->op());
208 Node* const object =
209 ActualValue(NodeProperties::GetValueInput(node, 0));
210 Node* const effect = NodeProperties::GetEffectInput(node);
211 AbstractState const* state = GetState(effect);
212 int field_index = FieldIndexOf(access);
213 DCHECK_LE(0, field_index);
214 if (Node* value = state->Lookup(object, field_index)) {
215 auto it = replacements.find(value);
216 if (it != replacements.end()) value = it->second;
217 Type* const value_type = NodeProperties::GetType(value);
218 if (value_type->Is(access.type)) {
219 replacements.insert(std::make_pair(node, value));
220 TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n",
221 node->id(), value->id(), value->op()->mnemonic());
222 NodeProperties::ReplaceUses(node, value, effect);
223 node->Kill();
57 } else { 224 } else {
58 // This LoadField has stronger guarantees than the stored value 225 TRACE(
59 // can give us, which suggests that we are probably in unreachable 226 " - Cannot replace redundant #%d:LoadField with #%d:%s,"
60 // code, guarded by some Check, so don't bother trying to optimize 227 " because types don't agree",
61 // this LoadField {node}. 228 node->id(), value->id(), value->op()->mnemonic());
62 return NoChange();
63 } 229 }
64 } 230 }
65 // TODO(turbofan): Alias analysis to the rescue? 231 break;
66 return NoChange();
67 } 232 }
233 case IrOpcode::kStoreField: {
234 FieldAccess const& access = FieldAccessOf(node->op());
235 Node* const object =
236 ActualValue(NodeProperties::GetValueInput(node, 0));
237 Node* const value =
238 ActualValue(NodeProperties::GetValueInput(node, 1));
239 Node* const effect = NodeProperties::GetEffectInput(node);
240 AbstractState const* state = GetState(effect);
241 int field_index = FieldIndexOf(access);
242 DCHECK_LE(0, field_index);
243 if (value == state->Lookup(object, field_index)) {
244 TRACE(" - Killing redundant #%d:StoreField\n", node->id());
245 NodeProperties::ReplaceUses(node, value, effect);
246 node->Kill();
247 }
248 break;
249 }
250 default:
251 UNREACHABLE();
252 }
253 }
254 }
255
256 private:
257 void VisitNode(Node* node) {
258 TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic());
259 switch (node->opcode()) {
260 case IrOpcode::kEffectPhi:
261 return VisitEffectPhi(node);
262 case IrOpcode::kLoadField:
263 return VisitLoadField(node);
264 case IrOpcode::kStoreElement:
265 return VisitStoreElement(node);
266 case IrOpcode::kStoreField:
267 return VisitStoreField(node);
268 case IrOpcode::kDeoptimize:
269 case IrOpcode::kReturn:
270 case IrOpcode::kTerminate:
271 case IrOpcode::kThrow:
68 break; 272 break;
69 } 273 default:
70 case IrOpcode::kBeginRegion: 274 return VisitOtherNode(node);
71 case IrOpcode::kStoreBuffer: 275 }
72 case IrOpcode::kStoreElement: { 276 }
73 // These can never interfere with field loads. 277
74 break; 278 void VisitEffectPhi(Node* node) {
75 } 279 int const input_count = node->InputCount() - 1;
76 case IrOpcode::kFinishRegion: { 280 DCHECK_LT(0, input_count);
77 // "Look through" FinishRegion nodes to make LoadElimination capable 281 Node* const control = NodeProperties::GetControlInput(node);
78 // of looking into atomic regions. 282 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
79 if (object == effect) object = NodeProperties::GetValueInput(effect, 0); 283 AbstractState const* state = GetState(effect0);
80 break; 284 if (state == nullptr) return;
81 } 285 if (control->opcode() == IrOpcode::kMerge) {
82 case IrOpcode::kAllocate: { 286 for (int i = 1; i < input_count; ++i) {
83 // Allocations don't interfere with field loads. In case we see the 287 Node* const effecti = NodeProperties::GetEffectInput(node, i);
84 // actual allocation for the {object} we can abort. 288 if (GetState(effecti) == nullptr) return;
85 if (object == effect) return NoChange(); 289 }
86 break; 290 }
87 } 291 for (int i = 1; i < input_count; ++i) {
88 default: { 292 Node* const effecti = NodeProperties::GetEffectInput(node, i);
89 if (!effect->op()->HasProperty(Operator::kNoWrite) || 293 if (AbstractState const* statei = GetState(effecti)) {
90 effect->op()->EffectInputCount() != 1) { 294 state = state->Merge(statei, zone());
91 return NoChange(); 295 }
92 } 296 }
93 break; 297 UpdateState(node, state);
94 } 298 }
95 } 299
96 } 300 int FieldIndexOf(FieldAccess const& access) const {
97 UNREACHABLE(); 301 if (access.offset % kPointerSize != 0) return -1;
98 return NoChange(); 302 int field_index = access.offset / kPointerSize;
303 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
304 return field_index;
305 }
306 AbstractState const* GetState(Node* node) const {
307 return node_states_[node->id()];
308 }
309 void SetState(Node* node, AbstractState const* state) {
310 node_states_[node->id()] = state;
311 }
312
313 void VisitLoadField(Node* node) {
314 FieldAccess const& access = FieldAccessOf(node->op());
315 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
316 Node* const effect = NodeProperties::GetEffectInput(node);
317 AbstractState const* state = GetState(effect);
318 int field_index = FieldIndexOf(access);
319 if (field_index >= 0) {
320 Node* const value = state->Lookup(object, field_index);
321 if (!value) {
322 TRACE(" Node #%d:LoadField is not redundant\n", node->id());
323 state = state->Extend(object, field_index, node, zone());
324 } else if (!NodeProperties::GetType(value)->Is(access.type)) {
325 TRACE(
326 " Node #%d:LoadField is redundant for #%d:%s, but"
327 " types don't agree\n",
328 node->id(), value->id(), value->op()->mnemonic());
329 state = state->Extend(object, field_index, node, zone());
330 } else if (value) {
331 TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n",
332 node->id(), value->id(), value->op()->mnemonic());
333 candidates_.insert(node);
334 }
335 }
336 UpdateState(node, state);
337 }
338
339 void VisitStoreField(Node* node) {
340 FieldAccess const& access = FieldAccessOf(node->op());
341 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
342 Node* const new_value = NodeProperties::GetValueInput(node, 1);
343 Node* const effect = NodeProperties::GetEffectInput(node);
344 AbstractState const* state = GetState(effect);
345 int field_index = FieldIndexOf(access);
346 if (field_index >= 0) {
347 Node* const old_value = state->Lookup(object, field_index);
348 if (old_value == new_value) {
349 TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n",
350 node->id(), new_value->id(), new_value->op()->mnemonic());
351 candidates_.insert(node);
352 }
353 TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n",
354 field_index, object->id(), object->op()->mnemonic());
355 state = state->Kill(object, field_index, zone());
356 TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n",
357 node->id(), new_value->id(), new_value->op()->mnemonic(),
358 field_index, object->id(), object->op()->mnemonic());
359 state = state->Extend(object, field_index, new_value, zone());
360 } else {
361 TRACE(" Node #%d:StoreField is misaligned\n", node->id());
362 state = empty_state();
363 }
364 UpdateState(node, state);
365 }
366
367 void VisitStoreElement(Node* node) {
368 Node* const effect = NodeProperties::GetEffectInput(node);
369 AbstractState const* state = GetState(effect);
370 UpdateState(node, state);
371 }
372
373 void VisitOtherNode(Node* node) {
374 DCHECK_EQ(1, node->op()->EffectInputCount());
375 DCHECK_EQ(1, node->op()->EffectOutputCount());
376 Node* const effect = NodeProperties::GetEffectInput(node);
377 AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite)
378 ? GetState(effect)
379 : empty_state();
380 UpdateState(node, state);
381 }
382
383 void UpdateState(Node* node, AbstractState const* new_state) {
384 AbstractState const* old_state = GetState(node);
385 if (old_state && old_state->Equals(new_state)) return;
386 SetState(node, new_state);
387 EnqueueUses(node);
388 }
389
390 void EnqueueUses(Node* node) {
391 for (Edge const edge : node->use_edges()) {
392 if (NodeProperties::IsEffectEdge(edge)) {
393 queue_.push(edge.from());
394 }
395 }
396 }
397
398 AbstractState const* empty_state() const { return &empty_state_; }
399 Zone* zone() const { return node_states_.get_allocator().zone(); }
400
401 ZoneSet<Node*> candidates_;
402 AbstractState const empty_state_;
403 ZoneStack<Node*> queue_;
404 ZoneVector<AbstractState const*> node_states_;
405
406 DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis);
407 };
408
409 } // namespace
410
411 void LoadElimination::Run() {
412 LoadEliminationAnalysis analysis(graph(), zone());
413 analysis.Run(graph()->start());
99 } 414 }
100 415
416 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
417
101 } // namespace compiler 418 } // namespace compiler
102 } // namespace internal 419 } // namespace internal
103 } // namespace v8 420 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698