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

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: 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/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; }
Jarin 2016/07/04 13:18:07 How about QueryAlias(a, b) == kMayAlias || QueryAl
Benedikt Meurer 2016/07/05 11:29:09 Done.
70
71 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
72
73 class AbstractField final : public ZoneObject {
Jarin 2016/07/04 13:18:07 Please explain in a comment what this is (+ the re
Benedikt Meurer 2016/07/05 11:29:09 Done.
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 }
99 return that;
100 }
101 }
102 return this;
103 }
104 bool Equals(AbstractField const* that) const {
105 return this == that || this->info_for_node_ == that->info_for_node_;
106 }
107 AbstractField const* Merge(AbstractField const* that, Zone* zone) const {
108 if (this->Equals(that)) return this;
109 AbstractField* copy = new (zone) AbstractField(zone);
110 for (auto this_it : this->info_for_node_) {
111 Node* this_object = this_it.first;
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);
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 replacements.insert(std::make_pair(node, value));
218 TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n",
219 node->id(), value->id(), value->op()->mnemonic());
220 NodeProperties::ReplaceUses(node, value, effect);
221 node->Kill();
222 }
223 break;
224 }
225 case IrOpcode::kStoreField: {
226 FieldAccess const& access = FieldAccessOf(node->op());
227 Node* const object =
228 ActualValue(NodeProperties::GetValueInput(node, 0));
229 Node* const value =
230 ActualValue(NodeProperties::GetValueInput(node, 1));
231 Node* const effect = NodeProperties::GetEffectInput(node);
232 AbstractState const* state = GetState(effect);
233 int field_index = FieldIndexOf(access);
234 DCHECK_LE(0, field_index);
Jarin 2016/07/04 13:18:07 It is a shame that we cannot learn from control fl
Benedikt Meurer 2016/07/05 11:29:09 Yeah... compiler hall of shame.
235 if (value == state->Lookup(object, field_index)) {
236 TRACE(" - Killing redundant #%d:StoreField\n", node->id());
237 NodeProperties::ReplaceUses(node, value, effect);
238 node->Kill();
239 }
240 break;
241 }
242 default:
243 UNREACHABLE();
244 }
245 }
246 }
247
248 private:
249 void VisitNode(Node* node) {
250 TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic());
251 switch (node->opcode()) {
252 case IrOpcode::kEffectPhi:
253 return VisitEffectPhi(node);
254 case IrOpcode::kLoadField:
255 return VisitLoadField(node);
256 case IrOpcode::kStoreElement:
257 return VisitStoreElement(node);
258 case IrOpcode::kStoreField:
259 return VisitStoreField(node);
260 case IrOpcode::kDeoptimize:
261 case IrOpcode::kReturn:
262 case IrOpcode::kTerminate:
263 case IrOpcode::kThrow:
43 break; 264 break;
44 } 265 default:
45 case IrOpcode::kStoreField: { 266 return VisitOtherNode(node);
46 if (access == FieldAccessOf(effect->op())) { 267 }
47 if (object == NodeProperties::GetValueInput(effect, 0)) { 268 }
48 Node* const value = NodeProperties::GetValueInput(effect, 1); 269
49 Type* value_type = NodeProperties::GetType(value); 270 void VisitEffectPhi(Node* node) {
50 Type* node_type = NodeProperties::GetType(node); 271 int const input_count = node->InputCount() - 1;
51 // Make sure the replacement's type is a subtype of the node's 272 DCHECK_LT(0, input_count);
52 // type. Otherwise we could confuse optimizations that were 273 Node* const control = NodeProperties::GetControlInput(node);
53 // based on the original type. 274 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
54 if (value_type->Is(node_type)) { 275 AbstractState const* state = GetState(effect0);
55 ReplaceWithValue(node, value); 276 if (state == nullptr) return;
56 return Replace(value); 277 if (control->opcode() == IrOpcode::kMerge) {
57 } else { 278 for (int i = 1; i < input_count; ++i) {
58 // This LoadField has stronger guarantees than the stored value 279 Node* const effecti = NodeProperties::GetEffectInput(node, i);
59 // can give us, which suggests that we are probably in unreachable 280 if (GetState(effecti) == nullptr) return;
60 // code, guarded by some Check, so don't bother trying to optimize 281 }
61 // this LoadField {node}. 282 }
62 return NoChange(); 283 for (int i = 1; i < input_count; ++i) {
63 } 284 Node* const effecti = NodeProperties::GetEffectInput(node, i);
64 } 285 if (AbstractState const* statei = GetState(effecti)) {
65 // TODO(turbofan): Alias analysis to the rescue? 286 state = state->Merge(statei, zone());
66 return NoChange(); 287 }
67 } 288 }
68 break; 289 UpdateState(node, state);
69 } 290 }
70 case IrOpcode::kBeginRegion: 291
71 case IrOpcode::kStoreBuffer: 292 int FieldIndexOf(FieldAccess const& access) const {
72 case IrOpcode::kStoreElement: { 293 if (access.offset % kPointerSize != 0) return -1;
73 // These can never interfere with field loads. 294 int field_index = access.offset / kPointerSize;
74 break; 295 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
75 } 296 return field_index;
76 case IrOpcode::kFinishRegion: { 297 }
77 // "Look through" FinishRegion nodes to make LoadElimination capable 298 AbstractState const* GetState(Node* node) const {
78 // of looking into atomic regions. 299 return node_states_[node->id()];
79 if (object == effect) object = NodeProperties::GetValueInput(effect, 0); 300 }
80 break; 301 void SetState(Node* node, AbstractState const* state) {
81 } 302 node_states_[node->id()] = state;
82 case IrOpcode::kAllocate: { 303 }
83 // Allocations don't interfere with field loads. In case we see the 304
84 // actual allocation for the {object} we can abort. 305 void VisitLoadField(Node* node) {
85 if (object == effect) return NoChange(); 306 FieldAccess const& access = FieldAccessOf(node->op());
86 break; 307 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
87 } 308 Node* const effect = NodeProperties::GetEffectInput(node);
88 default: { 309 AbstractState const* state = GetState(effect);
89 if (!effect->op()->HasProperty(Operator::kNoWrite) || 310 int field_index = FieldIndexOf(access);
Jarin 2016/07/04 13:18:07 Should not we also check the size? Either we only
Benedikt Meurer 2016/07/05 11:29:09 Done.
90 effect->op()->EffectInputCount() != 1) { 311 if (field_index >= 0) {
91 return NoChange(); 312 Node* const value = state->Lookup(object, field_index);
92 } 313 if (!value) {
93 break; 314 TRACE(" Node #%d:LoadField is not redundant\n", node->id());
94 } 315 state = state->Extend(object, field_index, node, zone());
95 } 316 } else if (!NodeProperties::GetType(value)->Is(access.type)) {
96 } 317 TRACE(
97 UNREACHABLE(); 318 " Node #%d:LoadField is redundant for #%d:%s, but"
98 return NoChange(); 319 " types don't agree\n",
320 node->id(), value->id(), value->op()->mnemonic());
321 state = state->Extend(object, field_index, node, zone());
322 } else if (value) {
323 TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n",
324 node->id(), value->id(), value->op()->mnemonic());
325 candidates_.insert(node);
326 }
327 }
328 UpdateState(node, state);
329 }
330
331 void VisitStoreField(Node* node) {
332 FieldAccess const& access = FieldAccessOf(node->op());
333 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
334 Node* const new_value = NodeProperties::GetValueInput(node, 1);
335 Node* const effect = NodeProperties::GetEffectInput(node);
336 AbstractState const* state = GetState(effect);
337 int field_index = FieldIndexOf(access);
338 if (field_index >= 0) {
339 Node* const old_value = state->Lookup(object, field_index);
340 if (old_value == new_value) {
341 TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n",
342 node->id(), new_value->id(), new_value->op()->mnemonic());
343 candidates_.insert(node);
344 }
345 TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n",
346 field_index, object->id(), object->op()->mnemonic());
347 state = state->Kill(object, field_index, zone());
348 TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n",
349 node->id(), new_value->id(), new_value->op()->mnemonic(),
350 field_index, object->id(), object->op()->mnemonic());
351 state = state->Extend(object, field_index, new_value, zone());
352 } else {
353 TRACE(" Node #%d:StoreField is misaligned\n", node->id());
354 state = empty_state();
355 }
356 UpdateState(node, state);
357 }
358
359 void VisitStoreElement(Node* node) {
360 Node* const effect = NodeProperties::GetEffectInput(node);
361 AbstractState const* state = GetState(effect);
362 UpdateState(node, state);
363 }
364
365 void VisitOtherNode(Node* node) {
366 DCHECK_EQ(1, node->op()->EffectInputCount());
367 DCHECK_EQ(1, node->op()->EffectOutputCount());
368 Node* const effect = NodeProperties::GetEffectInput(node);
369 AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite)
370 ? GetState(effect)
371 : empty_state();
372 UpdateState(node, state);
373 }
374
375 void UpdateState(Node* node, AbstractState const* new_state) {
376 AbstractState const* old_state = GetState(node);
377 if (old_state && old_state->Equals(new_state)) return;
378 SetState(node, new_state);
379 EnqueueUses(node);
380 }
381
382 void EnqueueUses(Node* node) {
383 for (Edge const edge : node->use_edges()) {
384 if (NodeProperties::IsEffectEdge(edge)) {
385 queue_.push(edge.from());
386 }
387 }
388 }
389
390 AbstractState const* empty_state() const { return &empty_state_; }
391 Zone* zone() const { return node_states_.get_allocator().zone(); }
392
393 ZoneSet<Node*> candidates_;
394 AbstractState const empty_state_;
395 ZoneStack<Node*> queue_;
396 ZoneVector<AbstractState const*> node_states_;
397
398 DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis);
399 };
400
401 } // namespace
402
403 void LoadElimination::Run() {
404 LoadEliminationAnalysis analysis(graph(), zone());
405 analysis.Run(graph()->start());
99 } 406 }
100 407
408 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
409
101 } // namespace compiler 410 } // namespace compiler
102 } // namespace internal 411 } // namespace internal
103 } // namespace v8 412 } // 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