OLD | NEW |
---|---|
(Empty) | |
1 // 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-check-elimination.h" | |
29 #include "hydrogen-alias-analysis.h" | |
30 | |
31 namespace v8 { | |
32 namespace internal { | |
33 | |
34 static const int kMaxTrackedObjects = 10; | |
35 typedef UniqueSet<Map>* MapSet; | |
36 | |
37 // The main datastructure used during check elimination, which stores a | |
38 // set of known maps for each object. | |
39 class HCheckTable { | |
40 public: | |
41 explicit HCheckTable(Zone* zone) : zone_(zone) { | |
42 Kill(); | |
43 redundant_ = 0; | |
44 narrowed_ = 0; | |
45 empty_ = 0; | |
46 removed_ = 0; | |
47 compares_true_ = 0; | |
48 compares_false_ = 0; | |
49 transitions_ = 0; | |
50 } | |
51 | |
52 void ReduceCheckMaps(HCheckMaps* instr) { | |
53 HValue* object = instr->ActualValue(); | |
Michael Starzinger
2013/09/26 09:09:56
Even though HCheckMaps serves as an informative de
| |
54 int index = Find(object); | |
55 if (index >= 0) { | |
56 // entry found; | |
57 MapSet a = known_maps_[index]; | |
58 MapSet i = instr->map_set().Copy(zone_); | |
59 if (a->IsSubset(i)) { | |
60 // The first check is more strict; the second is redundant. | |
61 if (checks_[index] != NULL) { | |
62 instr->DeleteAndReplaceWith(checks_[index]); | |
63 redundant_++; | |
64 } else { | |
65 instr->DeleteAndReplaceWith(instr->value()); | |
66 removed_++; | |
67 } | |
68 return; | |
69 } | |
70 i = i->Intersect(a, zone_); | |
71 if (i->size() == 0) { | |
72 // Intersection is empty; probably megamorphic, which is likely to | |
73 // deopt anyway, so just leave things as they are. | |
74 empty_++; | |
75 } else { | |
76 // TODO(titzer): replace the first check with a more strict check. | |
77 narrowed_++; | |
78 } | |
79 } else { | |
80 // No entry; insert a new one. | |
81 Insert(object, instr, instr->map_set().Copy(zone_)); | |
82 } | |
83 } | |
84 | |
85 void ReduceCheckValue(HCheckValue* instr) { | |
86 HValue* value = instr->value(); | |
87 if (value->IsLoadNamedField()) { | |
88 HLoadNamedField* load = HLoadNamedField::cast(value); | |
89 // Match a HCheckValue(HLoadNamedField[field = map](object)) | |
90 if (IsMapAccess(load->access())) { | |
91 MapSet maps = FindMaps(load->object()->ActualValue()); | |
92 if (maps == NULL) return; | |
93 if (maps->size() == 1 && maps->Contains(instr->object())) { | |
94 // The check is redundant because it's a load of the map, | |
95 // and the map is known to be exactly the given constant. | |
Michael Starzinger
2013/09/26 09:09:56
Technically this only holds if the load has happen
| |
96 instr->DeleteAndReplaceWith(NULL); | |
97 removed_++; | |
98 } | |
99 } | |
100 } | |
101 } | |
102 | |
103 void ReduceCheckMapValue(HCheckMapValue* instr) { | |
104 if (!instr->map()->IsConstant()) return; // Nothing to learn. | |
105 | |
106 HValue* object = instr->value()->ActualValue(); | |
107 // Match a HCheckMapValue(object, HConstant(map)) | |
108 Unique<Map> map = MapConstant(instr->map()); | |
109 MapSet maps = FindMaps(object); | |
110 if (maps != NULL) { | |
111 if (maps->Contains(map)) { | |
112 if (maps->size() == 1) { | |
113 // Object is known to have exactly this map. | |
114 instr->DeleteAndReplaceWith(NULL); | |
115 removed_++; | |
116 } else { | |
117 // Only one map survives the check. | |
118 maps->Clear(); | |
119 maps->Add(map, zone_); | |
120 } | |
121 } | |
122 } else { | |
123 // No prior information. | |
124 Insert(object, map); | |
125 } | |
126 } | |
127 | |
128 void ReduceStoreNamedField(HStoreNamedField* instr) { | |
129 HValue* object = instr->object()->ActualValue(); | |
130 if (instr->has_transition()) { | |
131 // This store transitions the object to a new map. | |
132 Kill(object); | |
133 Insert(object, MapConstant(instr->transition())); | |
134 } else if (IsMapAccess(instr->access())) { | |
135 // This is a store directly to the map field of the object. | |
136 Kill(object); | |
137 if (!instr->value()->IsConstant()) return; | |
138 Insert(object, MapConstant(instr->value())); | |
139 } else if (instr->CheckGVNFlag(kChangesMaps)) { | |
140 // This store indirectly changes the map of the object. | |
141 Kill(instr->object()); | |
142 UNREACHABLE(); | |
143 } | |
144 } | |
145 | |
146 void ReduceCompareMap(HCompareMap* instr) { | |
147 MapSet maps = FindMaps(instr->value()->ActualValue()); | |
148 if (maps == NULL) return; | |
149 if (maps->Contains(instr->map())) { | |
150 // TODO(titzer): replace with goto true branch | |
151 if (maps->size() == 1) compares_true_++; | |
152 } else { | |
153 // TODO(titzer): replace with goto false branch | |
154 compares_false_++; | |
155 } | |
156 } | |
157 | |
158 void ReduceTransitionElementsKind(HTransitionElementsKind* instr) { | |
159 MapSet maps = FindMaps(instr->object()->ActualValue()); | |
160 // Can only learn more about an object that already has a known set of maps. | |
161 if (maps == NULL) return; | |
162 if (maps->Contains(instr->original_map())) { | |
163 // If the object has the original map, it will be transitioned. | |
164 maps->Remove(instr->original_map()); | |
165 maps->Add(instr->transitioned_map(), zone_); | |
166 } else { | |
167 // Object does not have the given map, thus the transition is redundant. | |
168 instr->DeleteAndReplaceWith(instr->object()); | |
169 transitions_++; | |
170 } | |
171 } | |
172 | |
173 // Kill everything in the table. | |
174 void Kill() { | |
175 memset(objects_, 0, sizeof(objects_)); | |
176 } | |
177 | |
178 // Kill everything in the table that may alias {object}. | |
179 void Kill(HValue* object) { | |
180 for (int i = 0; i < kMaxTrackedObjects; i++) { | |
181 if (objects_[i] == NULL) continue; | |
182 if (aliasing_.MayAlias(objects_[i], object)) objects_[i] = NULL; | |
183 } | |
184 } | |
185 | |
186 void Print() { | |
187 for (int i = 0; i < kMaxTrackedObjects; i++) { | |
188 if (objects_[i] == NULL) continue; | |
189 PrintF(" checkmaps-table @%d: object #%d ", i, objects_[i]->id()); | |
190 if (checks_[i] != NULL) { | |
191 PrintF("check #%d ", checks_[i]->id()); | |
192 } | |
193 MapSet list = known_maps_[i]; | |
194 PrintF("%d maps { ", list->size()); | |
195 for (int j = 0; j < list->size(); j++) { | |
196 if (j > 0) PrintF(", "); | |
197 PrintF("%" V8PRIxPTR, list->at(j).Hashcode()); | |
198 } | |
199 PrintF(" }\n"); | |
200 } | |
201 } | |
202 | |
203 void PrintStats() { | |
204 if (redundant_ > 0) PrintF(" redundant = %2d\n", redundant_); | |
205 if (removed_ > 0) PrintF(" removed = %2d\n", removed_); | |
206 if (narrowed_ > 0) PrintF(" narrowed = %2d\n", narrowed_); | |
207 if (empty_ > 0) PrintF(" empty = %2d\n", empty_); | |
208 if (compares_true_ > 0) PrintF(" cmp_true = %2d\n", compares_true_); | |
209 if (compares_false_ > 0) PrintF(" cmp_false = %2d\n", compares_false_); | |
210 if (transitions_ > 0) PrintF(" transitions = %2d\n", transitions_); | |
211 } | |
212 | |
213 private: | |
214 int Find(HValue* object) { | |
215 for (int i = 0; i < kMaxTrackedObjects; i++) { | |
216 if (objects_[i] == NULL) continue; | |
217 if (aliasing_.MustAlias(objects_[i], object)) return i; | |
218 } | |
219 return -1; | |
220 } | |
221 | |
222 MapSet FindMaps(HValue* object) { | |
223 int index = Find(object); | |
224 if (index < 0) return NULL; | |
225 return known_maps_[index]; | |
226 } | |
227 | |
228 int Insert(HValue* object, Unique<Map> map) { | |
229 MapSet list = new(zone_) UniqueSet<Map>(); | |
230 list->Add(map, zone_); | |
231 return Insert(object, NULL, list); | |
232 } | |
233 | |
234 int Insert(HValue* object, HCheckMaps* check, MapSet maps) { | |
235 for (int i = 0; i < kMaxTrackedObjects; i++) { | |
Michael Starzinger
2013/09/26 09:09:56
Can we ASSERT(Find(object) < 0) here?
| |
236 if (objects_[i] == NULL) { | |
237 objects_[i] = object; | |
238 checks_[i] = check; | |
239 known_maps_[i] = maps; | |
240 return i; | |
241 } | |
242 } | |
243 return -1; | |
244 } | |
245 | |
246 bool IsMapAccess(HObjectAccess access) { | |
247 return access.IsInobject() && access.offset() == JSObject::kMapOffset; | |
248 } | |
249 | |
250 Unique<Map> MapConstant(HValue* value) { | |
251 return HConstant::cast(value)->GetCastedUnique<Map>(); | |
252 } | |
253 | |
254 Zone* zone_; | |
255 HValue* objects_[kMaxTrackedObjects]; | |
256 HValue* checks_[kMaxTrackedObjects]; | |
257 MapSet known_maps_[kMaxTrackedObjects]; | |
258 HAliasAnalyzer aliasing_; | |
259 int redundant_; | |
260 int removed_; | |
261 int narrowed_; | |
262 int empty_; | |
263 int compares_true_; | |
264 int compares_false_; | |
265 int transitions_; | |
266 }; | |
267 | |
268 | |
269 void HCheckEliminationPhase::Run() { | |
270 for (int i = 0; i < graph()->blocks()->length(); i++) { | |
271 EliminateLocalChecks(graph()->blocks()->at(i)); | |
272 } | |
273 } | |
274 | |
275 | |
276 // For code de-uglification. | |
277 #define TRACE(x) if (FLAG_trace_check_elimination) PrintF x | |
278 | |
279 | |
280 // Eliminate checks local to a block. | |
281 void HCheckEliminationPhase::EliminateLocalChecks(HBasicBlock* block) { | |
282 HCheckTable table(zone()); | |
283 TRACE(("-- check-elim B%d ------------------------------------------------\n", | |
284 block->block_id())); | |
285 | |
286 for (HInstructionIterator it(block); !it.Done(); it.Advance()) { | |
287 bool changed = false; | |
288 HInstruction* instr = it.Current(); | |
289 | |
290 switch (instr->opcode()) { | |
291 case HValue::kCheckMaps: { | |
292 table.ReduceCheckMaps(HCheckMaps::cast(instr)); | |
293 changed = true; | |
294 break; | |
295 } | |
296 case HValue::kCheckValue: { | |
297 table.ReduceCheckValue(HCheckValue::cast(instr)); | |
298 changed = true; | |
299 break; | |
300 } | |
301 case HValue::kStoreNamedField: { | |
302 table.ReduceStoreNamedField(HStoreNamedField::cast(instr)); | |
303 changed = true; | |
304 break; | |
305 } | |
306 case HValue::kCompareMap: { | |
307 table.ReduceCompareMap(HCompareMap::cast(instr)); | |
308 changed = true; | |
309 break; | |
310 } | |
311 case HValue::kTransitionElementsKind: { | |
312 table.ReduceTransitionElementsKind( | |
313 HTransitionElementsKind::cast(instr)); | |
314 changed = true; | |
315 break; | |
316 } | |
317 case HValue::kCheckMapValue: { | |
318 table.ReduceCheckMapValue(HCheckMapValue::cast(instr)); | |
319 changed = true; | |
320 break; | |
321 } | |
322 default: { | |
323 // If the instruction changes maps uncontrollably, kill the whole town. | |
324 if (instr->CheckGVNFlag(kChangesMaps)) { | |
325 table.Kill(); | |
326 changed = true; | |
327 } | |
328 } | |
329 // Improvements possible: | |
330 // - eliminate HCheckSmi and HCheckHeapObject | |
331 } | |
332 | |
333 if (changed && FLAG_trace_check_elimination) table.Print(); | |
334 } | |
335 | |
336 if (FLAG_trace_check_elimination) table.PrintStats(); | |
337 } | |
338 | |
339 | |
340 } } // namespace v8::internal | |
OLD | NEW |