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

Side by Side Diff: src/hydrogen-bce.cc

Issue 18826002: Turn redundant bounds checks elimination into a proper HPhase. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 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 | Annotate | Revision Log
« no previous file with comments | « src/hydrogen-bce.h ('k') | tools/gyp/v8.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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-bce.h"
29
30 namespace v8 {
31 namespace internal {
32
33 // We try to "factor up" HBoundsCheck instructions towards the root of the
34 // dominator tree.
35 // For now we handle checks where the index is like "exp + int32value".
36 // If in the dominator tree we check "exp + v1" and later (dominated)
37 // "exp + v2", if v2 <= v1 we can safely remove the second check, and if
38 // v2 > v1 we can use v2 in the 1st check and again remove the second.
39 // To do so we keep a dictionary of all checks where the key if the pair
40 // "exp, length".
41 // The class BoundsCheckKey represents this key.
42 class BoundsCheckKey : public ZoneObject {
43 public:
44 HValue* IndexBase() const { return index_base_; }
45 HValue* Length() const { return length_; }
46
47 uint32_t Hash() {
48 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
49 }
50
51 static BoundsCheckKey* Create(Zone* zone,
52 HBoundsCheck* check,
53 int32_t* offset) {
54 if (!check->index()->representation().IsSmiOrInteger32()) return NULL;
55
56 HValue* index_base = NULL;
57 HConstant* constant = NULL;
58 bool is_sub = false;
59
60 if (check->index()->IsAdd()) {
61 HAdd* index = HAdd::cast(check->index());
62 if (index->left()->IsConstant()) {
63 constant = HConstant::cast(index->left());
64 index_base = index->right();
65 } else if (index->right()->IsConstant()) {
66 constant = HConstant::cast(index->right());
67 index_base = index->left();
68 }
69 } else if (check->index()->IsSub()) {
70 HSub* index = HSub::cast(check->index());
71 is_sub = true;
72 if (index->left()->IsConstant()) {
73 constant = HConstant::cast(index->left());
74 index_base = index->right();
75 } else if (index->right()->IsConstant()) {
76 constant = HConstant::cast(index->right());
77 index_base = index->left();
78 }
79 }
80
81 if (constant != NULL && constant->HasInteger32Value()) {
82 *offset = is_sub ? - constant->Integer32Value()
83 : constant->Integer32Value();
84 } else {
85 *offset = 0;
86 index_base = check->index();
87 }
88
89 return new(zone) BoundsCheckKey(index_base, check->length());
90 }
91
92 private:
93 BoundsCheckKey(HValue* index_base, HValue* length)
94 : index_base_(index_base),
95 length_(length) { }
96
97 HValue* index_base_;
98 HValue* length_;
99
100 DISALLOW_COPY_AND_ASSIGN(BoundsCheckKey);
101 };
102
103
104 // Data about each HBoundsCheck that can be eliminated or moved.
105 // It is the "value" in the dictionary indexed by "base-index, length"
106 // (the key is BoundsCheckKey).
107 // We scan the code with a dominator tree traversal.
108 // Traversing the dominator tree we keep a stack (implemented as a singly
109 // linked list) of "data" for each basic block that contains a relevant check
110 // with the same key (the dictionary holds the head of the list).
111 // We also keep all the "data" created for a given basic block in a list, and
112 // use it to "clean up" the dictionary when backtracking in the dominator tree
113 // traversal.
114 // Doing this each dictionary entry always directly points to the check that
115 // is dominating the code being examined now.
116 // We also track the current "offset" of the index expression and use it to
117 // decide if any check is already "covered" (so it can be removed) or not.
118 class BoundsCheckBbData: public ZoneObject {
119 public:
120 BoundsCheckKey* Key() const { return key_; }
121 int32_t LowerOffset() const { return lower_offset_; }
122 int32_t UpperOffset() const { return upper_offset_; }
123 HBasicBlock* BasicBlock() const { return basic_block_; }
124 HBoundsCheck* LowerCheck() const { return lower_check_; }
125 HBoundsCheck* UpperCheck() const { return upper_check_; }
126 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
127 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
128
129 bool OffsetIsCovered(int32_t offset) const {
130 return offset >= LowerOffset() && offset <= UpperOffset();
131 }
132
133 bool HasSingleCheck() { return lower_check_ == upper_check_; }
134
135 // The goal of this method is to modify either upper_offset_ or
136 // lower_offset_ so that also new_offset is covered (the covered
137 // range grows).
138 //
139 // The precondition is that new_check follows UpperCheck() and
140 // LowerCheck() in the same basic block, and that new_offset is not
141 // covered (otherwise we could simply remove new_check).
142 //
143 // If HasSingleCheck() is true then new_check is added as "second check"
144 // (either upper or lower; note that HasSingleCheck() becomes false).
145 // Otherwise one of the current checks is modified so that it also covers
146 // new_offset, and new_check is removed.
147 //
148 // If the check cannot be modified because the context is unknown it
149 // returns false, otherwise it returns true.
150 bool CoverCheck(HBoundsCheck* new_check,
151 int32_t new_offset) {
152 ASSERT(new_check->index()->representation().IsSmiOrInteger32());
153 bool keep_new_check = false;
154
155 if (new_offset > upper_offset_) {
156 upper_offset_ = new_offset;
157 if (HasSingleCheck()) {
158 keep_new_check = true;
159 upper_check_ = new_check;
160 } else {
161 bool result = BuildOffsetAdd(upper_check_,
162 &added_upper_index_,
163 &added_upper_offset_,
164 Key()->IndexBase(),
165 new_check->index()->representation(),
166 new_offset);
167 if (!result) return false;
168 upper_check_->ReplaceAllUsesWith(upper_check_->index());
169 upper_check_->SetOperandAt(0, added_upper_index_);
170 }
171 } else if (new_offset < lower_offset_) {
172 lower_offset_ = new_offset;
173 if (HasSingleCheck()) {
174 keep_new_check = true;
175 lower_check_ = new_check;
176 } else {
177 bool result = BuildOffsetAdd(lower_check_,
178 &added_lower_index_,
179 &added_lower_offset_,
180 Key()->IndexBase(),
181 new_check->index()->representation(),
182 new_offset);
183 if (!result) return false;
184 lower_check_->ReplaceAllUsesWith(lower_check_->index());
185 lower_check_->SetOperandAt(0, added_lower_index_);
186 }
187 } else {
188 ASSERT(false);
189 }
190
191 if (!keep_new_check) {
192 new_check->DeleteAndReplaceWith(new_check->ActualValue());
193 }
194
195 return true;
196 }
197
198 void RemoveZeroOperations() {
199 RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
200 RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
201 }
202
203 BoundsCheckBbData(BoundsCheckKey* key,
204 int32_t lower_offset,
205 int32_t upper_offset,
206 HBasicBlock* bb,
207 HBoundsCheck* lower_check,
208 HBoundsCheck* upper_check,
209 BoundsCheckBbData* next_in_bb,
210 BoundsCheckBbData* father_in_dt)
211 : key_(key),
212 lower_offset_(lower_offset),
213 upper_offset_(upper_offset),
214 basic_block_(bb),
215 lower_check_(lower_check),
216 upper_check_(upper_check),
217 added_lower_index_(NULL),
218 added_lower_offset_(NULL),
219 added_upper_index_(NULL),
220 added_upper_offset_(NULL),
221 next_in_bb_(next_in_bb),
222 father_in_dt_(father_in_dt) { }
223
224 private:
225 BoundsCheckKey* key_;
226 int32_t lower_offset_;
227 int32_t upper_offset_;
228 HBasicBlock* basic_block_;
229 HBoundsCheck* lower_check_;
230 HBoundsCheck* upper_check_;
231 HInstruction* added_lower_index_;
232 HConstant* added_lower_offset_;
233 HInstruction* added_upper_index_;
234 HConstant* added_upper_offset_;
235 BoundsCheckBbData* next_in_bb_;
236 BoundsCheckBbData* father_in_dt_;
237
238 // Given an existing add instruction and a bounds check it tries to
239 // find the current context (either of the add or of the check index).
240 HValue* IndexContext(HInstruction* add, HBoundsCheck* check) {
241 if (add != NULL && add->IsAdd()) {
242 return HAdd::cast(add)->context();
243 }
244 if (check->index()->IsBinaryOperation()) {
245 return HBinaryOperation::cast(check->index())->context();
246 }
247 return NULL;
248 }
249
250 // This function returns false if it cannot build the add because the
251 // current context cannot be determined.
252 bool BuildOffsetAdd(HBoundsCheck* check,
253 HInstruction** add,
254 HConstant** constant,
255 HValue* original_value,
256 Representation representation,
257 int32_t new_offset) {
258 HValue* index_context = IndexContext(*add, check);
259 if (index_context == NULL) return false;
260
261 HConstant* new_constant = new(BasicBlock()->zone()) HConstant(
262 new_offset, representation);
263 if (*add == NULL) {
264 new_constant->InsertBefore(check);
265 (*add) = HAdd::New(
266 BasicBlock()->zone(), index_context, original_value, new_constant);
267 (*add)->AssumeRepresentation(representation);
268 (*add)->InsertBefore(check);
269 } else {
270 new_constant->InsertBefore(*add);
271 (*constant)->DeleteAndReplaceWith(new_constant);
272 }
273 *constant = new_constant;
274 return true;
275 }
276
277 void RemoveZeroAdd(HInstruction** add, HConstant** constant) {
278 if (*add != NULL && (*add)->IsAdd() && (*constant)->Integer32Value() == 0) {
279 (*add)->DeleteAndReplaceWith(HAdd::cast(*add)->left());
280 (*constant)->DeleteAndReplaceWith(NULL);
281 }
282 }
283
284 DISALLOW_COPY_AND_ASSIGN(BoundsCheckBbData);
285 };
286
287
288 static bool BoundsCheckKeyMatch(void* key1, void* key2) {
289 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
290 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
291 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
292 }
293
294
295 BoundsCheckTable::BoundsCheckTable(Zone* zone)
296 : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
297 ZoneAllocationPolicy(zone)) { }
298
299
300 BoundsCheckBbData** BoundsCheckTable::LookupOrInsert(BoundsCheckKey* key,
301 Zone* zone) {
302 return reinterpret_cast<BoundsCheckBbData**>(
303 &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
304 }
305
306
307 void BoundsCheckTable::Insert(BoundsCheckKey* key,
308 BoundsCheckBbData* data,
309 Zone* zone) {
310 Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
311 }
312
313
314 void BoundsCheckTable::Delete(BoundsCheckKey* key) {
315 Remove(key, key->Hash());
316 }
317
318
319 // Eliminates checks in bb and recursively in the dominated blocks.
320 // Also replace the results of check instructions with the original value, if
321 // the result is used. This is safe now, since we don't do code motion after
322 // this point. It enables better register allocation since the value produced
323 // by check instructions is really a copy of the original value.
324 void HBoundsCheckEliminationPhase::EliminateRedundantBoundsChecks(
325 HBasicBlock* bb) {
326 BoundsCheckBbData* bb_data_list = NULL;
327
328 for (HInstructionIterator it(bb); !it.Done(); it.Advance()) {
329 HInstruction* i = it.Current();
330 if (!i->IsBoundsCheck()) continue;
331
332 HBoundsCheck* check = HBoundsCheck::cast(i);
333 int32_t offset;
334 BoundsCheckKey* key =
335 BoundsCheckKey::Create(zone(), check, &offset);
336 if (key == NULL) continue;
337 BoundsCheckBbData** data_p = table_.LookupOrInsert(key, zone());
338 BoundsCheckBbData* data = *data_p;
339 if (data == NULL) {
340 bb_data_list = new(zone()) BoundsCheckBbData(key,
341 offset,
342 offset,
343 bb,
344 check,
345 check,
346 bb_data_list,
347 NULL);
348 *data_p = bb_data_list;
349 } else if (data->OffsetIsCovered(offset)) {
350 check->DeleteAndReplaceWith(check->ActualValue());
351 } else if (data->BasicBlock() != bb ||
352 !data->CoverCheck(check, offset)) {
353 // If the check is in the current BB we try to modify it by calling
354 // "CoverCheck", but if also that fails we record the current offsets
355 // in a new data instance because from now on they are covered.
356 int32_t new_lower_offset = offset < data->LowerOffset()
357 ? offset
358 : data->LowerOffset();
359 int32_t new_upper_offset = offset > data->UpperOffset()
360 ? offset
361 : data->UpperOffset();
362 bb_data_list = new(zone()) BoundsCheckBbData(key,
363 new_lower_offset,
364 new_upper_offset,
365 bb,
366 data->LowerCheck(),
367 data->UpperCheck(),
368 bb_data_list,
369 data);
370 table_.Insert(key, bb_data_list, zone());
371 }
372 }
373
374 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
375 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i));
376 }
377
378 for (BoundsCheckBbData* data = bb_data_list;
379 data != NULL;
380 data = data->NextInBasicBlock()) {
381 data->RemoveZeroOperations();
382 if (data->FatherInDominatorTree()) {
383 table_.Insert(data->Key(), data->FatherInDominatorTree(), zone());
384 } else {
385 table_.Delete(data->Key());
386 }
387 }
388 }
389
390 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/hydrogen-bce.h ('k') | tools/gyp/v8.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698