blob: 6d01ae573be81d1dd2e86444cfa88af0a9091762 [file] [log] [blame]
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001// Copyright 2013 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "hydrogen-alias-analysis.h"
29#include "hydrogen-load-elimination.h"
30#include "hydrogen-instructions.h"
31
32namespace v8 {
33namespace internal {
34
35static const int kMaxTrackedFields = 16;
36static const int kMaxTrackedObjects = 5;
37
38// An element in the field approximation list.
39class HFieldApproximation : public ZoneObject {
40 public: // Just a data blob.
41 HValue* object_;
42 HLoadNamedField* last_load_;
43 HValue* last_value_;
44 HFieldApproximation* next_;
45};
46
47
48// The main datastructure used during load/store elimination. Each in-object
49// field is tracked separately. For each field, store a list of known field
50// values for known objects.
51class HLoadEliminationTable BASE_EMBEDDED {
52 public:
53 HLoadEliminationTable(Zone* zone, HAliasAnalyzer* aliasing)
54 : zone_(zone), fields_(kMaxTrackedFields, zone), aliasing_(aliasing) { }
55
56 // Process a load instruction, updating internal table state. If a previous
57 // load or store for this object and field exists, return the new value with
58 // which the load should be replaced. Otherwise, return {instr}.
59 HValue* load(HLoadNamedField* instr) {
60 int field = FieldOf(instr->access());
61 if (field < 0) return instr;
62
63 HValue* object = instr->object()->ActualValue();
64 HFieldApproximation* approx = FindOrCreate(object, field);
65
66 if (approx->last_value_ == NULL) {
67 // Load is not redundant. Fill out a new entry.
68 approx->last_load_ = instr;
69 approx->last_value_ = instr;
70 return instr;
71 } else {
72 // Eliminate the load. Reuse previously stored value or load instruction.
73 return approx->last_value_;
74 }
75 }
76
77 // Process a store instruction, updating internal table state. If a previous
78 // store to the same object and field makes this store redundant (e.g. because
79 // the stored values are the same), return NULL indicating that this store
80 // instruction is redundant. Otherwise, return {instr}.
81 HValue* store(HStoreNamedField* instr) {
82 int field = FieldOf(instr->access());
83 if (field < 0) return instr;
84
85 HValue* object = instr->object()->ActualValue();
86 HValue* value = instr->value();
87
88 // Kill non-equivalent may-alias entries.
89 KillFieldInternal(object, field, value);
90 if (instr->has_transition()) {
91 // A transition store alters the map of the object.
92 // TODO(titzer): remember the new map (a constant) for the object.
93 KillFieldInternal(object, FieldOf(JSObject::kMapOffset), NULL);
94 }
95 HFieldApproximation* approx = FindOrCreate(object, field);
96
97 if (Equal(approx->last_value_, value)) {
98 // The store is redundant because the field already has this value.
99 return NULL;
100 } else {
101 // The store is not redundant. Update the entry.
102 approx->last_load_ = NULL;
103 approx->last_value_ = value;
104 return instr;
105 }
106 }
107
108 // Kill everything in this table.
109 void Kill() {
110 fields_.Rewind(0);
111 }
112
113 // Kill all entries matching the given offset.
114 void KillOffset(int offset) {
115 int field = FieldOf(offset);
116 if (field >= 0 && field < fields_.length()) {
117 fields_[field] = NULL;
118 }
119 }
120
121 // Compute the field index for the given object access; -1 if not tracked.
122 int FieldOf(HObjectAccess access) {
123 // Only track kMaxTrackedFields in-object fields.
124 if (!access.IsInobject()) return -1;
125 return FieldOf(access.offset());
126 }
127
128 // Print this table to stdout.
129 void Print() {
130 for (int i = 0; i < fields_.length(); i++) {
131 PrintF(" field %d: ", i);
132 for (HFieldApproximation* a = fields_[i]; a != NULL; a = a->next_) {
133 PrintF("[o%d =", a->object_->id());
134 if (a->last_load_ != NULL) PrintF(" L%d", a->last_load_->id());
135 if (a->last_value_ != NULL) PrintF(" v%d", a->last_value_->id());
136 PrintF("] ");
137 }
138 PrintF("\n");
139 }
140 }
141
142 private:
143 // Find or create an entry for the given object and field pair.
144 HFieldApproximation* FindOrCreate(HValue* object, int field) {
145 EnsureFields(field + 1);
146
147 // Search for a field approximation for this object.
148 HFieldApproximation* approx = fields_[field];
149 int count = 0;
150 while (approx != NULL) {
151 if (aliasing_->MustAlias(object, approx->object_)) return approx;
152 count++;
153 approx = approx->next_;
154 }
155
156 if (count >= kMaxTrackedObjects) {
157 // Pull the last entry off the end and repurpose it for this object.
158 approx = ReuseLastApproximation(field);
159 } else {
160 // Allocate a new entry.
161 approx = new(zone_) HFieldApproximation();
162 }
163
164 // Insert the entry at the head of the list.
165 approx->object_ = object;
166 approx->last_load_ = NULL;
167 approx->last_value_ = NULL;
168 approx->next_ = fields_[field];
169 fields_[field] = approx;
170
171 return approx;
172 }
173
174 // Kill all entries for a given field that _may_ alias the given object
175 // and do _not_ have the given value.
176 void KillFieldInternal(HValue* object, int field, HValue* value) {
177 if (field >= fields_.length()) return; // Nothing to do.
178
179 HFieldApproximation* approx = fields_[field];
180 HFieldApproximation* prev = NULL;
181 while (approx != NULL) {
182 if (aliasing_->MayAlias(object, approx->object_)) {
183 if (!Equal(approx->last_value_, value)) {
184 // Kill an aliasing entry that doesn't agree on the value.
185 if (prev != NULL) {
186 prev->next_ = approx->next_;
187 } else {
188 fields_[field] = approx->next_;
189 }
190 approx = approx->next_;
191 continue;
192 }
193 }
194 prev = approx;
195 approx = approx->next_;
196 }
197 }
198
199 bool Equal(HValue* a, HValue* b) {
200 if (a == b) return true;
201 if (a != NULL && b != NULL) return a->Equals(b);
202 return false;
203 }
204
205 // Remove the last approximation for a field so that it can be reused.
206 // We reuse the last entry because it was the first inserted and is thus
207 // farthest away from the current instruction.
208 HFieldApproximation* ReuseLastApproximation(int field) {
209 HFieldApproximation* approx = fields_[field];
210 ASSERT(approx != NULL);
211
212 HFieldApproximation* prev = NULL;
213 while (approx->next_ != NULL) {
214 prev = approx;
215 approx = approx->next_;
216 }
217 if (prev != NULL) prev->next_ = NULL;
218 return approx;
219 }
220
221 // Ensure internal storage for the given number of fields.
222 void EnsureFields(int num_fields) {
223 while (fields_.length() < num_fields) fields_.Add(NULL, zone_);
224 }
225
226 // Compute the field index for the given in-object offset.
227 int FieldOf(int offset) {
228 if (offset >= kMaxTrackedFields * kPointerSize) return -1;
229 ASSERT((offset % kPointerSize) == 0); // Assume aligned accesses.
230 return offset / kPointerSize;
231 }
232
233 Zone* zone_;
234 ZoneList<HFieldApproximation*> fields_;
235 HAliasAnalyzer* aliasing_;
236};
237
238
239void HLoadEliminationPhase::Run() {
240 for (int i = 0; i < graph()->blocks()->length(); i++) {
241 HBasicBlock* block = graph()->blocks()->at(i);
242 EliminateLoads(block);
243 }
244}
245
246
247// For code de-uglification.
248#define TRACE(x) if (FLAG_trace_load_elimination) PrintF x
249
250
251// Eliminate loads and stores local to a block.
252void HLoadEliminationPhase::EliminateLoads(HBasicBlock* block) {
253 HAliasAnalyzer aliasing;
254 HLoadEliminationTable table(zone(), &aliasing);
255
256 TRACE(("-- load-elim B%d -------------------------------------------------\n",
257 block->block_id()));
258
259 for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
260 bool changed = false;
261 HInstruction* instr = it.Current();
262
263 switch (instr->opcode()) {
264 case HValue::kLoadNamedField: {
265 HLoadNamedField* load = HLoadNamedField::cast(instr);
266 TRACE((" process L%d field %d (o%d)\n",
267 instr->id(),
268 table.FieldOf(load->access()),
269 load->object()->ActualValue()->id()));
270 HValue* result = table.load(load);
271 if (result != instr) {
272 // The load can be replaced with a previous load or a value.
273 TRACE((" replace L%d -> v%d\n", instr->id(), result->id()));
274 instr->DeleteAndReplaceWith(result);
275 }
276 changed = true;
277 break;
278 }
279 case HValue::kStoreNamedField: {
280 HStoreNamedField* store = HStoreNamedField::cast(instr);
281 TRACE((" process S%d field %d (o%d) = v%d\n",
282 instr->id(),
283 table.FieldOf(store->access()),
284 store->object()->ActualValue()->id(),
285 store->value()->id()));
286 HValue* result = table.store(store);
287 if (result == NULL) {
288 // The store is redundant. Remove it.
289 TRACE((" remove S%d\n", instr->id()));
290 instr->DeleteAndReplaceWith(NULL);
291 }
292 changed = true;
293 break;
294 }
295 default: {
296 if (instr->CheckGVNFlag(kChangesInobjectFields)) {
297 TRACE((" kill-all i%d\n", instr->id()));
298 table.Kill();
299 continue;
300 }
301 if (instr->CheckGVNFlag(kChangesMaps)) {
302 TRACE((" kill-maps i%d\n", instr->id()));
303 table.KillOffset(JSObject::kMapOffset);
304 }
305 if (instr->CheckGVNFlag(kChangesElementsKind)) {
306 TRACE((" kill-elements-kind i%d\n", instr->id()));
307 table.KillOffset(JSObject::kMapOffset);
308 table.KillOffset(JSObject::kElementsOffset);
309 }
310 if (instr->CheckGVNFlag(kChangesElementsPointer)) {
311 TRACE((" kill-elements i%d\n", instr->id()));
312 table.KillOffset(JSObject::kElementsOffset);
313 }
314 }
315 // Improvements possible:
316 // - learn from HCheckMaps for field 0
317 // - remove unobservable stores (write-after-write)
318 }
319
320 if (changed && FLAG_trace_load_elimination) {
321 table.Print();
322 }
323 }
324}
325
326
327} } // namespace v8::internal