blob: 7d0e228e8eeae3e88bc930be8d518966713afa99 [file] [log] [blame]
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001// Copyright 2012 the V8 project authors. All rights reserved.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002// 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
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000028#include "v8.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000029#include "hydrogen.h"
30
31#include "codegen.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000032#include "full-codegen.h"
33#include "hashmap.h"
34#include "lithium-allocator.h"
35#include "parser.h"
ricow@chromium.org4f693d62011-07-04 14:01:31 +000036#include "scopeinfo.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000037#include "scopes.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000038#include "stub-cache.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000039
40#if V8_TARGET_ARCH_IA32
41#include "ia32/lithium-codegen-ia32.h"
42#elif V8_TARGET_ARCH_X64
43#include "x64/lithium-codegen-x64.h"
44#elif V8_TARGET_ARCH_ARM
45#include "arm/lithium-codegen-arm.h"
lrn@chromium.org7516f052011-03-30 08:52:27 +000046#elif V8_TARGET_ARCH_MIPS
47#include "mips/lithium-codegen-mips.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000048#else
49#error Unsupported target architecture.
50#endif
51
52namespace v8 {
53namespace internal {
54
55HBasicBlock::HBasicBlock(HGraph* graph)
56 : block_id_(graph->GetNextBlockID()),
57 graph_(graph),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000058 phis_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000059 first_(NULL),
60 last_(NULL),
61 end_(NULL),
62 loop_information_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000063 predecessors_(2, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000064 dominator_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000065 dominated_blocks_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000066 last_environment_(NULL),
67 argument_count_(-1),
68 first_instruction_index_(-1),
69 last_instruction_index_(-1),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000070 deleted_phis_(4, graph->zone()),
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000071 parent_loop_header_(NULL),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000072 is_inline_return_target_(false),
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000073 is_deoptimizing_(false),
74 dominates_loop_successors_(false) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000075
76
77void HBasicBlock::AttachLoopInformation() {
78 ASSERT(!IsLoopHeader());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000079 loop_information_ = new(zone()) HLoopInformation(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000080}
81
82
83void HBasicBlock::DetachLoopInformation() {
84 ASSERT(IsLoopHeader());
85 loop_information_ = NULL;
86}
87
88
89void HBasicBlock::AddPhi(HPhi* phi) {
90 ASSERT(!IsStartBlock());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000091 phis_.Add(phi, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000092 phi->SetBlock(this);
93}
94
95
96void HBasicBlock::RemovePhi(HPhi* phi) {
97 ASSERT(phi->block() == this);
98 ASSERT(phis_.Contains(phi));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000099 ASSERT(phi->HasNoUses() || !phi->is_live());
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000100 phi->Kill();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000101 phis_.RemoveElement(phi);
102 phi->SetBlock(NULL);
103}
104
105
106void HBasicBlock::AddInstruction(HInstruction* instr) {
107 ASSERT(!IsStartBlock() || !IsFinished());
108 ASSERT(!instr->IsLinked());
109 ASSERT(!IsFinished());
110 if (first_ == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000111 HBlockEntry* entry = new(zone()) HBlockEntry();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000112 entry->InitializeAsFirst(this);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000113 first_ = last_ = entry;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000114 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000115 instr->InsertAfter(last_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000116}
117
118
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000119HDeoptimize* HBasicBlock::CreateDeoptimize(
120 HDeoptimize::UseEnvironment has_uses) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000121 ASSERT(HasEnvironment());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000122 if (has_uses == HDeoptimize::kNoUses)
123 return new(zone()) HDeoptimize(0, zone());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000124
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000125 HEnvironment* environment = last_environment();
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000126 HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000127 for (int i = 0; i < environment->length(); i++) {
128 HValue* val = environment->values()->at(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000129 instr->AddEnvironmentValue(val, zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000130 }
131
132 return instr;
133}
134
135
ager@chromium.org04921a82011-06-27 13:21:41 +0000136HSimulate* HBasicBlock::CreateSimulate(int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000137 ASSERT(HasEnvironment());
138 HEnvironment* environment = last_environment();
ager@chromium.org04921a82011-06-27 13:21:41 +0000139 ASSERT(ast_id == AstNode::kNoNumber ||
140 environment->closure()->shared()->VerifyBailoutId(ast_id));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000141
142 int push_count = environment->push_count();
143 int pop_count = environment->pop_count();
144
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000145 HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000146 for (int i = push_count - 1; i >= 0; --i) {
147 instr->AddPushedValue(environment->ExpressionStackAt(i));
148 }
149 for (int i = 0; i < environment->assigned_variables()->length(); ++i) {
150 int index = environment->assigned_variables()->at(i);
151 instr->AddAssignedValue(index, environment->Lookup(index));
152 }
153 environment->ClearHistory();
154 return instr;
155}
156
157
158void HBasicBlock::Finish(HControlInstruction* end) {
159 ASSERT(!IsFinished());
160 AddInstruction(end);
161 end_ = end;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000162 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
163 it.Current()->RegisterPredecessor(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000164 }
165}
166
167
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000168void HBasicBlock::Goto(HBasicBlock* block, FunctionState* state) {
169 bool drop_extra = state != NULL && state->drop_extra();
170 bool arguments_pushed = state != NULL && state->arguments_pushed();
171
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000172 if (block->IsInlineReturnTarget()) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000173 AddInstruction(new(zone()) HLeaveInlined(arguments_pushed));
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000174 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000175 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000176
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000177 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000178 HGoto* instr = new(zone()) HGoto(block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000179 Finish(instr);
180}
181
182
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000183void HBasicBlock::AddLeaveInlined(HValue* return_value,
184 HBasicBlock* target,
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000185 FunctionState* state) {
186 bool drop_extra = state != NULL && state->drop_extra();
187 bool arguments_pushed = state != NULL && state->arguments_pushed();
188
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000189 ASSERT(target->IsInlineReturnTarget());
190 ASSERT(return_value != NULL);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000191 AddInstruction(new(zone()) HLeaveInlined(arguments_pushed));
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000192 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000193 last_environment()->Push(return_value);
194 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000195 HGoto* instr = new(zone()) HGoto(target);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000196 Finish(instr);
197}
198
199
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000200void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
201 ASSERT(!HasEnvironment());
202 ASSERT(first() == NULL);
203 UpdateEnvironment(env);
204}
205
206
ager@chromium.org04921a82011-06-27 13:21:41 +0000207void HBasicBlock::SetJoinId(int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000208 int length = predecessors_.length();
209 ASSERT(length > 0);
210 for (int i = 0; i < length; i++) {
211 HBasicBlock* predecessor = predecessors_[i];
212 ASSERT(predecessor->end()->IsGoto());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000213 HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000214 // We only need to verify the ID once.
215 ASSERT(i != 0 ||
216 predecessor->last_environment()->closure()->shared()
ager@chromium.org04921a82011-06-27 13:21:41 +0000217 ->VerifyBailoutId(ast_id));
218 simulate->set_ast_id(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000219 }
220}
221
222
223bool HBasicBlock::Dominates(HBasicBlock* other) const {
224 HBasicBlock* current = other->dominator();
225 while (current != NULL) {
226 if (current == this) return true;
227 current = current->dominator();
228 }
229 return false;
230}
231
232
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000233int HBasicBlock::LoopNestingDepth() const {
234 const HBasicBlock* current = this;
235 int result = (current->IsLoopHeader()) ? 1 : 0;
236 while (current->parent_loop_header() != NULL) {
237 current = current->parent_loop_header();
238 result++;
239 }
240 return result;
241}
242
243
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000244void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
245 ASSERT(IsLoopHeader());
246
247 SetJoinId(stmt->EntryId());
248 if (predecessors()->length() == 1) {
249 // This is a degenerated loop.
250 DetachLoopInformation();
251 return;
252 }
253
254 // Only the first entry into the loop is from outside the loop. All other
255 // entries must be back edges.
256 for (int i = 1; i < predecessors()->length(); ++i) {
257 loop_information()->RegisterBackEdge(predecessors()->at(i));
258 }
259}
260
261
262void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
danno@chromium.org160a7b02011-04-18 15:51:38 +0000263 if (HasPredecessor()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000264 // Only loop header blocks can have a predecessor added after
265 // instructions have been added to the block (they have phis for all
266 // values in the environment, these phis may be eliminated later).
267 ASSERT(IsLoopHeader() || first_ == NULL);
268 HEnvironment* incoming_env = pred->last_environment();
269 if (IsLoopHeader()) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000270 ASSERT(phis()->length() == incoming_env->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000271 for (int i = 0; i < phis_.length(); ++i) {
272 phis_[i]->AddInput(incoming_env->values()->at(i));
273 }
274 } else {
275 last_environment()->AddIncomingEdge(this, pred->last_environment());
276 }
277 } else if (!HasEnvironment() && !IsFinished()) {
278 ASSERT(!IsLoopHeader());
279 SetInitialEnvironment(pred->last_environment()->Copy());
280 }
281
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000282 predecessors_.Add(pred, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000283}
284
285
286void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
287 ASSERT(!dominated_blocks_.Contains(block));
288 // Keep the list of dominated blocks sorted such that if there is two
289 // succeeding block in this list, the predecessor is before the successor.
290 int index = 0;
291 while (index < dominated_blocks_.length() &&
292 dominated_blocks_[index]->block_id() < block->block_id()) {
293 ++index;
294 }
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000295 dominated_blocks_.InsertAt(index, block, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000296}
297
298
299void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
300 if (dominator_ == NULL) {
301 dominator_ = other;
302 other->AddDominatedBlock(this);
303 } else if (other->dominator() != NULL) {
304 HBasicBlock* first = dominator_;
305 HBasicBlock* second = other;
306
307 while (first != second) {
308 if (first->block_id() > second->block_id()) {
309 first = first->dominator();
310 } else {
311 second = second->dominator();
312 }
313 ASSERT(first != NULL && second != NULL);
314 }
315
316 if (dominator_ != first) {
317 ASSERT(dominator_->dominated_blocks_.Contains(this));
318 dominator_->dominated_blocks_.RemoveElement(this);
319 dominator_ = first;
320 first->AddDominatedBlock(this);
321 }
322 }
323}
324
325
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000326void HBasicBlock::AssignLoopSuccessorDominators() {
327 // Mark blocks that dominate all subsequent reachable blocks inside their
328 // loop. Exploit the fact that blocks are sorted in reverse post order. When
329 // the loop is visited in increasing block id order, if the number of
330 // non-loop-exiting successor edges at the dominator_candidate block doesn't
331 // exceed the number of previously encountered predecessor edges, there is no
332 // path from the loop header to any block with higher id that doesn't go
333 // through the dominator_candidate block. In this case, the
334 // dominator_candidate block is guaranteed to dominate all blocks reachable
335 // from it with higher ids.
336 HBasicBlock* last = loop_information()->GetLastBackEdge();
337 int outstanding_successors = 1; // one edge from the pre-header
338 // Header always dominates everything.
339 MarkAsLoopSuccessorDominator();
340 for (int j = block_id(); j <= last->block_id(); ++j) {
341 HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
342 for (HPredecessorIterator it(dominator_candidate); !it.Done();
343 it.Advance()) {
344 HBasicBlock* predecessor = it.Current();
345 // Don't count back edges.
346 if (predecessor->block_id() < dominator_candidate->block_id()) {
347 outstanding_successors--;
348 }
349 }
350
351 // If more successors than predecessors have been seen in the loop up to
352 // now, it's not possible to guarantee that the current block dominates
353 // all of the blocks with higher IDs. In this case, assume conservatively
354 // that those paths through loop that don't go through the current block
355 // contain all of the loop's dependencies. Also be careful to record
356 // dominator information about the current loop that's being processed,
357 // and not nested loops, which will be processed when
358 // AssignLoopSuccessorDominators gets called on their header.
359 ASSERT(outstanding_successors >= 0);
360 HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header();
361 if (outstanding_successors == 0 &&
362 (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) {
363 dominator_candidate->MarkAsLoopSuccessorDominator();
364 }
365 HControlInstruction* end = dominator_candidate->end();
366 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
367 HBasicBlock* successor = it.Current();
368 // Only count successors that remain inside the loop and don't loop back
369 // to a loop header.
370 if (successor->block_id() > dominator_candidate->block_id() &&
371 successor->block_id() <= last->block_id()) {
372 // Backwards edges must land on loop headers.
373 ASSERT(successor->block_id() > dominator_candidate->block_id() ||
374 successor->IsLoopHeader());
375 outstanding_successors++;
376 }
377 }
378 }
379}
380
381
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000382int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
383 for (int i = 0; i < predecessors_.length(); ++i) {
384 if (predecessors_[i] == predecessor) return i;
385 }
386 UNREACHABLE();
387 return -1;
388}
389
390
391#ifdef DEBUG
392void HBasicBlock::Verify() {
393 // Check that every block is finished.
394 ASSERT(IsFinished());
395 ASSERT(block_id() >= 0);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000396
397 // Check that the incoming edges are in edge split form.
398 if (predecessors_.length() > 1) {
399 for (int i = 0; i < predecessors_.length(); ++i) {
400 ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
401 }
402 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000403}
404#endif
405
406
407void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000408 this->back_edges_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000409 AddBlock(block);
410}
411
412
413HBasicBlock* HLoopInformation::GetLastBackEdge() const {
414 int max_id = -1;
415 HBasicBlock* result = NULL;
416 for (int i = 0; i < back_edges_.length(); ++i) {
417 HBasicBlock* cur = back_edges_[i];
418 if (cur->block_id() > max_id) {
419 max_id = cur->block_id();
420 result = cur;
421 }
422 }
423 return result;
424}
425
426
427void HLoopInformation::AddBlock(HBasicBlock* block) {
428 if (block == loop_header()) return;
429 if (block->parent_loop_header() == loop_header()) return;
430 if (block->parent_loop_header() != NULL) {
431 AddBlock(block->parent_loop_header());
432 } else {
433 block->set_parent_loop_header(loop_header());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000434 blocks_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000435 for (int i = 0; i < block->predecessors()->length(); ++i) {
436 AddBlock(block->predecessors()->at(i));
437 }
438 }
439}
440
441
442#ifdef DEBUG
443
444// Checks reachability of the blocks in this graph and stores a bit in
445// the BitVector "reachable()" for every block that can be reached
446// from the start block of the graph. If "dont_visit" is non-null, the given
447// block is treated as if it would not be part of the graph. "visited_count()"
448// returns the number of reachable blocks.
449class ReachabilityAnalyzer BASE_EMBEDDED {
450 public:
451 ReachabilityAnalyzer(HBasicBlock* entry_block,
452 int block_count,
453 HBasicBlock* dont_visit)
454 : visited_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000455 stack_(16, entry_block->zone()),
456 reachable_(block_count, entry_block->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000457 dont_visit_(dont_visit) {
458 PushBlock(entry_block);
459 Analyze();
460 }
461
462 int visited_count() const { return visited_count_; }
463 const BitVector* reachable() const { return &reachable_; }
464
465 private:
466 void PushBlock(HBasicBlock* block) {
467 if (block != NULL && block != dont_visit_ &&
468 !reachable_.Contains(block->block_id())) {
469 reachable_.Add(block->block_id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000470 stack_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000471 visited_count_++;
472 }
473 }
474
475 void Analyze() {
476 while (!stack_.is_empty()) {
477 HControlInstruction* end = stack_.RemoveLast()->end();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000478 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
479 PushBlock(it.Current());
480 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000481 }
482 }
483
484 int visited_count_;
485 ZoneList<HBasicBlock*> stack_;
486 BitVector reachable_;
487 HBasicBlock* dont_visit_;
488};
489
490
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000491void HGraph::Verify(bool do_full_verify) const {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000492 for (int i = 0; i < blocks_.length(); i++) {
493 HBasicBlock* block = blocks_.at(i);
494
495 block->Verify();
496
497 // Check that every block contains at least one node and that only the last
498 // node is a control instruction.
499 HInstruction* current = block->first();
500 ASSERT(current != NULL && current->IsBlockEntry());
501 while (current != NULL) {
502 ASSERT((current->next() == NULL) == current->IsControlInstruction());
503 ASSERT(current->block() == block);
504 current->Verify();
505 current = current->next();
506 }
507
508 // Check that successors are correctly set.
509 HBasicBlock* first = block->end()->FirstSuccessor();
510 HBasicBlock* second = block->end()->SecondSuccessor();
511 ASSERT(second == NULL || first != NULL);
512
513 // Check that the predecessor array is correct.
514 if (first != NULL) {
515 ASSERT(first->predecessors()->Contains(block));
516 if (second != NULL) {
517 ASSERT(second->predecessors()->Contains(block));
518 }
519 }
520
521 // Check that phis have correct arguments.
522 for (int j = 0; j < block->phis()->length(); j++) {
523 HPhi* phi = block->phis()->at(j);
524 phi->Verify();
525 }
526
527 // Check that all join blocks have predecessors that end with an
528 // unconditional goto and agree on their environment node id.
529 if (block->predecessors()->length() >= 2) {
530 int id = block->predecessors()->first()->last_environment()->ast_id();
531 for (int k = 0; k < block->predecessors()->length(); k++) {
532 HBasicBlock* predecessor = block->predecessors()->at(k);
533 ASSERT(predecessor->end()->IsGoto());
534 ASSERT(predecessor->last_environment()->ast_id() == id);
535 }
536 }
537 }
538
539 // Check special property of first block to have no predecessors.
540 ASSERT(blocks_.at(0)->predecessors()->is_empty());
541
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000542 if (do_full_verify) {
543 // Check that the graph is fully connected.
544 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
545 ASSERT(analyzer.visited_count() == blocks_.length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000546
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000547 // Check that entry block dominator is NULL.
548 ASSERT(entry_block_->dominator() == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000549
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000550 // Check dominators.
551 for (int i = 0; i < blocks_.length(); ++i) {
552 HBasicBlock* block = blocks_.at(i);
553 if (block->dominator() == NULL) {
554 // Only start block may have no dominator assigned to.
555 ASSERT(i == 0);
556 } else {
557 // Assert that block is unreachable if dominator must not be visited.
558 ReachabilityAnalyzer dominator_analyzer(entry_block_,
559 blocks_.length(),
560 block->dominator());
561 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
562 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000563 }
564 }
565}
566
567#endif
568
569
570HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000571 Handle<Object> value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000572 if (!pointer->is_set()) {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000573 HConstant* constant = new(zone()) HConstant(value,
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000574 Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000575 constant->InsertAfter(GetConstantUndefined());
576 pointer->set(constant);
577 }
578 return pointer->get();
579}
580
581
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000582HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer,
583 int32_t value) {
584 if (!pointer->is_set()) {
585 HConstant* constant =
586 new(zone()) HConstant(value, Representation::Integer32());
587 constant->InsertAfter(GetConstantUndefined());
588 pointer->set(constant);
589 }
590 return pointer->get();
591}
592
593
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000594HConstant* HGraph::GetConstant1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000595 return GetConstantInt32(&constant_1_, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000596}
597
598
599HConstant* HGraph::GetConstantMinus1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000600 return GetConstantInt32(&constant_minus1_, -1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000601}
602
603
604HConstant* HGraph::GetConstantTrue() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000605 return GetConstant(&constant_true_, isolate()->factory()->true_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000606}
607
608
609HConstant* HGraph::GetConstantFalse() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000610 return GetConstant(&constant_false_, isolate()->factory()->false_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000611}
612
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000613
614HConstant* HGraph::GetConstantHole() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000615 return GetConstant(&constant_hole_, isolate()->factory()->the_hole_value());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000616}
617
618
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000619HGraphBuilder::HGraphBuilder(CompilationInfo* info,
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000620 TypeFeedbackOracle* oracle)
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000621 : function_state_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +0000622 initial_function_state_(this, info, oracle, NORMAL_RETURN),
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000623 ast_context_(NULL),
624 break_scope_(NULL),
625 graph_(NULL),
626 current_block_(NULL),
627 inlined_count_(0),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000628 globals_(10, info->zone()),
629 zone_(info->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000630 inline_bailout_(false) {
631 // This is not initialized in the initializer list because the
632 // constructor for the initial state relies on function_state_ == NULL
633 // to know it's the initial state.
634 function_state_= &initial_function_state_;
635}
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000636
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000637HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
638 HBasicBlock* second,
639 int join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000640 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000641 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000642 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000643 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000644 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000645 HBasicBlock* join_block = graph_->CreateBasicBlock();
646 first->Goto(join_block);
647 second->Goto(join_block);
648 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000649 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000650 }
651}
652
653
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000654HBasicBlock* HGraphBuilder::JoinContinue(IterationStatement* statement,
655 HBasicBlock* exit_block,
656 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000657 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000658 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000659 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000660 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000661 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000662 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000663}
664
665
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000666HBasicBlock* HGraphBuilder::CreateLoop(IterationStatement* statement,
667 HBasicBlock* loop_entry,
668 HBasicBlock* body_exit,
669 HBasicBlock* loop_successor,
670 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +0000671 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000672 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000673 if (break_block != NULL) {
674 if (loop_successor != NULL) loop_successor->Goto(break_block);
675 break_block->SetJoinId(statement->ExitId());
676 return break_block;
677 }
678 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000679}
680
681
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000682void HBasicBlock::FinishExit(HControlInstruction* instruction) {
683 Finish(instruction);
684 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000685}
686
687
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000688HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000689 : isolate_(info->isolate()),
690 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000691 entry_block_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000692 blocks_(8, info->zone()),
693 values_(16, info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000694 phi_list_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000695 info_(info),
696 zone_(info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000697 is_recursive_(false) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000698 start_environment_ =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000699 new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000700 start_environment_->set_ast_id(AstNode::kFunctionEntryId);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000701 entry_block_ = CreateBasicBlock();
702 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000703}
704
705
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000706HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000707 HBasicBlock* result = new(zone()) HBasicBlock(this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000708 blocks_.Add(result, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000709 return result;
710}
711
712
713void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000714 if (!FLAG_use_canonicalizing) return;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000715 HPhase phase("H_Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000716 for (int i = 0; i < blocks()->length(); ++i) {
717 HInstruction* instr = blocks()->at(i)->first();
718 while (instr != NULL) {
719 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000720 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000721 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000722 }
723 }
724}
725
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000726// Block ordering was implemented with two mutually recursive methods,
727// HGraph::Postorder and HGraph::PostorderLoopBlocks.
728// The recursion could lead to stack overflow so the algorithm has been
729// implemented iteratively.
730// At a high level the algorithm looks like this:
731//
732// Postorder(block, loop_header) : {
733// if (block has already been visited or is of another loop) return;
734// mark block as visited;
735// if (block is a loop header) {
736// VisitLoopMembers(block, loop_header);
737// VisitSuccessorsOfLoopHeader(block);
738// } else {
739// VisitSuccessors(block)
740// }
741// put block in result list;
742// }
743//
744// VisitLoopMembers(block, outer_loop_header) {
745// foreach (block b in block loop members) {
746// VisitSuccessorsOfLoopMember(b, outer_loop_header);
747// if (b is loop header) VisitLoopMembers(b);
748// }
749// }
750//
751// VisitSuccessorsOfLoopMember(block, outer_loop_header) {
752// foreach (block b in block successors) Postorder(b, outer_loop_header)
753// }
754//
755// VisitSuccessorsOfLoopHeader(block) {
756// foreach (block b in block successors) Postorder(b, block)
757// }
758//
759// VisitSuccessors(block, loop_header) {
760// foreach (block b in block successors) Postorder(b, loop_header)
761// }
762//
763// The ordering is started calling Postorder(entry, NULL).
764//
765// Each instance of PostorderProcessor represents the "stack frame" of the
766// recursion, and particularly keeps the state of the loop (iteration) of the
767// "Visit..." function it represents.
768// To recycle memory we keep all the frames in a double linked list but
769// this means that we cannot use constructors to initialize the frames.
770//
771class PostorderProcessor : public ZoneObject {
772 public:
773 // Back link (towards the stack bottom).
774 PostorderProcessor* parent() {return father_; }
775 // Forward link (towards the stack top).
776 PostorderProcessor* child() {return child_; }
777 HBasicBlock* block() { return block_; }
778 HLoopInformation* loop() { return loop_; }
779 HBasicBlock* loop_header() { return loop_header_; }
780
781 static PostorderProcessor* CreateEntryProcessor(Zone* zone,
782 HBasicBlock* block,
783 BitVector* visited) {
784 PostorderProcessor* result = new(zone) PostorderProcessor(NULL);
785 return result->SetupSuccessors(zone, block, NULL, visited);
786 }
787
788 PostorderProcessor* PerformStep(Zone* zone,
789 BitVector* visited,
790 ZoneList<HBasicBlock*>* order) {
791 PostorderProcessor* next =
792 PerformNonBacktrackingStep(zone, visited, order);
793 if (next != NULL) {
794 return next;
795 } else {
796 return Backtrack(zone, visited, order);
797 }
798 }
799
800 private:
801 explicit PostorderProcessor(PostorderProcessor* father)
802 : father_(father), child_(NULL), successor_iterator(NULL) { }
803
804 // Each enum value states the cycle whose state is kept by this instance.
805 enum LoopKind {
806 NONE,
807 SUCCESSORS,
808 SUCCESSORS_OF_LOOP_HEADER,
809 LOOP_MEMBERS,
810 SUCCESSORS_OF_LOOP_MEMBER
811 };
812
813 // Each "Setup..." method is like a constructor for a cycle state.
814 PostorderProcessor* SetupSuccessors(Zone* zone,
815 HBasicBlock* block,
816 HBasicBlock* loop_header,
817 BitVector* visited) {
818 if (block == NULL || visited->Contains(block->block_id()) ||
819 block->parent_loop_header() != loop_header) {
820 kind_ = NONE;
821 block_ = NULL;
822 loop_ = NULL;
823 loop_header_ = NULL;
824 return this;
825 } else {
826 block_ = block;
827 loop_ = NULL;
828 visited->Add(block->block_id());
829
830 if (block->IsLoopHeader()) {
831 kind_ = SUCCESSORS_OF_LOOP_HEADER;
832 loop_header_ = block;
833 InitializeSuccessors();
834 PostorderProcessor* result = Push(zone);
835 return result->SetupLoopMembers(zone, block, block->loop_information(),
836 loop_header);
837 } else {
838 ASSERT(block->IsFinished());
839 kind_ = SUCCESSORS;
840 loop_header_ = loop_header;
841 InitializeSuccessors();
842 return this;
843 }
844 }
845 }
846
847 PostorderProcessor* SetupLoopMembers(Zone* zone,
848 HBasicBlock* block,
849 HLoopInformation* loop,
850 HBasicBlock* loop_header) {
851 kind_ = LOOP_MEMBERS;
852 block_ = block;
853 loop_ = loop;
854 loop_header_ = loop_header;
855 InitializeLoopMembers();
856 return this;
857 }
858
859 PostorderProcessor* SetupSuccessorsOfLoopMember(
860 HBasicBlock* block,
861 HLoopInformation* loop,
862 HBasicBlock* loop_header) {
863 kind_ = SUCCESSORS_OF_LOOP_MEMBER;
864 block_ = block;
865 loop_ = loop;
866 loop_header_ = loop_header;
867 InitializeSuccessors();
868 return this;
869 }
870
871 // This method "allocates" a new stack frame.
872 PostorderProcessor* Push(Zone* zone) {
873 if (child_ == NULL) {
874 child_ = new(zone) PostorderProcessor(this);
875 }
876 return child_;
877 }
878
879 void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) {
880 ASSERT(block_->end()->FirstSuccessor() == NULL ||
881 order->Contains(block_->end()->FirstSuccessor()) ||
882 block_->end()->FirstSuccessor()->IsLoopHeader());
883 ASSERT(block_->end()->SecondSuccessor() == NULL ||
884 order->Contains(block_->end()->SecondSuccessor()) ||
885 block_->end()->SecondSuccessor()->IsLoopHeader());
886 order->Add(block_, zone);
887 }
888
889 // This method is the basic block to walk up the stack.
890 PostorderProcessor* Pop(Zone* zone,
891 BitVector* visited,
892 ZoneList<HBasicBlock*>* order) {
893 switch (kind_) {
894 case SUCCESSORS:
895 case SUCCESSORS_OF_LOOP_HEADER:
896 ClosePostorder(order, zone);
897 return father_;
898 case LOOP_MEMBERS:
899 return father_;
900 case SUCCESSORS_OF_LOOP_MEMBER:
901 if (block()->IsLoopHeader() && block() != loop_->loop_header()) {
902 // In this case we need to perform a LOOP_MEMBERS cycle so we
903 // initialize it and return this instead of father.
904 return SetupLoopMembers(zone, block(),
905 block()->loop_information(), loop_header_);
906 } else {
907 return father_;
908 }
909 case NONE:
910 return father_;
911 }
912 UNREACHABLE();
913 return NULL;
914 }
915
916 // Walks up the stack.
917 PostorderProcessor* Backtrack(Zone* zone,
918 BitVector* visited,
919 ZoneList<HBasicBlock*>* order) {
920 PostorderProcessor* parent = Pop(zone, visited, order);
921 while (parent != NULL) {
922 PostorderProcessor* next =
923 parent->PerformNonBacktrackingStep(zone, visited, order);
924 if (next != NULL) {
925 return next;
926 } else {
927 parent = parent->Pop(zone, visited, order);
928 }
929 }
930 return NULL;
931 }
932
933 PostorderProcessor* PerformNonBacktrackingStep(
934 Zone* zone,
935 BitVector* visited,
936 ZoneList<HBasicBlock*>* order) {
937 HBasicBlock* next_block;
938 switch (kind_) {
939 case SUCCESSORS:
940 next_block = AdvanceSuccessors();
941 if (next_block != NULL) {
942 PostorderProcessor* result = Push(zone);
943 return result->SetupSuccessors(zone, next_block,
944 loop_header_, visited);
945 }
946 break;
947 case SUCCESSORS_OF_LOOP_HEADER:
948 next_block = AdvanceSuccessors();
949 if (next_block != NULL) {
950 PostorderProcessor* result = Push(zone);
951 return result->SetupSuccessors(zone, next_block,
952 block(), visited);
953 }
954 break;
955 case LOOP_MEMBERS:
956 next_block = AdvanceLoopMembers();
957 if (next_block != NULL) {
958 PostorderProcessor* result = Push(zone);
959 return result->SetupSuccessorsOfLoopMember(next_block,
960 loop_, loop_header_);
961 }
962 break;
963 case SUCCESSORS_OF_LOOP_MEMBER:
964 next_block = AdvanceSuccessors();
965 if (next_block != NULL) {
966 PostorderProcessor* result = Push(zone);
967 return result->SetupSuccessors(zone, next_block,
968 loop_header_, visited);
969 }
970 break;
971 case NONE:
972 return NULL;
973 }
974 return NULL;
975 }
976
977 // The following two methods implement a "foreach b in successors" cycle.
978 void InitializeSuccessors() {
979 loop_index = 0;
980 loop_length = 0;
981 successor_iterator = HSuccessorIterator(block_->end());
982 }
983
984 HBasicBlock* AdvanceSuccessors() {
985 if (!successor_iterator.Done()) {
986 HBasicBlock* result = successor_iterator.Current();
987 successor_iterator.Advance();
988 return result;
989 }
990 return NULL;
991 }
992
993 // The following two methods implement a "foreach b in loop members" cycle.
994 void InitializeLoopMembers() {
995 loop_index = 0;
996 loop_length = loop_->blocks()->length();
997 }
998
999 HBasicBlock* AdvanceLoopMembers() {
1000 if (loop_index < loop_length) {
1001 HBasicBlock* result = loop_->blocks()->at(loop_index);
1002 loop_index++;
1003 return result;
1004 } else {
1005 return NULL;
1006 }
1007 }
1008
1009 LoopKind kind_;
1010 PostorderProcessor* father_;
1011 PostorderProcessor* child_;
1012 HLoopInformation* loop_;
1013 HBasicBlock* block_;
1014 HBasicBlock* loop_header_;
1015 int loop_index;
1016 int loop_length;
1017 HSuccessorIterator successor_iterator;
1018};
1019
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001020
1021void HGraph::OrderBlocks() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001022 HPhase phase("H_Block ordering");
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001023 BitVector visited(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001024
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001025 ZoneList<HBasicBlock*> reverse_result(8, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001026 HBasicBlock* start = blocks_[0];
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001027 PostorderProcessor* postorder =
1028 PostorderProcessor::CreateEntryProcessor(zone(), start, &visited);
1029 while (postorder != NULL) {
1030 postorder = postorder->PerformStep(zone(), &visited, &reverse_result);
1031 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001032 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001033 int index = 0;
1034 for (int i = reverse_result.length() - 1; i >= 0; --i) {
1035 HBasicBlock* b = reverse_result[i];
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001036 blocks_.Add(b, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001037 b->set_block_id(index++);
1038 }
1039}
1040
1041
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001042void HGraph::AssignDominators() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001043 HPhase phase("H_Assign dominators", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001044 for (int i = 0; i < blocks_.length(); ++i) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001045 HBasicBlock* block = blocks_[i];
1046 if (block->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001047 // Only the first predecessor of a loop header is from outside the loop.
1048 // All others are back edges, and thus cannot dominate the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001049 block->AssignCommonDominator(block->predecessors()->first());
1050 block->AssignLoopSuccessorDominators();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001051 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00001052 for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001053 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
1054 }
1055 }
1056 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001057}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001058
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001059// Mark all blocks that are dominated by an unconditional soft deoptimize to
1060// prevent code motion across those blocks.
1061void HGraph::PropagateDeoptimizingMark() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001062 HPhase phase("H_Propagate deoptimizing mark", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001063 MarkAsDeoptimizingRecursively(entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001064}
1065
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001066void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
1067 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1068 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1069 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
1070 MarkAsDeoptimizingRecursively(dominated);
1071 }
1072}
1073
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001074void HGraph::EliminateRedundantPhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001075 HPhase phase("H_Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001076
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001077 // Worklist of phis that can potentially be eliminated. Initialized with
1078 // all phi nodes. When elimination of a phi node modifies another phi node
1079 // the modified phi node is added to the worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001080 ZoneList<HPhi*> worklist(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001081 for (int i = 0; i < blocks_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001082 worklist.AddAll(*blocks_[i]->phis(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001083 }
1084
1085 while (!worklist.is_empty()) {
1086 HPhi* phi = worklist.RemoveLast();
1087 HBasicBlock* block = phi->block();
1088
1089 // Skip phi node if it was already replaced.
1090 if (block == NULL) continue;
1091
1092 // Get replacement value if phi is redundant.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001093 HValue* replacement = phi->GetRedundantReplacement();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001094
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001095 if (replacement != NULL) {
1096 // Iterate through the uses and replace them all.
1097 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1098 HValue* value = it.value();
1099 value->SetOperandAt(it.index(), replacement);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001100 if (value->IsPhi()) worklist.Add(HPhi::cast(value), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001101 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001102 block->RemovePhi(phi);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001103 }
1104 }
1105}
1106
1107
1108void HGraph::EliminateUnreachablePhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001109 HPhase phase("H_Unreachable phi elimination", this);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001110
1111 // Initialize worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001112 ZoneList<HPhi*> phi_list(blocks_.length(), zone());
1113 ZoneList<HPhi*> worklist(blocks_.length(), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001114 for (int i = 0; i < blocks_.length(); ++i) {
1115 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
1116 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001117 phi_list.Add(phi, zone());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001118 // We can't eliminate phis in the receiver position in the environment
1119 // because in case of throwing an error we need this value to
1120 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001121 if (phi->HasRealUses() || phi->IsReceiver()) {
1122 phi->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001123 worklist.Add(phi, zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001124 }
1125 }
1126 }
1127
1128 // Iteratively mark live phis.
1129 while (!worklist.is_empty()) {
1130 HPhi* phi = worklist.RemoveLast();
1131 for (int i = 0; i < phi->OperandCount(); i++) {
1132 HValue* operand = phi->OperandAt(i);
1133 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
1134 HPhi::cast(operand)->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001135 worklist.Add(HPhi::cast(operand), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001136 }
1137 }
1138 }
1139
1140 // Remove unreachable phis.
1141 for (int i = 0; i < phi_list.length(); i++) {
1142 HPhi* phi = phi_list[i];
1143 if (!phi->is_live()) {
1144 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001145 block->RemovePhi(phi);
1146 block->RecordDeletedPhi(phi->merged_index());
1147 }
1148 }
1149}
1150
1151
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001152bool HGraph::CheckArgumentsPhiUses() {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00001153 int block_count = blocks_.length();
1154 for (int i = 0; i < block_count; ++i) {
1155 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1156 HPhi* phi = blocks_[i]->phis()->at(j);
1157 // We don't support phi uses of arguments for now.
1158 if (phi->CheckFlag(HValue::kIsArguments)) return false;
1159 }
1160 }
1161 return true;
1162}
1163
1164
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001165bool HGraph::CheckConstPhiUses() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001166 int block_count = blocks_.length();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001167 for (int i = 0; i < block_count; ++i) {
1168 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1169 HPhi* phi = blocks_[i]->phis()->at(j);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001170 // Check for the hole value (from an uninitialized const).
1171 for (int k = 0; k < phi->OperandCount(); k++) {
1172 if (phi->OperandAt(k) == GetConstantHole()) return false;
1173 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001174 }
1175 }
1176 return true;
1177}
1178
1179
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001180void HGraph::CollectPhis() {
1181 int block_count = blocks_.length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001182 phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001183 for (int i = 0; i < block_count; ++i) {
1184 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1185 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001186 phi_list_->Add(phi, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001187 }
1188 }
1189}
1190
1191
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001192void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001193 BitVector in_worklist(GetMaximumValueID(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001194 for (int i = 0; i < worklist->length(); ++i) {
1195 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
1196 in_worklist.Add(worklist->at(i)->id());
1197 }
1198
1199 while (!worklist->is_empty()) {
1200 HValue* current = worklist->RemoveLast();
1201 in_worklist.Remove(current->id());
1202 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001203 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
1204 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001205 if (!in_worklist.Contains(use->id())) {
1206 in_worklist.Add(use->id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001207 worklist->Add(use, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001208 }
1209 }
1210 }
1211 }
1212}
1213
1214
1215class HRangeAnalysis BASE_EMBEDDED {
1216 public:
ulan@chromium.org812308e2012-02-29 15:58:45 +00001217 explicit HRangeAnalysis(HGraph* graph) :
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001218 graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001219
1220 void Analyze();
1221
1222 private:
1223 void TraceRange(const char* msg, ...);
1224 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001225 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
1226 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001227 void InferRange(HValue* value);
1228 void RollBackTo(int index);
1229 void AddRange(HValue* value, Range* range);
1230
1231 HGraph* graph_;
ulan@chromium.org812308e2012-02-29 15:58:45 +00001232 Zone* zone_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001233 ZoneList<HValue*> changed_ranges_;
1234};
1235
1236
1237void HRangeAnalysis::TraceRange(const char* msg, ...) {
1238 if (FLAG_trace_range) {
1239 va_list arguments;
1240 va_start(arguments, msg);
1241 OS::VPrint(msg, arguments);
1242 va_end(arguments);
1243 }
1244}
1245
1246
1247void HRangeAnalysis::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001248 HPhase phase("H_Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001249 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001250}
1251
1252
1253void HRangeAnalysis::Analyze(HBasicBlock* block) {
1254 TraceRange("Analyzing block B%d\n", block->block_id());
1255
1256 int last_changed_range = changed_ranges_.length() - 1;
1257
1258 // Infer range based on control flow.
1259 if (block->predecessors()->length() == 1) {
1260 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001261 if (pred->end()->IsCompareIDAndBranch()) {
1262 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001263 }
1264 }
1265
1266 // Process phi instructions.
1267 for (int i = 0; i < block->phis()->length(); ++i) {
1268 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001269 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001270 }
1271
1272 // Go through all instructions of the current block.
1273 HInstruction* instr = block->first();
1274 while (instr != block->end()) {
1275 InferRange(instr);
1276 instr = instr->next();
1277 }
1278
1279 // Continue analysis in all dominated blocks.
1280 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1281 Analyze(block->dominated_blocks()->at(i));
1282 }
1283
1284 RollBackTo(last_changed_range);
1285}
1286
1287
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001288void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
1289 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001290 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001291 if (test->GetInputRepresentation().IsInteger32()) {
1292 Token::Value op = test->token();
1293 if (test->SecondSuccessor() == dest) {
1294 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001295 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001296 Token::Value inverted_op = Token::InvertCompareOp(op);
1297 UpdateControlFlowRange(op, test->left(), test->right());
1298 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001299 }
1300}
1301
1302
1303// We know that value [op] other. Use this information to update the range on
1304// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001305void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
1306 HValue* value,
1307 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001308 Range temp_range;
1309 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001310 Range* new_range = NULL;
1311
1312 TraceRange("Control flow range infer %d %s %d\n",
1313 value->id(),
1314 Token::Name(op),
1315 other->id());
1316
1317 if (op == Token::EQ || op == Token::EQ_STRICT) {
1318 // The same range has to apply for value.
ulan@chromium.org812308e2012-02-29 15:58:45 +00001319 new_range = range->Copy(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001320 } else if (op == Token::LT || op == Token::LTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001321 new_range = range->CopyClearLower(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001322 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001323 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001324 }
1325 } else if (op == Token::GT || op == Token::GTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001326 new_range = range->CopyClearUpper(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001327 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001328 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001329 }
1330 }
1331
1332 if (new_range != NULL && !new_range->IsMostGeneric()) {
1333 AddRange(value, new_range);
1334 }
1335}
1336
1337
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001338void HRangeAnalysis::InferRange(HValue* value) {
1339 ASSERT(!value->HasRange());
1340 if (!value->representation().IsNone()) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001341 value->ComputeInitialRange(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001342 Range* range = value->range();
1343 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1344 value->id(),
1345 value->Mnemonic(),
1346 range->lower(),
1347 range->upper());
1348 }
1349}
1350
1351
1352void HRangeAnalysis::RollBackTo(int index) {
1353 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1354 changed_ranges_[i]->RemoveLastAddedRange();
1355 }
1356 changed_ranges_.Rewind(index + 1);
1357}
1358
1359
1360void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1361 Range* original_range = value->range();
ulan@chromium.org812308e2012-02-29 15:58:45 +00001362 value->AddNewRange(range, zone_);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001363 changed_ranges_.Add(value, zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001364 Range* new_range = value->range();
1365 TraceRange("Updated range of %d set to [%d,%d]\n",
1366 value->id(),
1367 new_range->lower(),
1368 new_range->upper());
1369 if (original_range != NULL) {
1370 TraceRange("Original range was [%d,%d]\n",
1371 original_range->lower(),
1372 original_range->upper());
1373 }
1374 TraceRange("New information was [%d,%d]\n",
1375 range->lower(),
1376 range->upper());
1377}
1378
1379
1380void TraceGVN(const char* msg, ...) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001381 va_list arguments;
1382 va_start(arguments, msg);
1383 OS::VPrint(msg, arguments);
1384 va_end(arguments);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001385}
1386
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001387// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when
1388// --trace-gvn is off.
1389#define TRACE_GVN_1(msg, a1) \
1390 if (FLAG_trace_gvn) { \
1391 TraceGVN(msg, a1); \
1392 }
1393
1394#define TRACE_GVN_2(msg, a1, a2) \
1395 if (FLAG_trace_gvn) { \
1396 TraceGVN(msg, a1, a2); \
1397 }
1398
1399#define TRACE_GVN_3(msg, a1, a2, a3) \
1400 if (FLAG_trace_gvn) { \
1401 TraceGVN(msg, a1, a2, a3); \
1402 }
1403
1404#define TRACE_GVN_4(msg, a1, a2, a3, a4) \
1405 if (FLAG_trace_gvn) { \
1406 TraceGVN(msg, a1, a2, a3, a4); \
1407 }
1408
1409#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \
1410 if (FLAG_trace_gvn) { \
1411 TraceGVN(msg, a1, a2, a3, a4, a5); \
1412 }
1413
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001414
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001415HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001416 : array_size_(other->array_size_),
1417 lists_size_(other->lists_size_),
1418 count_(other->count_),
1419 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001420 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
1421 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001422 free_list_head_(other->free_list_head_) {
1423 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
1424 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
1425}
1426
1427
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001428void HValueMap::Kill(GVNFlagSet flags) {
1429 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags);
1430 if (!present_flags_.ContainsAnyOf(depends_flags)) return;
1431 present_flags_.RemoveAll();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001432 for (int i = 0; i < array_size_; ++i) {
1433 HValue* value = array_[i].value;
1434 if (value != NULL) {
1435 // Clear list of collisions first, so we know if it becomes empty.
1436 int kept = kNil; // List of kept elements.
1437 int next;
1438 for (int current = array_[i].next; current != kNil; current = next) {
1439 next = lists_[current].next;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001440 HValue* value = lists_[current].value;
1441 if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001442 // Drop it.
1443 count_--;
1444 lists_[current].next = free_list_head_;
1445 free_list_head_ = current;
1446 } else {
1447 // Keep it.
1448 lists_[current].next = kept;
1449 kept = current;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001450 present_flags_.Add(value->gvn_flags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001451 }
1452 }
1453 array_[i].next = kept;
1454
1455 // Now possibly drop directly indexed element.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001456 value = array_[i].value;
1457 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001458 count_--;
1459 int head = array_[i].next;
1460 if (head == kNil) {
1461 array_[i].value = NULL;
1462 } else {
1463 array_[i].value = lists_[head].value;
1464 array_[i].next = lists_[head].next;
1465 lists_[head].next = free_list_head_;
1466 free_list_head_ = head;
1467 }
1468 } else {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001469 present_flags_.Add(value->gvn_flags()); // Keep it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001470 }
1471 }
1472 }
1473}
1474
1475
1476HValue* HValueMap::Lookup(HValue* value) const {
1477 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
1478 uint32_t pos = Bound(hash);
1479 if (array_[pos].value != NULL) {
1480 if (array_[pos].value->Equals(value)) return array_[pos].value;
1481 int next = array_[pos].next;
1482 while (next != kNil) {
1483 if (lists_[next].value->Equals(value)) return lists_[next].value;
1484 next = lists_[next].next;
1485 }
1486 }
1487 return NULL;
1488}
1489
1490
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001491void HValueMap::Resize(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001492 ASSERT(new_size > count_);
1493 // Hashing the values into the new array has no more collisions than in the
1494 // old hash map, so we can use the existing lists_ array, if we are careful.
1495
1496 // Make sure we have at least one free element.
1497 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001498 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001499 }
1500
1501 HValueMapListElement* new_array =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001502 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001503 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
1504
1505 HValueMapListElement* old_array = array_;
1506 int old_size = array_size_;
1507
1508 int old_count = count_;
1509 count_ = 0;
1510 // Do not modify present_flags_. It is currently correct.
1511 array_size_ = new_size;
1512 array_ = new_array;
1513
1514 if (old_array != NULL) {
1515 // Iterate over all the elements in lists, rehashing them.
1516 for (int i = 0; i < old_size; ++i) {
1517 if (old_array[i].value != NULL) {
1518 int current = old_array[i].next;
1519 while (current != kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001520 Insert(lists_[current].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001521 int next = lists_[current].next;
1522 lists_[current].next = free_list_head_;
1523 free_list_head_ = current;
1524 current = next;
1525 }
1526 // Rehash the directly stored value.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001527 Insert(old_array[i].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001528 }
1529 }
1530 }
1531 USE(old_count);
1532 ASSERT(count_ == old_count);
1533}
1534
1535
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001536void HValueMap::ResizeLists(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001537 ASSERT(new_size > lists_size_);
1538
1539 HValueMapListElement* new_lists =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001540 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001541 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
1542
1543 HValueMapListElement* old_lists = lists_;
1544 int old_size = lists_size_;
1545
1546 lists_size_ = new_size;
1547 lists_ = new_lists;
1548
1549 if (old_lists != NULL) {
1550 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
1551 }
1552 for (int i = old_size; i < lists_size_; ++i) {
1553 lists_[i].next = free_list_head_;
1554 free_list_head_ = i;
1555 }
1556}
1557
1558
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001559void HValueMap::Insert(HValue* value, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001560 ASSERT(value != NULL);
1561 // Resizing when half of the hashtable is filled up.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001562 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001563 ASSERT(count_ < array_size_);
1564 count_++;
1565 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
1566 if (array_[pos].value == NULL) {
1567 array_[pos].value = value;
1568 array_[pos].next = kNil;
1569 } else {
1570 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001571 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001572 }
1573 int new_element_pos = free_list_head_;
1574 ASSERT(new_element_pos != kNil);
1575 free_list_head_ = lists_[free_list_head_].next;
1576 lists_[new_element_pos].value = value;
1577 lists_[new_element_pos].next = array_[pos].next;
1578 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
1579 array_[pos].next = new_element_pos;
1580 }
1581}
1582
1583
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001584HSideEffectMap::HSideEffectMap() : count_(0) {
1585 memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize);
1586}
1587
1588
1589HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001590 *this = *other; // Calls operator=.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001591}
1592
1593
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001594HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001595 if (this != &other) {
1596 memcpy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize);
1597 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001598 return *this;
1599}
1600
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001601void HSideEffectMap::Kill(GVNFlagSet flags) {
1602 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
1603 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
1604 if (flags.Contains(changes_flag)) {
1605 if (data_[i] != NULL) count_--;
1606 data_[i] = NULL;
1607 }
1608 }
1609}
1610
1611
1612void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) {
1613 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
1614 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
1615 if (flags.Contains(changes_flag)) {
1616 if (data_[i] == NULL) count_++;
1617 data_[i] = instr;
1618 }
1619 }
1620}
1621
1622
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001623class HStackCheckEliminator BASE_EMBEDDED {
1624 public:
1625 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
1626
1627 void Process();
1628
1629 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001630 HGraph* graph_;
1631};
1632
1633
1634void HStackCheckEliminator::Process() {
1635 // For each loop block walk the dominator tree from the backwards branch to
1636 // the loop header. If a call instruction is encountered the backwards branch
1637 // is dominated by a call and the stack check in the backwards branch can be
1638 // removed.
1639 for (int i = 0; i < graph_->blocks()->length(); i++) {
1640 HBasicBlock* block = graph_->blocks()->at(i);
1641 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001642 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
1643 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00001644 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001645 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001646 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001647 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00001648 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001649 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001650 }
1651 instr = instr->next();
1652 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00001653
1654 // Done when the loop header is processed.
1655 if (dominator == block) break;
1656
1657 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001658 dominator = dominator->dominator();
1659 }
1660 }
1661 }
1662}
1663
1664
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001665// Simple sparse set with O(1) add, contains, and clear.
1666class SparseSet {
1667 public:
1668 SparseSet(Zone* zone, int capacity)
1669 : capacity_(capacity),
1670 length_(0),
1671 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001672 sparse_(zone->NewArray<int>(capacity)) {
1673#ifndef NVALGRIND
1674 // Initialize the sparse array to make valgrind happy.
1675 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
1676#endif
1677 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001678
1679 bool Contains(int n) const {
1680 ASSERT(0 <= n && n < capacity_);
1681 int d = sparse_[n];
1682 return 0 <= d && d < length_ && dense_[d] == n;
1683 }
1684
1685 bool Add(int n) {
1686 if (Contains(n)) return false;
1687 dense_[length_] = n;
1688 sparse_[n] = length_;
1689 ++length_;
1690 return true;
1691 }
1692
1693 void Clear() { length_ = 0; }
1694
1695 private:
1696 int capacity_;
1697 int length_;
1698 int* dense_;
1699 int* sparse_;
1700
1701 DISALLOW_COPY_AND_ASSIGN(SparseSet);
1702};
1703
1704
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001705class HGlobalValueNumberer BASE_EMBEDDED {
1706 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001707 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001708 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001709 info_(info),
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001710 removed_side_effects_(false),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001711 block_side_effects_(graph->blocks()->length(), graph->zone()),
1712 loop_side_effects_(graph->blocks()->length(), graph->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001713 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00001714 ASSERT(!info->isolate()->heap()->IsAllocationAllowed());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001715 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
1716 graph_->zone());
1717 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
1718 graph_->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001719 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001720
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001721 // Returns true if values with side effects are removed.
1722 bool Analyze();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001723
1724 private:
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001725 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
1726 HBasicBlock* dominator,
1727 HBasicBlock* dominated);
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001728 void AnalyzeGraph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001729 void ComputeBlockSideEffects();
1730 void LoopInvariantCodeMotion();
1731 void ProcessLoopBlock(HBasicBlock* block,
1732 HBasicBlock* before_loop,
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001733 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001734 GVNFlagSet* accumulated_first_time_depends,
1735 GVNFlagSet* accumulated_first_time_changes);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001736 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001737 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
1738
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001739 HGraph* graph() { return graph_; }
1740 CompilationInfo* info() { return info_; }
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001741 Zone* zone() const { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001742
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001743 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001744 CompilationInfo* info_;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001745 bool removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001746
1747 // A map of block IDs to their side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001748 ZoneList<GVNFlagSet> block_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001749
1750 // A map of loop header block IDs to their loop's side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001751 ZoneList<GVNFlagSet> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001752
1753 // Used when collecting side effects on paths from dominator to
1754 // dominated.
1755 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001756};
1757
1758
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001759bool HGlobalValueNumberer::Analyze() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001760 removed_side_effects_ = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001761 ComputeBlockSideEffects();
1762 if (FLAG_loop_invariant_code_motion) {
1763 LoopInvariantCodeMotion();
1764 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001765 AnalyzeGraph();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001766 return removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001767}
1768
1769
1770void HGlobalValueNumberer::ComputeBlockSideEffects() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001771 // The Analyze phase of GVN can be called multiple times. Clear loop side
1772 // effects before computing them to erase the contents from previous Analyze
1773 // passes.
1774 for (int i = 0; i < loop_side_effects_.length(); ++i) {
1775 loop_side_effects_[i].RemoveAll();
1776 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001777 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1778 // Compute side effects for the block.
1779 HBasicBlock* block = graph_->blocks()->at(i);
1780 HInstruction* instr = block->first();
1781 int id = block->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001782 GVNFlagSet side_effects;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001783 while (instr != NULL) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001784 side_effects.Add(instr->ChangesFlags());
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00001785 if (instr->IsSoftDeoptimize()) {
1786 block_side_effects_[id].RemoveAll();
1787 side_effects.RemoveAll();
1788 break;
1789 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001790 instr = instr->next();
1791 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001792 block_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001793
1794 // Loop headers are part of their loop.
1795 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001796 loop_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001797 }
1798
1799 // Propagate loop side effects upwards.
1800 if (block->HasParentLoopHeader()) {
1801 int header_id = block->parent_loop_header()->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001802 loop_side_effects_[header_id].Add(block->IsLoopHeader()
1803 ? loop_side_effects_[id]
1804 : side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001805 }
1806 }
1807}
1808
1809
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001810SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) {
1811 char underlying_buffer[kLastFlag * 128];
1812 Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer));
1813#if DEBUG
1814 int offset = 0;
1815 const char* separator = "";
1816 const char* comma = ", ";
1817 buffer[0] = 0;
1818 uint32_t set_depends_on = 0;
1819 uint32_t set_changes = 0;
1820 for (int bit = 0; bit < kLastFlag; ++bit) {
1821 if ((flags.ToIntegral() & (1 << bit)) != 0) {
1822 if (bit % 2 == 0) {
1823 set_changes++;
1824 } else {
1825 set_depends_on++;
1826 }
1827 }
1828 }
1829 bool positive_changes = set_changes < (kLastFlag / 2);
1830 bool positive_depends_on = set_depends_on < (kLastFlag / 2);
1831 if (set_changes > 0) {
1832 if (positive_changes) {
1833 offset += OS::SNPrintF(buffer + offset, "changes [");
1834 } else {
1835 offset += OS::SNPrintF(buffer + offset, "changes all except [");
1836 }
1837 for (int bit = 0; bit < kLastFlag; ++bit) {
1838 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) {
1839 switch (static_cast<GVNFlag>(bit)) {
1840#define DECLARE_FLAG(type) \
1841 case kChanges##type: \
1842 offset += OS::SNPrintF(buffer + offset, separator); \
1843 offset += OS::SNPrintF(buffer + offset, #type); \
1844 separator = comma; \
1845 break;
1846GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
1847GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
1848#undef DECLARE_FLAG
1849 default:
1850 break;
1851 }
1852 }
1853 }
1854 offset += OS::SNPrintF(buffer + offset, "]");
1855 }
1856 if (set_depends_on > 0) {
1857 separator = "";
1858 if (set_changes > 0) {
1859 offset += OS::SNPrintF(buffer + offset, ", ");
1860 }
1861 if (positive_depends_on) {
1862 offset += OS::SNPrintF(buffer + offset, "depends on [");
1863 } else {
1864 offset += OS::SNPrintF(buffer + offset, "depends on all except [");
1865 }
1866 for (int bit = 0; bit < kLastFlag; ++bit) {
1867 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) {
1868 switch (static_cast<GVNFlag>(bit)) {
1869#define DECLARE_FLAG(type) \
1870 case kDependsOn##type: \
1871 offset += OS::SNPrintF(buffer + offset, separator); \
1872 offset += OS::SNPrintF(buffer + offset, #type); \
1873 separator = comma; \
1874 break;
1875GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
1876GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
1877#undef DECLARE_FLAG
1878 default:
1879 break;
1880 }
1881 }
1882 }
1883 offset += OS::SNPrintF(buffer + offset, "]");
1884 }
1885#else
1886 OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral());
1887#endif
1888 size_t string_len = strlen(underlying_buffer) + 1;
1889 ASSERT(string_len <= sizeof(underlying_buffer));
1890 char* result = new char[strlen(underlying_buffer) + 1];
1891 memcpy(result, underlying_buffer, string_len);
1892 return SmartArrayPointer<char>(result);
1893}
1894
1895
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001896void HGlobalValueNumberer::LoopInvariantCodeMotion() {
1897 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1898 HBasicBlock* block = graph_->blocks()->at(i);
1899 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001900 GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001901 TRACE_GVN_2("Try loop invariant motion for block B%d %s\n",
1902 block->block_id(),
1903 *GetGVNFlagsString(side_effects));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001904
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001905 GVNFlagSet accumulated_first_time_depends;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001906 GVNFlagSet accumulated_first_time_changes;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001907 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
1908 for (int j = block->block_id(); j <= last->block_id(); ++j) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001909 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001910 &accumulated_first_time_depends,
1911 &accumulated_first_time_changes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001912 }
1913 }
1914 }
1915}
1916
1917
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001918void HGlobalValueNumberer::ProcessLoopBlock(
1919 HBasicBlock* block,
1920 HBasicBlock* loop_header,
1921 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001922 GVNFlagSet* first_time_depends,
1923 GVNFlagSet* first_time_changes) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001924 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001925 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001926 TRACE_GVN_2("Loop invariant motion for B%d %s\n",
1927 block->block_id(),
1928 *GetGVNFlagsString(depends_flags));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001929 HInstruction* instr = block->first();
1930 while (instr != NULL) {
1931 HInstruction* next = instr->next();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001932 bool hoisted = false;
1933 if (instr->CheckFlag(HValue::kUseGVN)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001934 TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n",
1935 instr->id(),
1936 instr->Mnemonic(),
1937 *GetGVNFlagsString(instr->gvn_flags()),
1938 *GetGVNFlagsString(loop_kills));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001939 bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001940 if (instr->IsTransitionElementsKind()) {
1941 // It's possible to hoist transitions out of a loop as long as the
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001942 // hoisting wouldn't move the transition past an instruction that has a
1943 // DependsOn flag for anything it changes.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001944 GVNFlagSet hoist_depends_blockers =
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001945 HValue::ConvertChangesToDependsFlags(instr->ChangesFlags());
1946
1947 // In addition, the transition must not be hoisted above elements kind
1948 // changes, or if the transition is destructive to the elements buffer,
1949 // changes to array pointer or array contents.
1950 GVNFlagSet hoist_change_blockers;
1951 hoist_change_blockers.Add(kChangesElementsKind);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001952 HTransitionElementsKind* trans = HTransitionElementsKind::cast(instr);
1953 if (trans->original_map()->has_fast_double_elements()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001954 hoist_change_blockers.Add(kChangesElementsPointer);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001955 hoist_change_blockers.Add(kChangesDoubleArrayElements);
1956 }
1957 if (trans->transitioned_map()->has_fast_double_elements()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001958 hoist_change_blockers.Add(kChangesElementsPointer);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001959 hoist_change_blockers.Add(kChangesArrayElements);
1960 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001961 if (FLAG_trace_gvn) {
1962 GVNFlagSet hoist_blockers = hoist_depends_blockers;
1963 hoist_blockers.Add(hoist_change_blockers);
1964 GVNFlagSet first_time = *first_time_changes;
1965 first_time.Add(*first_time_depends);
1966 TRACE_GVN_4("Checking dependencies on HTransitionElementsKind "
1967 "%d (%s) hoist blockers: %s; "
1968 "first-time accumulated: %s\n",
1969 instr->id(),
1970 instr->Mnemonic(),
1971 *GetGVNFlagsString(hoist_blockers),
1972 *GetGVNFlagsString(first_time));
1973 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001974 // It's possible to hoist transition from the current loop loop only if
1975 // they dominate all of the successor blocks in the same loop and there
1976 // are not any instructions that have Changes/DependsOn that intervene
1977 // between it and the beginning of the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001978 bool in_nested_loop = block != loop_header &&
1979 ((block->parent_loop_header() != loop_header) ||
1980 block->IsLoopHeader());
1981 can_hoist = !in_nested_loop &&
1982 block->IsLoopSuccessorDominator() &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001983 !first_time_depends->ContainsAnyOf(hoist_depends_blockers) &&
1984 !first_time_changes->ContainsAnyOf(hoist_change_blockers);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001985 }
1986
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001987 if (can_hoist) {
1988 bool inputs_loop_invariant = true;
1989 for (int i = 0; i < instr->OperandCount(); ++i) {
1990 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
1991 inputs_loop_invariant = false;
1992 }
1993 }
1994
1995 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001996 TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id());
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001997 // Move the instruction out of the loop.
1998 instr->Unlink();
1999 instr->InsertBefore(pre_header->end());
2000 if (instr->HasSideEffects()) removed_side_effects_ = true;
2001 hoisted = true;
2002 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002003 }
2004 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002005 if (!hoisted) {
2006 // If an instruction is not hoisted, we have to account for its side
2007 // effects when hoisting later HTransitionElementsKind instructions.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002008 GVNFlagSet previous_depends = *first_time_depends;
2009 GVNFlagSet previous_changes = *first_time_changes;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002010 first_time_depends->Add(instr->DependsOnFlags());
2011 first_time_changes->Add(instr->ChangesFlags());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002012 if (!(previous_depends == *first_time_depends)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002013 TRACE_GVN_1("Updated first-time accumulated %s\n",
2014 *GetGVNFlagsString(*first_time_depends));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002015 }
2016 if (!(previous_changes == *first_time_changes)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002017 TRACE_GVN_1("Updated first-time accumulated %s\n",
2018 *GetGVNFlagsString(*first_time_changes));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002019 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002020 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002021 instr = next;
2022 }
2023}
2024
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00002025
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002026bool HGlobalValueNumberer::AllowCodeMotion() {
2027 return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount;
2028}
2029
2030
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002031bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
2032 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002033 // If we've disabled code motion or we're in a block that unconditionally
2034 // deoptimizes, don't move any instructions.
2035 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002036}
2037
2038
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002039GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002040 HBasicBlock* dominator, HBasicBlock* dominated) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002041 GVNFlagSet side_effects;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002042 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
2043 HBasicBlock* block = dominated->predecessors()->at(i);
2044 if (dominator->block_id() < block->block_id() &&
2045 block->block_id() < dominated->block_id() &&
2046 visited_on_paths_.Add(block->block_id())) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002047 side_effects.Add(block_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002048 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002049 side_effects.Add(loop_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002050 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002051 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
2052 dominator, block));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002053 }
2054 }
2055 return side_effects;
2056}
2057
2058
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002059// Each instance of this class is like a "stack frame" for the recursive
2060// traversal of the dominator tree done during GVN (the stack is handled
2061// as a double linked list).
2062// We reuse frames when possible so the list length is limited by the depth
2063// of the dominator tree but this forces us to initialize each frame calling
2064// an explicit "Initialize" method instead of a using constructor.
2065class GvnBasicBlockState: public ZoneObject {
2066 public:
2067 static GvnBasicBlockState* CreateEntry(Zone* zone,
2068 HBasicBlock* entry_block,
2069 HValueMap* entry_map) {
2070 return new(zone)
2071 GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002072 }
2073
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002074 HBasicBlock* block() { return block_; }
2075 HValueMap* map() { return map_; }
2076 HSideEffectMap* dominators() { return &dominators_; }
2077
2078 GvnBasicBlockState* next_in_dominator_tree_traversal(
2079 Zone* zone,
2080 HBasicBlock** dominator) {
2081 // This assignment needs to happen before calling next_dominated() because
2082 // that call can reuse "this" if we are at the last dominated block.
2083 *dominator = block();
2084 GvnBasicBlockState* result = next_dominated(zone);
2085 if (result == NULL) {
2086 GvnBasicBlockState* dominator_state = pop();
2087 if (dominator_state != NULL) {
2088 // This branch is guaranteed not to return NULL because pop() never
2089 // returns a state where "is_done() == true".
2090 *dominator = dominator_state->block();
2091 result = dominator_state->next_dominated(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002092 } else {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002093 // Unnecessary (we are returning NULL) but done for cleanness.
2094 *dominator = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002095 }
2096 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002097 return result;
2098 }
2099
2100 private:
2101 void Initialize(HBasicBlock* block,
2102 HValueMap* map,
2103 HSideEffectMap* dominators,
2104 bool copy_map,
2105 Zone* zone) {
2106 block_ = block;
2107 map_ = copy_map ? map->Copy(zone) : map;
2108 dominated_index_ = -1;
2109 length_ = block->dominated_blocks()->length();
2110 if (dominators != NULL) {
2111 dominators_ = *dominators;
2112 }
2113 }
2114 bool is_done() { return dominated_index_ >= length_; }
2115
2116 GvnBasicBlockState(GvnBasicBlockState* previous,
2117 HBasicBlock* block,
2118 HValueMap* map,
2119 HSideEffectMap* dominators,
2120 Zone* zone)
2121 : previous_(previous), next_(NULL) {
2122 Initialize(block, map, dominators, true, zone);
2123 }
2124
2125 GvnBasicBlockState* next_dominated(Zone* zone) {
2126 dominated_index_++;
2127 if (dominated_index_ == length_ - 1) {
2128 // No need to copy the map for the last child in the dominator tree.
2129 Initialize(block_->dominated_blocks()->at(dominated_index_),
2130 map(),
2131 dominators(),
2132 false,
2133 zone);
2134 return this;
2135 } else if (dominated_index_ < length_) {
2136 return push(zone,
2137 block_->dominated_blocks()->at(dominated_index_),
2138 dominators());
2139 } else {
2140 return NULL;
2141 }
2142 }
2143
2144 GvnBasicBlockState* push(Zone* zone,
2145 HBasicBlock* block,
2146 HSideEffectMap* dominators) {
2147 if (next_ == NULL) {
2148 next_ =
2149 new(zone) GvnBasicBlockState(this, block, map(), dominators, zone);
2150 } else {
2151 next_->Initialize(block, map(), dominators, true, zone);
2152 }
2153 return next_;
2154 }
2155 GvnBasicBlockState* pop() {
2156 GvnBasicBlockState* result = previous_;
2157 while (result != NULL && result->is_done()) {
2158 TRACE_GVN_2("Backtracking from block B%d to block b%d\n",
2159 block()->block_id(),
2160 previous_->block()->block_id())
2161 result = result->previous_;
2162 }
2163 return result;
2164 }
2165
2166 GvnBasicBlockState* previous_;
2167 GvnBasicBlockState* next_;
2168 HBasicBlock* block_;
2169 HValueMap* map_;
2170 HSideEffectMap dominators_;
2171 int dominated_index_;
2172 int length_;
2173};
2174
2175// This is a recursive traversal of the dominator tree but it has been turned
2176// into a loop to avoid stack overflows.
2177// The logical "stack frames" of the recursion are kept in a list of
2178// GvnBasicBlockState instances.
2179void HGlobalValueNumberer::AnalyzeGraph() {
2180 HBasicBlock* entry_block = graph_->entry_block();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002181 HValueMap* entry_map = new(zone()) HValueMap(zone());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002182 GvnBasicBlockState* current =
2183 GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map);
2184
2185 while (current != NULL) {
2186 HBasicBlock* block = current->block();
2187 HValueMap* map = current->map();
2188 HSideEffectMap* dominators = current->dominators();
2189
2190 TRACE_GVN_2("Analyzing block B%d%s\n",
2191 block->block_id(),
2192 block->IsLoopHeader() ? " (loop header)" : "");
2193
2194 // If this is a loop header kill everything killed by the loop.
2195 if (block->IsLoopHeader()) {
2196 map->Kill(loop_side_effects_[block->block_id()]);
2197 }
2198
2199 // Go through all instructions of the current block.
2200 HInstruction* instr = block->first();
2201 while (instr != NULL) {
2202 HInstruction* next = instr->next();
2203 GVNFlagSet flags = instr->ChangesFlags();
2204 if (!flags.IsEmpty()) {
2205 // Clear all instructions in the map that are affected by side effects.
2206 // Store instruction as the dominating one for tracked side effects.
2207 map->Kill(flags);
2208 dominators->Store(flags, instr);
2209 TRACE_GVN_2("Instruction %d %s\n", instr->id(),
2210 *GetGVNFlagsString(flags));
2211 }
2212 if (instr->CheckFlag(HValue::kUseGVN)) {
2213 ASSERT(!instr->HasObservableSideEffects());
2214 HValue* other = map->Lookup(instr);
2215 if (other != NULL) {
2216 ASSERT(instr->Equals(other) && other->Equals(instr));
2217 TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n",
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002218 instr->id(),
2219 instr->Mnemonic(),
2220 other->id(),
2221 other->Mnemonic());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002222 if (instr->HasSideEffects()) removed_side_effects_ = true;
2223 instr->DeleteAndReplaceWith(other);
2224 } else {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002225 map->Add(instr, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002226 }
2227 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002228 if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) {
2229 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
2230 HValue* other = dominators->at(i);
2231 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
2232 GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i);
2233 if (instr->DependsOnFlags().Contains(depends_on_flag) &&
2234 (other != NULL)) {
2235 TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n",
2236 i,
2237 instr->id(),
2238 instr->Mnemonic(),
2239 other->id(),
2240 other->Mnemonic());
2241 instr->SetSideEffectDominator(changes_flag, other);
2242 }
2243 }
2244 }
2245 instr = next;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002246 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002247
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002248 HBasicBlock* dominator_block;
2249 GvnBasicBlockState* next =
2250 current->next_in_dominator_tree_traversal(zone(), &dominator_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002251
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002252 if (next != NULL) {
2253 HBasicBlock* dominated = next->block();
2254 HValueMap* successor_map = next->map();
2255 HSideEffectMap* successor_dominators = next->dominators();
2256
2257 // Kill everything killed on any path between this block and the
2258 // dominated block. We don't have to traverse these paths if the
2259 // value map and the dominators list is already empty. If the range
2260 // of block ids (block_id, dominated_id) is empty there are no such
2261 // paths.
2262 if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) &&
2263 dominator_block->block_id() + 1 < dominated->block_id()) {
2264 visited_on_paths_.Clear();
2265 GVNFlagSet side_effects_on_all_paths =
2266 CollectSideEffectsOnPathsToDominatedBlock(dominator_block,
2267 dominated);
2268 successor_map->Kill(side_effects_on_all_paths);
2269 successor_dominators->Kill(side_effects_on_all_paths);
2270 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002271 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002272 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002273 }
2274}
2275
2276
2277class HInferRepresentation BASE_EMBEDDED {
2278 public:
2279 explicit HInferRepresentation(HGraph* graph)
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002280 : graph_(graph),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002281 worklist_(8, graph->zone()),
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002282 in_worklist_(graph->GetMaximumValueID(), graph->zone()) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002283
2284 void Analyze();
2285
2286 private:
2287 Representation TryChange(HValue* current);
2288 void AddToWorklist(HValue* current);
2289 void InferBasedOnInputs(HValue* current);
2290 void AddDependantsToWorklist(HValue* current);
2291 void InferBasedOnUses(HValue* current);
2292
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002293 Zone* zone() const { return graph_->zone(); }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002294
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002295 HGraph* graph_;
2296 ZoneList<HValue*> worklist_;
2297 BitVector in_worklist_;
2298};
2299
2300
2301void HInferRepresentation::AddToWorklist(HValue* current) {
2302 if (current->representation().IsSpecialization()) return;
2303 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
2304 if (in_worklist_.Contains(current->id())) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002305 worklist_.Add(current, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002306 in_worklist_.Add(current->id());
2307}
2308
2309
2310// This method tries to specialize the representation type of the value
2311// given as a parameter. The value is asked to infer its representation type
2312// based on its inputs. If the inferred type is more specialized, then this
2313// becomes the new representation type of the node.
2314void HInferRepresentation::InferBasedOnInputs(HValue* current) {
2315 Representation r = current->representation();
2316 if (r.IsSpecialization()) return;
2317 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
2318 Representation inferred = current->InferredRepresentation();
2319 if (inferred.IsSpecialization()) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00002320 if (FLAG_trace_representation) {
2321 PrintF("Changing #%d representation %s -> %s based on inputs\n",
2322 current->id(),
2323 r.Mnemonic(),
2324 inferred.Mnemonic());
2325 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002326 current->ChangeRepresentation(inferred);
2327 AddDependantsToWorklist(current);
2328 }
2329}
2330
2331
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002332void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
2333 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2334 AddToWorklist(it.value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002335 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002336 for (int i = 0; i < value->OperandCount(); ++i) {
2337 AddToWorklist(value->OperandAt(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002338 }
2339}
2340
2341
2342// This method calculates whether specializing the representation of the value
2343// given as the parameter has a benefit in terms of less necessary type
2344// conversions. If there is a benefit, then the representation of the value is
2345// specialized.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002346void HInferRepresentation::InferBasedOnUses(HValue* value) {
2347 Representation r = value->representation();
2348 if (r.IsSpecialization() || value->HasNoUses()) return;
2349 ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
2350 Representation new_rep = TryChange(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002351 if (!new_rep.IsNone()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002352 if (!value->representation().Equals(new_rep)) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00002353 if (FLAG_trace_representation) {
2354 PrintF("Changing #%d representation %s -> %s based on uses\n",
2355 value->id(),
2356 r.Mnemonic(),
2357 new_rep.Mnemonic());
2358 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002359 value->ChangeRepresentation(new_rep);
2360 AddDependantsToWorklist(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002361 }
2362 }
2363}
2364
2365
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002366Representation HInferRepresentation::TryChange(HValue* value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002367 // Array of use counts for each representation.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002368 int use_count[Representation::kNumRepresentations] = { 0 };
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002369
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002370 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2371 HValue* use = it.value();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002372 Representation rep = use->ObservedInputRepresentation(it.index());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002373 if (rep.IsNone()) continue;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002374 if (FLAG_trace_representation) {
2375 PrintF("%d %s is used by %d %s as %s\n",
2376 value->id(),
2377 value->Mnemonic(),
2378 use->id(),
2379 use->Mnemonic(),
2380 rep.Mnemonic());
2381 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002382 if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002383 use_count[rep.kind()] += use->LoopWeight();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002384 }
2385 int tagged_count = use_count[Representation::kTagged];
2386 int double_count = use_count[Representation::kDouble];
2387 int int32_count = use_count[Representation::kInteger32];
2388 int non_tagged_count = double_count + int32_count;
2389
2390 // If a non-loop phi has tagged uses, don't convert it to untagged.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002391 if (value->IsPhi() && !value->block()->IsLoopHeader() && tagged_count > 0) {
2392 return Representation::None();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002393 }
2394
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002395 // Prefer unboxing over boxing, the latter is more expensive.
ricow@chromium.org27bf2882011-11-17 08:34:43 +00002396 if (tagged_count > non_tagged_count) return Representation::None();
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002397
2398 // Prefer Integer32 over Double, if possible.
2399 if (int32_count > 0 && value->IsConvertibleToInteger()) {
2400 return Representation::Integer32();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002401 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002402
2403 if (double_count > 0) return Representation::Double();
2404
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002405 return Representation::None();
2406}
2407
2408
2409void HInferRepresentation::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002410 HPhase phase("H_Infer representations", graph_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002411
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002412 // (1) Initialize bit vectors and count real uses. Each phi gets a
2413 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002414 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002415 int phi_count = phi_list->length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002416 ZoneList<BitVector*> connected_phis(phi_count, graph_->zone());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002417 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002418 phi_list->at(i)->InitRealUses(i);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002419 BitVector* connected_set = new(zone()) BitVector(phi_count, graph_->zone());
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00002420 connected_set->Add(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002421 connected_phis.Add(connected_set, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002422 }
2423
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002424 // (2) Do a fixed point iteration to find the set of connected phis. A
2425 // phi is connected to another phi if its value is used either directly or
2426 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002427 bool change = true;
2428 while (change) {
2429 change = false;
ricow@chromium.org55ee8072011-09-08 16:33:10 +00002430 // We normally have far more "forward edges" than "backward edges",
2431 // so we terminate faster when we walk backwards.
2432 for (int i = phi_count - 1; i >= 0; --i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002433 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002434 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2435 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002436 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002437 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002438 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
2439 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002440 }
2441 }
2442 }
2443 }
2444
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002445 // (3a) Use the phi reachability information from step 2 to
2446 // push information about values which can't be converted to integer
2447 // without deoptimization through the phi use-def chains, avoiding
2448 // unnecessary deoptimizations later.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002449 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002450 HPhi* phi = phi_list->at(i);
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002451 bool cti = phi->AllOperandsConvertibleToInteger();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002452 if (cti) continue;
2453
2454 for (BitVector::Iterator it(connected_phis.at(i));
2455 !it.Done();
2456 it.Advance()) {
2457 HPhi* phi = phi_list->at(it.Current());
2458 phi->set_is_convertible_to_integer(false);
2459 phi->ResetInteger32Uses();
2460 }
2461 }
2462
2463 // (3b) Use the phi reachability information from step 2 to
2464 // sum up the non-phi use counts of all connected phis.
2465 for (int i = 0; i < phi_count; ++i) {
2466 HPhi* phi = phi_list->at(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002467 for (BitVector::Iterator it(connected_phis.at(i));
2468 !it.Done();
2469 it.Advance()) {
2470 int index = it.Current();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002471 HPhi* it_use = phi_list->at(index);
2472 if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002473 }
2474 }
2475
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002476 // Initialize work list
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002477 for (int i = 0; i < graph_->blocks()->length(); ++i) {
2478 HBasicBlock* block = graph_->blocks()->at(i);
2479 const ZoneList<HPhi*>* phis = block->phis();
2480 for (int j = 0; j < phis->length(); ++j) {
2481 AddToWorklist(phis->at(j));
2482 }
2483
2484 HInstruction* current = block->first();
2485 while (current != NULL) {
2486 AddToWorklist(current);
2487 current = current->next();
2488 }
2489 }
2490
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002491 // Do a fixed point iteration, trying to improve representations
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002492 while (!worklist_.is_empty()) {
2493 HValue* current = worklist_.RemoveLast();
2494 in_worklist_.Remove(current->id());
2495 InferBasedOnInputs(current);
2496 InferBasedOnUses(current);
2497 }
2498}
2499
2500
2501void HGraph::InitializeInferredTypes() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002502 HPhase phase("H_Inferring types", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002503 InitializeInferredTypes(0, this->blocks_.length() - 1);
2504}
2505
2506
2507void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
2508 for (int i = from_inclusive; i <= to_inclusive; ++i) {
2509 HBasicBlock* block = blocks_[i];
2510
2511 const ZoneList<HPhi*>* phis = block->phis();
2512 for (int j = 0; j < phis->length(); j++) {
2513 phis->at(j)->UpdateInferredType();
2514 }
2515
2516 HInstruction* current = block->first();
2517 while (current != NULL) {
2518 current->UpdateInferredType();
2519 current = current->next();
2520 }
2521
2522 if (block->IsLoopHeader()) {
2523 HBasicBlock* last_back_edge =
2524 block->loop_information()->GetLastBackEdge();
2525 InitializeInferredTypes(i + 1, last_back_edge->block_id());
2526 // Skip all blocks already processed by the recursive call.
2527 i = last_back_edge->block_id();
2528 // Update phis of the loop header now after the whole loop body is
2529 // guaranteed to be processed.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002530 ZoneList<HValue*> worklist(block->phis()->length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002531 for (int j = 0; j < block->phis()->length(); ++j) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002532 worklist.Add(block->phis()->at(j), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002533 }
2534 InferTypes(&worklist);
2535 }
2536 }
2537}
2538
2539
2540void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
2541 HValue* current = value;
2542 while (current != NULL) {
2543 if (visited->Contains(current->id())) return;
2544
2545 // For phis, we must propagate the check to all of its inputs.
2546 if (current->IsPhi()) {
2547 visited->Add(current->id());
2548 HPhi* phi = HPhi::cast(current);
2549 for (int i = 0; i < phi->OperandCount(); ++i) {
2550 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
2551 }
2552 break;
2553 }
2554
2555 // For multiplication and division, we must propagate to the left and
2556 // the right side.
2557 if (current->IsMul()) {
2558 HMul* mul = HMul::cast(current);
2559 mul->EnsureAndPropagateNotMinusZero(visited);
2560 PropagateMinusZeroChecks(mul->left(), visited);
2561 PropagateMinusZeroChecks(mul->right(), visited);
2562 } else if (current->IsDiv()) {
2563 HDiv* div = HDiv::cast(current);
2564 div->EnsureAndPropagateNotMinusZero(visited);
2565 PropagateMinusZeroChecks(div->left(), visited);
2566 PropagateMinusZeroChecks(div->right(), visited);
2567 }
2568
2569 current = current->EnsureAndPropagateNotMinusZero(visited);
2570 }
2571}
2572
2573
2574void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002575 HValue* use_value,
2576 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002577 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002578 // Insert the representation change right before its use. For phi-uses we
2579 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002580 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002581 if (use_value->IsPhi()) {
2582 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002583 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002584 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002585 }
2586
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002587 // For constants we try to make the representation change at compile
2588 // time. When a representation change is not possible without loss of
2589 // information we treat constants like normal instructions and insert the
2590 // change instructions for them.
2591 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002592 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002593 bool deoptimize_on_undefined =
2594 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002595 if (value->IsConstant()) {
2596 HConstant* constant = HConstant::cast(value);
2597 // Try to create a new copy of the constant with the new representation.
2598 new_value = is_truncating
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002599 ? constant->CopyToTruncatedInt32(zone())
2600 : constant->CopyToRepresentation(to, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002601 }
2602
2603 if (new_value == NULL) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002604 new_value = new(zone()) HChange(value, to,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002605 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002606 }
2607
2608 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002609 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002610}
2611
2612
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002613void HGraph::InsertRepresentationChangesForValue(HValue* value) {
2614 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002615 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002616 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002617
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002618 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2619 HValue* use_value = it.value();
2620 int use_index = it.index();
2621 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002622 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002623 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002624 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002625 if (value->HasNoUses()) {
2626 ASSERT(value->IsConstant());
2627 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002628 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00002629
2630 // The only purpose of a HForceRepresentation is to represent the value
2631 // after the (possible) HChange instruction. We make it disappear.
2632 if (value->IsForceRepresentation()) {
2633 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
2634 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002635}
2636
2637
2638void HGraph::InsertRepresentationChanges() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002639 HPhase phase("H_Representation changes", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002640
2641 // Compute truncation flag for phis: Initially assume that all
2642 // int32-phis allow truncation and iteratively remove the ones that
2643 // are used in an operation that does not allow a truncating
2644 // conversion.
2645 // TODO(fschneider): Replace this with a worklist-based iteration.
2646 for (int i = 0; i < phi_list()->length(); i++) {
2647 HPhi* phi = phi_list()->at(i);
2648 if (phi->representation().IsInteger32()) {
2649 phi->SetFlag(HValue::kTruncatingToInt32);
2650 }
2651 }
2652 bool change = true;
2653 while (change) {
2654 change = false;
2655 for (int i = 0; i < phi_list()->length(); i++) {
2656 HPhi* phi = phi_list()->at(i);
2657 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
ulan@chromium.org812308e2012-02-29 15:58:45 +00002658 if (!phi->CheckUsesForFlag(HValue::kTruncatingToInt32)) {
2659 phi->ClearFlag(HValue::kTruncatingToInt32);
2660 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002661 }
2662 }
2663 }
2664
2665 for (int i = 0; i < blocks_.length(); ++i) {
2666 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002667 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
2668 for (int j = 0; j < phis->length(); j++) {
2669 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002670 }
2671
2672 // Process normal instructions.
2673 HInstruction* current = blocks_[i]->first();
2674 while (current != NULL) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002675 InsertRepresentationChangesForValue(current);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002676 current = current->next();
2677 }
2678 }
2679}
2680
2681
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002682void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
2683 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
2684 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
2685 for (int i = 0; i < phi->OperandCount(); ++i) {
2686 HValue* input = phi->OperandAt(i);
2687 if (input->IsPhi()) {
2688 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
2689 }
2690 }
2691}
2692
2693
2694void HGraph::MarkDeoptimizeOnUndefined() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002695 HPhase phase("H_MarkDeoptimizeOnUndefined", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002696 // Compute DeoptimizeOnUndefined flag for phis.
2697 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002698 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002699 // double input representation, has this flag set.
2700 // The flag is used by HChange tagged->double, which must deoptimize
2701 // if one of its uses has this flag set.
2702 for (int i = 0; i < phi_list()->length(); i++) {
2703 HPhi* phi = phi_list()->at(i);
2704 if (phi->representation().IsDouble()) {
2705 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2706 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
2707 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
2708 break;
2709 }
2710 }
2711 }
2712 }
2713}
2714
2715
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00002716void HGraph::ComputeMinusZeroChecks() {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002717 BitVector visited(GetMaximumValueID(), zone());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00002718 for (int i = 0; i < blocks_.length(); ++i) {
2719 for (HInstruction* current = blocks_[i]->first();
2720 current != NULL;
2721 current = current->next()) {
2722 if (current->IsChange()) {
2723 HChange* change = HChange::cast(current);
2724 // Propagate flags for negative zero checks upwards from conversions
2725 // int32-to-tagged and int32-to-double.
2726 Representation from = change->value()->representation();
2727 ASSERT(from.Equals(change->from()));
2728 if (from.IsInteger32()) {
2729 ASSERT(change->to().IsTagged() || change->to().IsDouble());
2730 ASSERT(visited.IsEmpty());
2731 PropagateMinusZeroChecks(change->value(), &visited);
2732 visited.Clear();
2733 }
2734 }
2735 }
2736 }
2737}
2738
2739
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002740// Implementation of utility class to encapsulate the translation state for
2741// a (possibly inlined) function.
2742FunctionState::FunctionState(HGraphBuilder* owner,
2743 CompilationInfo* info,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002744 TypeFeedbackOracle* oracle,
ulan@chromium.org967e2702012-02-28 09:49:15 +00002745 ReturnHandlingFlag return_handling)
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002746 : owner_(owner),
2747 compilation_info_(info),
2748 oracle_(oracle),
2749 call_context_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +00002750 return_handling_(return_handling),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002751 function_return_(NULL),
2752 test_context_(NULL),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002753 entry_(NULL),
2754 arguments_elements_(NULL),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002755 outer_(owner->function_state()) {
2756 if (outer_ != NULL) {
2757 // State for an inline function.
2758 if (owner->ast_context()->IsTest()) {
2759 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
2760 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
2761 if_true->MarkAsInlineReturnTarget();
2762 if_false->MarkAsInlineReturnTarget();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002763 Expression* cond = TestContext::cast(owner->ast_context())->condition();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002764 // The AstContext constructor pushed on the context stack. This newed
2765 // instance is the reason that AstContext can't be BASE_EMBEDDED.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002766 test_context_ = new TestContext(owner, cond, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002767 } else {
2768 function_return_ = owner->graph()->CreateBasicBlock();
2769 function_return()->MarkAsInlineReturnTarget();
2770 }
2771 // Set this after possibly allocating a new TestContext above.
2772 call_context_ = owner->ast_context();
2773 }
2774
2775 // Push on the state stack.
2776 owner->set_function_state(this);
2777}
2778
2779
2780FunctionState::~FunctionState() {
2781 delete test_context_;
2782 owner_->set_function_state(outer_);
2783}
2784
2785
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002786// Implementation of utility classes to represent an expression's context in
2787// the AST.
2788AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002789 : owner_(owner),
2790 kind_(kind),
2791 outer_(owner->ast_context()),
2792 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002793 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002794#ifdef DEBUG
ulan@chromium.org967e2702012-02-28 09:49:15 +00002795 ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002796 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002797#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002798}
2799
2800
2801AstContext::~AstContext() {
2802 owner_->set_ast_context(outer_); // Pop.
2803}
2804
2805
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002806EffectContext::~EffectContext() {
2807 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002808 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002809 (owner()->environment()->length() == original_length_ &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00002810 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002811}
2812
2813
2814ValueContext::~ValueContext() {
2815 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002816 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002817 (owner()->environment()->length() == original_length_ + 1 &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00002818 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002819}
2820
2821
2822void EffectContext::ReturnValue(HValue* value) {
2823 // The value is simply ignored.
2824}
2825
2826
2827void ValueContext::ReturnValue(HValue* value) {
2828 // The value is tracked in the bailout environment, and communicated
2829 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002830 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
2831 owner()->Bailout("bad value context for arguments value");
2832 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002833 owner()->Push(value);
2834}
2835
2836
2837void TestContext::ReturnValue(HValue* value) {
2838 BuildBranch(value);
2839}
2840
2841
2842void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002843 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002844 owner()->AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002845 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002846}
2847
2848
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002849void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002850 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002851 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2852 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2853 instr->SetSuccessorAt(0, empty_true);
2854 instr->SetSuccessorAt(1, empty_false);
2855 owner()->current_block()->Finish(instr);
2856 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
2857 owner()->set_current_block(join);
2858}
2859
2860
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002861void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002862 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002863 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002864 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002865 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002866 owner()->AddInstruction(instr);
2867 owner()->Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002868 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002869}
2870
2871
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002872void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002873 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002874 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
2875 return owner()->Bailout("bad value context for arguments object value");
2876 }
2877 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
2878 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
2879 instr->SetSuccessorAt(0, materialize_true);
2880 instr->SetSuccessorAt(1, materialize_false);
2881 owner()->current_block()->Finish(instr);
2882 owner()->set_current_block(materialize_true);
2883 owner()->Push(owner()->graph()->GetConstantTrue());
2884 owner()->set_current_block(materialize_false);
2885 owner()->Push(owner()->graph()->GetConstantFalse());
2886 HBasicBlock* join =
2887 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
2888 owner()->set_current_block(join);
2889}
2890
2891
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002892void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002893 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002894 HGraphBuilder* builder = owner();
2895 builder->AddInstruction(instr);
2896 // We expect a simulate after every expression with side effects, though
2897 // this one isn't actually needed (and wouldn't work if it were targeted).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002898 if (instr->HasObservableSideEffects()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002899 builder->Push(instr);
2900 builder->AddSimulate(ast_id);
2901 builder->Pop();
2902 }
2903 BuildBranch(instr);
2904}
2905
2906
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002907void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002908 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002909 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2910 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2911 instr->SetSuccessorAt(0, empty_true);
2912 instr->SetSuccessorAt(1, empty_false);
2913 owner()->current_block()->Finish(instr);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002914 empty_true->Goto(if_true(), owner()->function_state());
2915 empty_false->Goto(if_false(), owner()->function_state());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002916 owner()->set_current_block(NULL);
2917}
2918
2919
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002920void TestContext::BuildBranch(HValue* value) {
2921 // We expect the graph to be in edge-split form: there is no edge that
2922 // connects a branch node to a join node. We conservatively ensure that
2923 // property by always adding an empty block on the outgoing edges of this
2924 // branch.
2925 HGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002926 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002927 builder->Bailout("arguments object value in a test context");
2928 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002929 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
2930 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002931 unsigned test_id = condition()->test_id();
2932 ToBooleanStub::Types expected(builder->oracle()->ToBooleanTypes(test_id));
2933 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002934 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002935
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002936 empty_true->Goto(if_true(), owner()->function_state());
2937 empty_false->Goto(if_false(), owner()->function_state());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002938 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002939}
2940
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002941
2942// HGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002943#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002944 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002945 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002946 if (HasStackOverflow()) return; \
2947 } while (false)
2948
2949
danno@chromium.org160a7b02011-04-18 15:51:38 +00002950#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002951 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002952 call; \
2953 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002954 } while (false)
2955
2956
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002957void HGraphBuilder::Bailout(const char* reason) {
2958 if (FLAG_trace_bailout) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002959 SmartArrayPointer<char> name(
2960 info()->shared_info()->DebugName()->ToCString());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002961 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002962 }
2963 SetStackOverflow();
2964}
2965
2966
2967void HGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002968 EffectContext for_effect(this);
2969 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002970}
2971
2972
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002973void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
2974 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002975 Visit(expr);
2976}
2977
2978
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002979void HGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002980 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002981 for_value.set_for_typeof(true);
2982 Visit(expr);
2983}
2984
2985
2986
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002987void HGraphBuilder::VisitForControl(Expression* expr,
2988 HBasicBlock* true_block,
2989 HBasicBlock* false_block) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002990 TestContext for_test(this, expr, true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002991 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002992}
2993
2994
verwaest@chromium.org753aee42012-07-17 16:15:42 +00002995void HGraphBuilder::VisitArgument(Expression* expr) {
2996 CHECK_ALIVE(VisitForValue(expr));
2997 Push(AddInstruction(new(zone()) HPushArgument(Pop())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002998}
2999
3000
3001void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
3002 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003003 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003004 }
3005}
3006
3007
3008void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
3009 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003010 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003011 }
3012}
3013
3014
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003015HGraph* HGraphBuilder::CreateGraph() {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00003016 graph_ = new(zone()) HGraph(info());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003017 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
3018
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003019 {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003020 HPhase phase("H_Block building");
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00003021 CompilationHandleScope handle_scope(info());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003022 current_block_ = graph()->entry_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003023
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003024 Scope* scope = info()->scope();
3025 if (scope->HasIllegalRedeclaration()) {
3026 Bailout("function with illegal redeclaration");
3027 return NULL;
3028 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003029 if (scope->calls_eval()) {
3030 Bailout("function calls eval");
3031 return NULL;
3032 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003033 SetUpScope(scope);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003034
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003035 // Add an edge to the body entry. This is warty: the graph's start
3036 // environment will be used by the Lithium translation as the initial
3037 // environment on graph entry, but it has now been mutated by the
3038 // Hydrogen translation of the instructions in the start block. This
3039 // environment uses values which have not been defined yet. These
3040 // Hydrogen instructions will then be replayed by the Lithium
3041 // translation, so they cannot have an environment effect. The edge to
3042 // the body's entry block (along with some special logic for the start
3043 // block in HInstruction::InsertAfter) seals the start block from
3044 // getting unwanted instructions inserted.
3045 //
3046 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
3047 // Make the Hydrogen instructions in the initial block into Hydrogen
3048 // values (but not instructions), present in the initial environment and
3049 // not replayed by the Lithium translation.
3050 HEnvironment* initial_env = environment()->CopyWithoutHistory();
3051 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
3052 current_block()->Goto(body_entry);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003053 body_entry->SetJoinId(AstNode::kFunctionEntryId);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003054 set_current_block(body_entry);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003055
3056 // Handle implicit declaration of the function name in named function
3057 // expressions before other declarations.
3058 if (scope->is_function_scope() && scope->function() != NULL) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003059 VisitVariableDeclaration(scope->function());
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003060 }
3061 VisitDeclarations(scope->declarations());
3062 AddSimulate(AstNode::kDeclarationsId);
3063
3064 HValue* context = environment()->LookupContext();
3065 AddInstruction(
3066 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
3067
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003068 VisitStatements(info()->function()->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003069 if (HasStackOverflow()) return NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003070
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003071 if (current_block() != NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003072 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003073 current_block()->FinishExit(instr);
3074 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003075 }
3076 }
3077
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003078 return graph();
3079}
3080
3081bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
3082 NoHandleAllocation no_handles;
3083 AssertNoAllocation no_gc;
3084
3085 *bailout_reason = SmartArrayPointer<char>();
3086 OrderBlocks();
3087 AssignDominators();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003088
3089#ifdef DEBUG
3090 // Do a full verify after building the graph and computing dominators.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003091 Verify(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003092#endif
3093
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003094 PropagateDeoptimizingMark();
3095 if (!CheckConstPhiUses()) {
3096 *bailout_reason = SmartArrayPointer<char>(StrDup(
3097 "Unsupported phi use of const variable"));
3098 return false;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003099 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003100 EliminateRedundantPhis();
3101 if (!CheckArgumentsPhiUses()) {
3102 *bailout_reason = SmartArrayPointer<char>(StrDup(
3103 "Unsupported phi use of arguments"));
3104 return false;
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00003105 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003106 if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
3107 CollectPhis();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003108
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003109 if (has_osr_loop_entry()) {
3110 const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00003111 for (int j = 0; j < phis->length(); j++) {
3112 HPhi* phi = phis->at(j);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003113 osr_values()->at(phi->merged_index())->set_incoming_value(phi);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00003114 }
3115 }
3116
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003117 HInferRepresentation rep(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003118 rep.Analyze();
3119
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003120 MarkDeoptimizeOnUndefined();
3121 InsertRepresentationChanges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003122
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003123 InitializeInferredTypes();
3124 Canonicalize();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003125
3126 // Perform common subexpression elimination and loop-invariant code motion.
3127 if (FLAG_use_gvn) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003128 HPhase phase("H_Global value numbering", this);
3129 HGlobalValueNumberer gvn(this, info());
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003130 bool removed_side_effects = gvn.Analyze();
3131 // Trigger a second analysis pass to further eliminate duplicate values that
3132 // could only be discovered by removing side-effect-generating instructions
3133 // during the first pass.
3134 if (FLAG_smi_only_arrays && removed_side_effects) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003135 removed_side_effects = gvn.Analyze();
3136 ASSERT(!removed_side_effects);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003137 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003138 }
3139
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003140 if (FLAG_use_range) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003141 HRangeAnalysis rangeAnalysis(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003142 rangeAnalysis.Analyze();
3143 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003144 ComputeMinusZeroChecks();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003145
3146 // Eliminate redundant stack checks on backwards branches.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003147 HStackCheckEliminator sce(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003148 sce.Process();
3149
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003150 EliminateRedundantBoundsChecks();
3151 DehoistSimpleArrayIndexComputations();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003152
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003153 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003154}
3155
3156
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003157// We try to "factor up" HBoundsCheck instructions towards the root of the
3158// dominator tree.
3159// For now we handle checks where the index is like "exp + int32value".
3160// If in the dominator tree we check "exp + v1" and later (dominated)
3161// "exp + v2", if v2 <= v1 we can safely remove the second check, and if
3162// v2 > v1 we can use v2 in the 1st check and again remove the second.
3163// To do so we keep a dictionary of all checks where the key if the pair
3164// "exp, length".
3165// The class BoundsCheckKey represents this key.
3166class BoundsCheckKey : public ZoneObject {
3167 public:
3168 HValue* IndexBase() const { return index_base_; }
3169 HValue* Length() const { return length_; }
3170
3171 uint32_t Hash() {
3172 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
3173 }
3174
3175 static BoundsCheckKey* Create(Zone* zone,
3176 HBoundsCheck* check,
3177 int32_t* offset) {
3178 HValue* index_base = NULL;
3179 HConstant* constant = NULL;
3180 bool is_sub = false;
3181
3182 if (check->index()->IsAdd()) {
3183 HAdd* index = HAdd::cast(check->index());
3184 if (index->left()->IsConstant()) {
3185 constant = HConstant::cast(index->left());
3186 index_base = index->right();
3187 } else if (index->right()->IsConstant()) {
3188 constant = HConstant::cast(index->right());
3189 index_base = index->left();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003190 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003191 } else if (check->index()->IsSub()) {
3192 HSub* index = HSub::cast(check->index());
3193 is_sub = true;
3194 if (index->left()->IsConstant()) {
3195 constant = HConstant::cast(index->left());
3196 index_base = index->right();
3197 } else if (index->right()->IsConstant()) {
3198 constant = HConstant::cast(index->right());
3199 index_base = index->left();
3200 }
3201 }
3202
3203 if (constant != NULL && constant->HasInteger32Value()) {
3204 *offset = is_sub ? - constant->Integer32Value()
3205 : constant->Integer32Value();
3206 } else {
3207 *offset = 0;
3208 index_base = check->index();
3209 }
3210
3211 return new(zone) BoundsCheckKey(index_base, check->length());
3212 }
3213
3214 private:
3215 BoundsCheckKey(HValue* index_base, HValue* length)
3216 : index_base_(index_base),
3217 length_(length) { }
3218
3219 HValue* index_base_;
3220 HValue* length_;
3221};
3222
3223
3224// Data about each HBoundsCheck that can be eliminated or moved.
3225// It is the "value" in the dictionary indexed by "base-index, length"
3226// (the key is BoundsCheckKey).
3227// We scan the code with a dominator tree traversal.
3228// Traversing the dominator tree we keep a stack (implemented as a singly
3229// linked list) of "data" for each basic block that contains a relevant check
3230// with the same key (the dictionary holds the head of the list).
3231// We also keep all the "data" created for a given basic block in a list, and
3232// use it to "clean up" the dictionary when backtracking in the dominator tree
3233// traversal.
3234// Doing this each dictionary entry always directly points to the check that
3235// is dominating the code being examined now.
3236// We also track the current "offset" of the index expression and use it to
3237// decide if any check is already "covered" (so it can be removed) or not.
3238class BoundsCheckBbData: public ZoneObject {
3239 public:
3240 BoundsCheckKey* Key() const { return key_; }
3241 int32_t LowerOffset() const { return lower_offset_; }
3242 int32_t UpperOffset() const { return upper_offset_; }
3243 HBasicBlock* BasicBlock() const { return basic_block_; }
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003244 HBoundsCheck* LowerCheck() const { return lower_check_; }
3245 HBoundsCheck* UpperCheck() const { return upper_check_; }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003246 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
3247 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
3248
3249 bool OffsetIsCovered(int32_t offset) const {
3250 return offset >= LowerOffset() && offset <= UpperOffset();
3251 }
3252
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003253 bool HasSingleCheck() { return lower_check_ == upper_check_; }
3254
3255 // The goal of this method is to modify either upper_offset_ or
3256 // lower_offset_ so that also new_offset is covered (the covered
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003257 // range grows).
3258 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003259 // The precondition is that new_check follows UpperCheck() and
3260 // LowerCheck() in the same basic block, and that new_offset is not
3261 // covered (otherwise we could simply remove new_check).
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003262 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003263 // If HasSingleCheck() is true then new_check is added as "second check"
3264 // (either upper or lower; note that HasSingleCheck() becomes false).
3265 // Otherwise one of the current checks is modified so that it also covers
3266 // new_offset, and new_check is removed.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003267 void CoverCheck(HBoundsCheck* new_check,
3268 int32_t new_offset) {
3269 ASSERT(new_check->index()->representation().IsInteger32());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003270 bool keep_new_check = false;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003271
3272 if (new_offset > upper_offset_) {
3273 upper_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003274 if (HasSingleCheck()) {
3275 keep_new_check = true;
3276 upper_check_ = new_check;
3277 } else {
3278 BuildOffsetAdd(upper_check_,
3279 &added_upper_index_,
3280 &added_upper_offset_,
3281 Key()->IndexBase(),
3282 new_check->index()->representation(),
3283 new_offset);
3284 upper_check_->SetOperandAt(0, added_upper_index_);
3285 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003286 } else if (new_offset < lower_offset_) {
3287 lower_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003288 if (HasSingleCheck()) {
3289 keep_new_check = true;
3290 lower_check_ = new_check;
3291 } else {
3292 BuildOffsetAdd(lower_check_,
3293 &added_lower_index_,
3294 &added_lower_offset_,
3295 Key()->IndexBase(),
3296 new_check->index()->representation(),
3297 new_offset);
3298 lower_check_->SetOperandAt(0, added_lower_index_);
3299 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003300 } else {
3301 ASSERT(false);
3302 }
3303
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003304 if (!keep_new_check) {
3305 new_check->DeleteAndReplaceWith(NULL);
3306 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003307 }
3308
3309 void RemoveZeroOperations() {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003310 RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
3311 RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003312 }
3313
3314 BoundsCheckBbData(BoundsCheckKey* key,
3315 int32_t lower_offset,
3316 int32_t upper_offset,
3317 HBasicBlock* bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003318 HBoundsCheck* lower_check,
3319 HBoundsCheck* upper_check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003320 BoundsCheckBbData* next_in_bb,
3321 BoundsCheckBbData* father_in_dt)
3322 : key_(key),
3323 lower_offset_(lower_offset),
3324 upper_offset_(upper_offset),
3325 basic_block_(bb),
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003326 lower_check_(lower_check),
3327 upper_check_(upper_check),
3328 added_lower_index_(NULL),
3329 added_lower_offset_(NULL),
3330 added_upper_index_(NULL),
3331 added_upper_offset_(NULL),
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003332 next_in_bb_(next_in_bb),
3333 father_in_dt_(father_in_dt) { }
3334
3335 private:
3336 BoundsCheckKey* key_;
3337 int32_t lower_offset_;
3338 int32_t upper_offset_;
3339 HBasicBlock* basic_block_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003340 HBoundsCheck* lower_check_;
3341 HBoundsCheck* upper_check_;
3342 HAdd* added_lower_index_;
3343 HConstant* added_lower_offset_;
3344 HAdd* added_upper_index_;
3345 HConstant* added_upper_offset_;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003346 BoundsCheckBbData* next_in_bb_;
3347 BoundsCheckBbData* father_in_dt_;
3348
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003349 void BuildOffsetAdd(HBoundsCheck* check,
3350 HAdd** add,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003351 HConstant** constant,
3352 HValue* original_value,
3353 Representation representation,
3354 int32_t new_offset) {
3355 HConstant* new_constant = new(BasicBlock()->zone())
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00003356 HConstant(new_offset, Representation::Integer32());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003357 if (*add == NULL) {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003358 new_constant->InsertBefore(check);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003359 *add = new(BasicBlock()->zone()) HAdd(NULL,
3360 original_value,
3361 new_constant);
3362 (*add)->AssumeRepresentation(representation);
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003363 (*add)->InsertBefore(check);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003364 } else {
3365 new_constant->InsertBefore(*add);
3366 (*constant)->DeleteAndReplaceWith(new_constant);
3367 }
3368 *constant = new_constant;
3369 }
3370
3371 void RemoveZeroAdd(HAdd** add, HConstant** constant) {
3372 if (*add != NULL && (*constant)->Integer32Value() == 0) {
3373 (*add)->DeleteAndReplaceWith((*add)->left());
3374 (*constant)->DeleteAndReplaceWith(NULL);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003375 }
3376 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003377};
3378
3379
3380static bool BoundsCheckKeyMatch(void* key1, void* key2) {
3381 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
3382 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
3383 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
3384}
3385
3386
3387class BoundsCheckTable : private ZoneHashMap {
3388 public:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003389 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003390 return reinterpret_cast<BoundsCheckBbData**>(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003391 &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003392 }
3393
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003394 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone) {
3395 Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003396 }
3397
3398 void Delete(BoundsCheckKey* key) {
3399 Remove(key, key->Hash());
3400 }
3401
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003402 explicit BoundsCheckTable(Zone* zone)
3403 : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
3404 ZoneAllocationPolicy(zone)) { }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003405};
3406
3407
3408// Eliminates checks in bb and recursively in the dominated blocks.
3409// Also replace the results of check instructions with the original value, if
3410// the result is used. This is safe now, since we don't do code motion after
3411// this point. It enables better register allocation since the value produced
3412// by check instructions is really a copy of the original value.
3413void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
3414 BoundsCheckTable* table) {
3415 BoundsCheckBbData* bb_data_list = NULL;
3416
3417 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
3418 if (!i->IsBoundsCheck()) continue;
3419
3420 HBoundsCheck* check = HBoundsCheck::cast(i);
3421 check->ReplaceAllUsesWith(check->index());
3422
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003423 if (!FLAG_array_bounds_checks_elimination) continue;
3424
3425 int32_t offset;
3426 BoundsCheckKey* key =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003427 BoundsCheckKey::Create(zone(), check, &offset);
3428 BoundsCheckBbData** data_p = table->LookupOrInsert(key, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003429 BoundsCheckBbData* data = *data_p;
3430 if (data == NULL) {
3431 bb_data_list = new(zone()) BoundsCheckBbData(key,
3432 offset,
3433 offset,
3434 bb,
3435 check,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003436 check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003437 bb_data_list,
3438 NULL);
3439 *data_p = bb_data_list;
3440 } else if (data->OffsetIsCovered(offset)) {
3441 check->DeleteAndReplaceWith(NULL);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003442 } else if (data->BasicBlock() == bb) {
3443 data->CoverCheck(check, offset);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003444 } else {
3445 int32_t new_lower_offset = offset < data->LowerOffset()
3446 ? offset
3447 : data->LowerOffset();
3448 int32_t new_upper_offset = offset > data->UpperOffset()
3449 ? offset
3450 : data->UpperOffset();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003451 bb_data_list = new(zone()) BoundsCheckBbData(key,
3452 new_lower_offset,
3453 new_upper_offset,
3454 bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00003455 data->LowerCheck(),
3456 data->UpperCheck(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003457 bb_data_list,
3458 data);
3459 table->Insert(key, bb_data_list, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003460 }
3461 }
3462
3463 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
3464 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table);
3465 }
3466
3467 for (BoundsCheckBbData* data = bb_data_list;
3468 data != NULL;
3469 data = data->NextInBasicBlock()) {
3470 data->RemoveZeroOperations();
3471 if (data->FatherInDominatorTree()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003472 table->Insert(data->Key(), data->FatherInDominatorTree(), zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003473 } else {
3474 table->Delete(data->Key());
3475 }
3476 }
3477}
3478
3479
3480void HGraph::EliminateRedundantBoundsChecks() {
3481 HPhase phase("H_Eliminate bounds checks", this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003482 BoundsCheckTable checks_table(zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003483 EliminateRedundantBoundsChecks(entry_block(), &checks_table);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003484}
3485
3486
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00003487static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
3488 HValue* index = array_operation->GetKey();
3489
3490 HConstant* constant;
3491 HValue* subexpression;
3492 int32_t sign;
3493 if (index->IsAdd()) {
3494 sign = 1;
3495 HAdd* add = HAdd::cast(index);
3496 if (add->left()->IsConstant()) {
3497 subexpression = add->right();
3498 constant = HConstant::cast(add->left());
3499 } else if (add->right()->IsConstant()) {
3500 subexpression = add->left();
3501 constant = HConstant::cast(add->right());
3502 } else {
3503 return;
3504 }
3505 } else if (index->IsSub()) {
3506 sign = -1;
3507 HSub* sub = HSub::cast(index);
3508 if (sub->left()->IsConstant()) {
3509 subexpression = sub->right();
3510 constant = HConstant::cast(sub->left());
3511 } else if (sub->right()->IsConstant()) {
3512 subexpression = sub->left();
3513 constant = HConstant::cast(sub->right());
3514 } return;
3515 } else {
3516 return;
3517 }
3518
3519 if (!constant->HasInteger32Value()) return;
3520 int32_t value = constant->Integer32Value() * sign;
3521 // We limit offset values to 30 bits because we want to avoid the risk of
3522 // overflows when the offset is added to the object header size.
3523 if (value >= 1 << 30 || value < 0) return;
3524 array_operation->SetKey(subexpression);
3525 if (index->HasNoUses()) {
3526 index->DeleteAndReplaceWith(NULL);
3527 }
3528 ASSERT(value >= 0);
3529 array_operation->SetIndexOffset(static_cast<uint32_t>(value));
3530 array_operation->SetDehoisted(true);
3531}
3532
3533
3534void HGraph::DehoistSimpleArrayIndexComputations() {
3535 if (!FLAG_array_index_dehoisting) return;
3536
3537 HPhase phase("H_Dehoist index computations", this);
3538 for (int i = 0; i < blocks()->length(); ++i) {
3539 for (HInstruction* instr = blocks()->at(i)->first();
3540 instr != NULL;
3541 instr = instr->next()) {
3542 ArrayInstructionInterface* array_instruction = NULL;
3543 if (instr->IsLoadKeyedFastElement()) {
3544 HLoadKeyedFastElement* op = HLoadKeyedFastElement::cast(instr);
3545 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3546 } else if (instr->IsLoadKeyedFastDoubleElement()) {
3547 HLoadKeyedFastDoubleElement* op =
3548 HLoadKeyedFastDoubleElement::cast(instr);
3549 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3550 } else if (instr->IsLoadKeyedSpecializedArrayElement()) {
3551 HLoadKeyedSpecializedArrayElement* op =
3552 HLoadKeyedSpecializedArrayElement::cast(instr);
3553 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3554 } else if (instr->IsStoreKeyedFastElement()) {
3555 HStoreKeyedFastElement* op = HStoreKeyedFastElement::cast(instr);
3556 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3557 } else if (instr->IsStoreKeyedFastDoubleElement()) {
3558 HStoreKeyedFastDoubleElement* op =
3559 HStoreKeyedFastDoubleElement::cast(instr);
3560 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3561 } else if (instr->IsStoreKeyedSpecializedArrayElement()) {
3562 HStoreKeyedSpecializedArrayElement* op =
3563 HStoreKeyedSpecializedArrayElement::cast(instr);
3564 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3565 } else {
3566 continue;
3567 }
3568 DehoistArrayIndex(array_instruction);
3569 }
3570 }
3571}
3572
3573
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003574HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003575 ASSERT(current_block() != NULL);
3576 current_block()->AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003577 return instr;
3578}
3579
3580
ager@chromium.org04921a82011-06-27 13:21:41 +00003581void HGraphBuilder::AddSimulate(int ast_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003582 ASSERT(current_block() != NULL);
ager@chromium.org04921a82011-06-27 13:21:41 +00003583 current_block()->AddSimulate(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003584}
3585
3586
3587void HGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003588 ASSERT(current_block() != NULL);
3589 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003590}
3591
3592
3593void HGraphBuilder::PushAndAdd(HInstruction* instr) {
3594 Push(instr);
3595 AddInstruction(instr);
3596}
3597
3598
ulan@chromium.org967e2702012-02-28 09:49:15 +00003599template <class Instruction>
3600HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003601 int count = call->argument_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003602 ZoneList<HValue*> arguments(count, zone());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003603 for (int i = 0; i < count; ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003604 arguments.Add(Pop(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003605 }
3606
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003607 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003608 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003609 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003610 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003611}
3612
3613
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003614void HGraphBuilder::SetUpScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003615 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003616 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003617 AddInstruction(undefined_constant);
3618 graph_->set_undefined_constant(undefined_constant);
3619
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00003620 HArgumentsObject* object = new(zone()) HArgumentsObject;
3621 AddInstruction(object);
3622 graph()->SetArgumentsObject(object);
3623
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003624 // Set the initial values of parameters including "this". "This" has
3625 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003626 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
3627
3628 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003629 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003630 environment()->Bind(i, parameter);
3631 }
3632
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003633 // First special is HContext.
3634 HInstruction* context = AddInstruction(new(zone()) HContext);
3635 environment()->BindContext(context);
3636
3637 // Initialize specials and locals to undefined.
3638 for (int i = environment()->parameter_count() + 1;
3639 i < environment()->length();
3640 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003641 environment()->Bind(i, undefined_constant);
3642 }
3643
3644 // Handle the arguments and arguments shadow variables specially (they do
3645 // not have declarations).
3646 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003647 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003648 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003649 }
yangguo@chromium.org154ff992012-03-13 08:09:54 +00003650
yangguo@chromium.org154ff992012-03-13 08:09:54 +00003651 environment()->Bind(scope->arguments(),
3652 graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003653 }
3654}
3655
3656
3657void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
3658 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003659 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003660 }
3661}
3662
3663
3664HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
3665 HBasicBlock* b = graph()->CreateBasicBlock();
3666 b->SetInitialEnvironment(env);
3667 return b;
3668}
3669
3670
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003671HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003672 HBasicBlock* header = graph()->CreateBasicBlock();
3673 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
3674 header->SetInitialEnvironment(entry_env);
3675 header->AttachLoopInformation();
3676 return header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003677}
3678
3679
3680void HGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003681 ASSERT(!HasStackOverflow());
3682 ASSERT(current_block() != NULL);
3683 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003684 if (stmt->scope() != NULL) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003685 return Bailout("ScopedBlock");
3686 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003687 BreakAndContinueInfo break_info(stmt);
3688 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003689 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003690 }
3691 HBasicBlock* break_block = break_info.break_block();
3692 if (break_block != NULL) {
3693 if (current_block() != NULL) current_block()->Goto(break_block);
3694 break_block->SetJoinId(stmt->ExitId());
3695 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003696 }
3697}
3698
3699
3700void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003701 ASSERT(!HasStackOverflow());
3702 ASSERT(current_block() != NULL);
3703 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003704 VisitForEffect(stmt->expression());
3705}
3706
3707
3708void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003709 ASSERT(!HasStackOverflow());
3710 ASSERT(current_block() != NULL);
3711 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003712}
3713
3714
3715void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003716 ASSERT(!HasStackOverflow());
3717 ASSERT(current_block() != NULL);
3718 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003719 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003720 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003721 Visit(stmt->then_statement());
3722 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003723 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003724 Visit(stmt->else_statement());
3725 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003726 HBasicBlock* cond_true = graph()->CreateBasicBlock();
3727 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003728 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003729
danno@chromium.org160a7b02011-04-18 15:51:38 +00003730 if (cond_true->HasPredecessor()) {
3731 cond_true->SetJoinId(stmt->ThenId());
3732 set_current_block(cond_true);
3733 CHECK_BAILOUT(Visit(stmt->then_statement()));
3734 cond_true = current_block();
3735 } else {
3736 cond_true = NULL;
3737 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003738
danno@chromium.org160a7b02011-04-18 15:51:38 +00003739 if (cond_false->HasPredecessor()) {
3740 cond_false->SetJoinId(stmt->ElseId());
3741 set_current_block(cond_false);
3742 CHECK_BAILOUT(Visit(stmt->else_statement()));
3743 cond_false = current_block();
3744 } else {
3745 cond_false = NULL;
3746 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003747
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003748 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003749 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003750 }
3751}
3752
3753
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003754HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
3755 BreakableStatement* stmt,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003756 BreakType type,
3757 int* drop_extra) {
3758 *drop_extra = 0;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003759 BreakAndContinueScope* current = this;
3760 while (current != NULL && current->info()->target() != stmt) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003761 *drop_extra += current->info()->drop_extra();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003762 current = current->next();
3763 }
3764 ASSERT(current != NULL); // Always found (unless stack is malformed).
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003765
3766 if (type == BREAK) {
3767 *drop_extra += current->info()->drop_extra();
3768 }
3769
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003770 HBasicBlock* block = NULL;
3771 switch (type) {
3772 case BREAK:
3773 block = current->info()->break_block();
3774 if (block == NULL) {
3775 block = current->owner()->graph()->CreateBasicBlock();
3776 current->info()->set_break_block(block);
3777 }
3778 break;
3779
3780 case CONTINUE:
3781 block = current->info()->continue_block();
3782 if (block == NULL) {
3783 block = current->owner()->graph()->CreateBasicBlock();
3784 current->info()->set_continue_block(block);
3785 }
3786 break;
3787 }
3788
3789 return block;
3790}
3791
3792
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003793void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003794 ASSERT(!HasStackOverflow());
3795 ASSERT(current_block() != NULL);
3796 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003797 int drop_extra = 0;
3798 HBasicBlock* continue_block = break_scope()->Get(stmt->target(),
3799 CONTINUE,
3800 &drop_extra);
3801 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003802 current_block()->Goto(continue_block);
3803 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003804}
3805
3806
3807void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003808 ASSERT(!HasStackOverflow());
3809 ASSERT(current_block() != NULL);
3810 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003811 int drop_extra = 0;
3812 HBasicBlock* break_block = break_scope()->Get(stmt->target(),
3813 BREAK,
3814 &drop_extra);
3815 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003816 current_block()->Goto(break_block);
3817 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003818}
3819
3820
3821void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003822 ASSERT(!HasStackOverflow());
3823 ASSERT(current_block() != NULL);
3824 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003825 AstContext* context = call_context();
3826 if (context == NULL) {
3827 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003828 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003829 HValue* result = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003830 current_block()->FinishExit(new(zone()) HReturn(result));
ulan@chromium.org967e2702012-02-28 09:49:15 +00003831 } else if (function_state()->is_construct()) {
3832 // Return from an inlined construct call. In a test context the return
3833 // value will always evaluate to true, in a value context the return value
3834 // needs to be a JSObject.
3835 if (context->IsTest()) {
3836 TestContext* test = TestContext::cast(context);
3837 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003838 current_block()->Goto(test->if_true(), function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003839 } else if (context->IsEffect()) {
3840 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003841 current_block()->Goto(function_return(), function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003842 } else {
3843 ASSERT(context->IsValue());
3844 CHECK_ALIVE(VisitForValue(stmt->expression()));
3845 HValue* return_value = Pop();
3846 HValue* receiver = environment()->Lookup(0);
3847 HHasInstanceTypeAndBranch* typecheck =
3848 new(zone()) HHasInstanceTypeAndBranch(return_value,
3849 FIRST_SPEC_OBJECT_TYPE,
3850 LAST_SPEC_OBJECT_TYPE);
3851 HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
3852 HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
3853 typecheck->SetSuccessorAt(0, if_spec_object);
3854 typecheck->SetSuccessorAt(1, not_spec_object);
3855 current_block()->Finish(typecheck);
3856 if_spec_object->AddLeaveInlined(return_value,
3857 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003858 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003859 not_spec_object->AddLeaveInlined(receiver,
3860 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003861 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003862 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003863 } else {
3864 // Return from an inlined function, visit the subexpression in the
3865 // expression context of the call.
3866 if (context->IsTest()) {
3867 TestContext* test = TestContext::cast(context);
3868 VisitForControl(stmt->expression(),
3869 test->if_true(),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003870 test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003871 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003872 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003873 current_block()->Goto(function_return(), function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003874 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003875 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003876 CHECK_ALIVE(VisitForValue(stmt->expression()));
ulan@chromium.org967e2702012-02-28 09:49:15 +00003877 HValue* return_value = Pop();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003878 current_block()->AddLeaveInlined(return_value,
3879 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003880 function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003881 }
3882 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00003883 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003884}
3885
3886
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003887void HGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003888 ASSERT(!HasStackOverflow());
3889 ASSERT(current_block() != NULL);
3890 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003891 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003892}
3893
3894
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003895void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003896 ASSERT(!HasStackOverflow());
3897 ASSERT(current_block() != NULL);
3898 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003899 // We only optimize switch statements with smi-literal smi comparisons,
3900 // with a bounded number of clauses.
3901 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003902 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003903 int clause_count = clauses->length();
3904 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003905 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003906 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003907
erikcorry0ad885c2011-11-21 13:51:57 +00003908 HValue* context = environment()->LookupContext();
3909
danno@chromium.org160a7b02011-04-18 15:51:38 +00003910 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003911 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003912 HValue* tag_value = Pop();
3913 HBasicBlock* first_test_block = current_block();
3914
erikcorry0ad885c2011-11-21 13:51:57 +00003915 SwitchType switch_type = UNKNOWN_SWITCH;
3916
3917 // 1. Extract clause type
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003918 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003919 CaseClause* clause = clauses->at(i);
3920 if (clause->is_default()) continue;
erikcorry0ad885c2011-11-21 13:51:57 +00003921
3922 if (switch_type == UNKNOWN_SWITCH) {
3923 if (clause->label()->IsSmiLiteral()) {
3924 switch_type = SMI_SWITCH;
3925 } else if (clause->label()->IsStringLiteral()) {
3926 switch_type = STRING_SWITCH;
3927 } else {
3928 return Bailout("SwitchStatement: non-literal switch label");
3929 }
3930 } else if ((switch_type == STRING_SWITCH &&
3931 !clause->label()->IsStringLiteral()) ||
3932 (switch_type == SMI_SWITCH &&
3933 !clause->label()->IsSmiLiteral())) {
3934 return Bailout("SwitchStatemnt: mixed label types are not supported");
3935 }
3936 }
3937
3938 HUnaryControlInstruction* string_check = NULL;
3939 HBasicBlock* not_string_block = NULL;
3940
3941 // Test switch's tag value if all clauses are string literals
3942 if (switch_type == STRING_SWITCH) {
3943 string_check = new(zone()) HIsStringAndBranch(tag_value);
3944 first_test_block = graph()->CreateBasicBlock();
3945 not_string_block = graph()->CreateBasicBlock();
3946
3947 string_check->SetSuccessorAt(0, first_test_block);
3948 string_check->SetSuccessorAt(1, not_string_block);
3949 current_block()->Finish(string_check);
3950
3951 set_current_block(first_test_block);
3952 }
3953
3954 // 2. Build all the tests, with dangling true branches
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003955 int default_id = AstNode::kNoNumber;
erikcorry0ad885c2011-11-21 13:51:57 +00003956 for (int i = 0; i < clause_count; ++i) {
3957 CaseClause* clause = clauses->at(i);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003958 if (clause->is_default()) {
3959 default_id = clause->EntryId();
3960 continue;
3961 }
erikcorry0ad885c2011-11-21 13:51:57 +00003962 if (switch_type == SMI_SWITCH) {
3963 clause->RecordTypeFeedback(oracle());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003964 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003965
erikcorry0ad885c2011-11-21 13:51:57 +00003966 // Generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003967 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003968 HValue* label_value = Pop();
erikcorry0ad885c2011-11-21 13:51:57 +00003969
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003970 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
erikcorry0ad885c2011-11-21 13:51:57 +00003971 HBasicBlock* body_block = graph()->CreateBasicBlock();
3972
3973 HControlInstruction* compare;
3974
3975 if (switch_type == SMI_SWITCH) {
3976 if (!clause->IsSmiCompare()) {
3977 // Finish with deoptimize and add uses of enviroment values to
3978 // account for invisible uses.
3979 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
3980 set_current_block(NULL);
3981 break;
3982 }
3983
3984 HCompareIDAndBranch* compare_ =
3985 new(zone()) HCompareIDAndBranch(tag_value,
3986 label_value,
3987 Token::EQ_STRICT);
3988 compare_->SetInputRepresentation(Representation::Integer32());
3989 compare = compare_;
3990 } else {
3991 compare = new(zone()) HStringCompareAndBranch(context, tag_value,
3992 label_value,
3993 Token::EQ_STRICT);
3994 }
3995
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003996 compare->SetSuccessorAt(0, body_block);
3997 compare->SetSuccessorAt(1, next_test_block);
3998 current_block()->Finish(compare);
erikcorry0ad885c2011-11-21 13:51:57 +00003999
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004000 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004001 }
4002
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004003 // Save the current block to use for the default or to join with the
4004 // exit. This block is NULL if we deoptimized.
4005 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004006
erikcorry0ad885c2011-11-21 13:51:57 +00004007 if (not_string_block != NULL) {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004008 int join_id = (default_id != AstNode::kNoNumber)
4009 ? default_id
4010 : stmt->ExitId();
4011 last_block = CreateJoin(last_block, not_string_block, join_id);
erikcorry0ad885c2011-11-21 13:51:57 +00004012 }
4013
4014 // 3. Loop over the clauses and the linked list of tests in lockstep,
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004015 // translating the clause bodies.
4016 HBasicBlock* curr_test_block = first_test_block;
4017 HBasicBlock* fall_through_block = NULL;
erikcorry0ad885c2011-11-21 13:51:57 +00004018
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004019 BreakAndContinueInfo break_info(stmt);
4020 { BreakAndContinueScope push(&break_info, this);
4021 for (int i = 0; i < clause_count; ++i) {
4022 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004023
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004024 // Identify the block where normal (non-fall-through) control flow
4025 // goes to.
4026 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00004027 if (clause->is_default()) {
4028 if (last_block != NULL) {
4029 normal_block = last_block;
4030 last_block = NULL; // Cleared to indicate we've handled it.
4031 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004032 } else if (!curr_test_block->end()->IsDeoptimize()) {
4033 normal_block = curr_test_block->end()->FirstSuccessor();
4034 curr_test_block = curr_test_block->end()->SecondSuccessor();
4035 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004036
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004037 // Identify a block to emit the body into.
4038 if (normal_block == NULL) {
4039 if (fall_through_block == NULL) {
4040 // (a) Unreachable.
4041 if (clause->is_default()) {
4042 continue; // Might still be reachable clause bodies.
4043 } else {
4044 break;
4045 }
4046 } else {
4047 // (b) Reachable only as fall through.
4048 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004049 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004050 } else if (fall_through_block == NULL) {
4051 // (c) Reachable only normally.
4052 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004053 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004054 // (d) Reachable both ways.
4055 HBasicBlock* join = CreateJoin(fall_through_block,
4056 normal_block,
4057 clause->EntryId());
4058 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004059 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004060
danno@chromium.org160a7b02011-04-18 15:51:38 +00004061 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004062 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004063 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004064 }
4065
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004066 // Create an up-to-3-way join. Use the break block if it exists since
4067 // it's already a join block.
4068 HBasicBlock* break_block = break_info.break_block();
4069 if (break_block == NULL) {
4070 set_current_block(CreateJoin(fall_through_block,
4071 last_block,
4072 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004073 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004074 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
4075 if (last_block != NULL) last_block->Goto(break_block);
4076 break_block->SetJoinId(stmt->ExitId());
4077 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004078 }
4079}
4080
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004081
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004082bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004083 return statement->OsrEntryId() == info()->osr_ast_id();
4084}
4085
4086
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004087bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
4088 if (!HasOsrEntryAt(statement)) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004089
4090 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
4091 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
4092 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004093 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004094 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004095
4096 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
4097 non_osr_entry->Goto(loop_predecessor);
4098
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004099 set_current_block(osr_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004100 int osr_entry_id = statement->OsrEntryId();
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004101 int first_expression_index = environment()->first_expression_index();
4102 int length = environment()->length();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004103 ZoneList<HUnknownOSRValue*>* osr_values =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004104 new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004105
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004106 for (int i = 0; i < first_expression_index; ++i) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004107 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
4108 AddInstruction(osr_value);
4109 environment()->Bind(i, osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004110 osr_values->Add(osr_value, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004111 }
4112
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004113 if (first_expression_index != length) {
4114 environment()->Drop(length - first_expression_index);
4115 for (int i = first_expression_index; i < length; ++i) {
4116 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
4117 AddInstruction(osr_value);
4118 environment()->Push(osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004119 osr_values->Add(osr_value, zone());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004120 }
4121 }
4122
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004123 graph()->set_osr_values(osr_values);
4124
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004125 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004126 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004127 HContext* context = new(zone()) HContext;
4128 AddInstruction(context);
4129 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004130 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004131 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004132 set_current_block(loop_predecessor);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004133 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004134}
4135
4136
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004137void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
ager@chromium.org04921a82011-06-27 13:21:41 +00004138 HBasicBlock* loop_entry,
4139 BreakAndContinueInfo* break_info) {
4140 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004141 AddSimulate(stmt->StackCheckId());
4142 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00004143 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004144 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00004145 AddInstruction(stack_check);
4146 ASSERT(loop_entry->IsLoopHeader());
4147 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004148 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00004149}
4150
4151
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004152void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004153 ASSERT(!HasStackOverflow());
4154 ASSERT(current_block() != NULL);
4155 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004156 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004157 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004158 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004159 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004160 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004161 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004162
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004163 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004164 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004165 HBasicBlock* body_exit =
4166 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004167 HBasicBlock* loop_successor = NULL;
4168 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004169 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004170 // The block for a true condition, the actual predecessor block of the
4171 // back edge.
4172 body_exit = graph()->CreateBasicBlock();
4173 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004174 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
4175 if (body_exit->HasPredecessor()) {
4176 body_exit->SetJoinId(stmt->BackEdgeId());
4177 } else {
4178 body_exit = NULL;
4179 }
4180 if (loop_successor->HasPredecessor()) {
4181 loop_successor->SetJoinId(stmt->ExitId());
4182 } else {
4183 loop_successor = NULL;
4184 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004185 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004186 HBasicBlock* loop_exit = CreateLoop(stmt,
4187 loop_entry,
4188 body_exit,
4189 loop_successor,
4190 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004191 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004192}
4193
4194
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004195void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004196 ASSERT(!HasStackOverflow());
4197 ASSERT(current_block() != NULL);
4198 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004199 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004200 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004201 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004202 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004203 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004204 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4205
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004206
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004207 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004208 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004209 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004210 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4211 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004212 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4213 if (body_entry->HasPredecessor()) {
4214 body_entry->SetJoinId(stmt->BodyId());
4215 set_current_block(body_entry);
4216 }
4217 if (loop_successor->HasPredecessor()) {
4218 loop_successor->SetJoinId(stmt->ExitId());
4219 } else {
4220 loop_successor = NULL;
4221 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004222 }
4223
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004224 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004225 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004226 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004227 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004228 HBasicBlock* body_exit =
4229 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004230 HBasicBlock* loop_exit = CreateLoop(stmt,
4231 loop_entry,
4232 body_exit,
4233 loop_successor,
4234 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004235 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004236}
4237
4238
4239void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004240 ASSERT(!HasStackOverflow());
4241 ASSERT(current_block() != NULL);
4242 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004243 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004244 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004245 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004246 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004247 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004248 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004249 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004250 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004251 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004252
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004253 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004254 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004255 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4256 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004257 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4258 if (body_entry->HasPredecessor()) {
4259 body_entry->SetJoinId(stmt->BodyId());
4260 set_current_block(body_entry);
4261 }
4262 if (loop_successor->HasPredecessor()) {
4263 loop_successor->SetJoinId(stmt->ExitId());
4264 } else {
4265 loop_successor = NULL;
4266 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004267 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004268
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004269 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004270 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004271 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004272 }
4273 HBasicBlock* body_exit =
4274 JoinContinue(stmt, current_block(), break_info.continue_block());
4275
4276 if (stmt->next() != NULL && body_exit != NULL) {
4277 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004278 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004279 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004280 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004281
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004282 HBasicBlock* loop_exit = CreateLoop(stmt,
4283 loop_entry,
4284 body_exit,
4285 loop_successor,
4286 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004287 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004288}
4289
4290
4291void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004292 ASSERT(!HasStackOverflow());
4293 ASSERT(current_block() != NULL);
4294 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004295
yangguo@chromium.orga7d3df92012-02-27 11:46:55 +00004296 if (!FLAG_optimize_for_in) {
4297 return Bailout("ForInStatement optimization is disabled");
4298 }
4299
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004300 if (!oracle()->IsForInFastCase(stmt)) {
4301 return Bailout("ForInStatement is not fast case");
4302 }
4303
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004304 if (!stmt->each()->IsVariableProxy() ||
4305 !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
4306 return Bailout("ForInStatement with non-local each variable");
4307 }
4308
4309 Variable* each_var = stmt->each()->AsVariableProxy()->var();
4310
4311 CHECK_ALIVE(VisitForValue(stmt->enumerable()));
4312 HValue* enumerable = Top(); // Leave enumerable at the top.
4313
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004314 HInstruction* map = AddInstruction(new(zone()) HForInPrepareMap(
ulan@chromium.org967e2702012-02-28 09:49:15 +00004315 environment()->LookupContext(), enumerable));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004316 AddSimulate(stmt->PrepareId());
4317
4318 HInstruction* array = AddInstruction(
4319 new(zone()) HForInCacheArray(
4320 enumerable,
4321 map,
4322 DescriptorArray::kEnumCacheBridgeCacheIndex));
4323
4324 HInstruction* array_length = AddInstruction(
4325 new(zone()) HFixedArrayBaseLength(array));
4326
4327 HInstruction* start_index = AddInstruction(new(zone()) HConstant(
4328 Handle<Object>(Smi::FromInt(0)), Representation::Integer32()));
4329
4330 Push(map);
4331 Push(array);
4332 Push(array_length);
4333 Push(start_index);
4334
4335 HInstruction* index_cache = AddInstruction(
4336 new(zone()) HForInCacheArray(
4337 enumerable,
4338 map,
4339 DescriptorArray::kEnumCacheBridgeIndicesCacheIndex));
4340 HForInCacheArray::cast(array)->set_index_cache(
4341 HForInCacheArray::cast(index_cache));
4342
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004343 bool osr_entry = PreProcessOsrEntry(stmt);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004344 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4345 current_block()->Goto(loop_entry);
4346 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004347 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004348
4349 HValue* index = environment()->ExpressionStackAt(0);
4350 HValue* limit = environment()->ExpressionStackAt(1);
4351
4352 // Check that we still have more keys.
4353 HCompareIDAndBranch* compare_index =
4354 new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
4355 compare_index->SetInputRepresentation(Representation::Integer32());
4356
4357 HBasicBlock* loop_body = graph()->CreateBasicBlock();
4358 HBasicBlock* loop_successor = graph()->CreateBasicBlock();
4359
4360 compare_index->SetSuccessorAt(0, loop_body);
4361 compare_index->SetSuccessorAt(1, loop_successor);
4362 current_block()->Finish(compare_index);
4363
4364 set_current_block(loop_successor);
4365 Drop(5);
4366
4367 set_current_block(loop_body);
4368
4369 HValue* key = AddInstruction(
4370 new(zone()) HLoadKeyedFastElement(
4371 environment()->ExpressionStackAt(2), // Enum cache.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004372 environment()->ExpressionStackAt(0))); // Iteration index.
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004373
4374 // Check if the expected map still matches that of the enumerable.
4375 // If not just deoptimize.
4376 AddInstruction(new(zone()) HCheckMapValue(
4377 environment()->ExpressionStackAt(4),
4378 environment()->ExpressionStackAt(3)));
4379
4380 Bind(each_var, key);
4381
4382 BreakAndContinueInfo break_info(stmt, 5);
4383 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
4384
4385 HBasicBlock* body_exit =
4386 JoinContinue(stmt, current_block(), break_info.continue_block());
4387
4388 if (body_exit != NULL) {
4389 set_current_block(body_exit);
4390
4391 HValue* current_index = Pop();
ulan@chromium.org967e2702012-02-28 09:49:15 +00004392 HInstruction* new_index = new(zone()) HAdd(environment()->LookupContext(),
4393 current_index,
4394 graph()->GetConstant1());
4395 new_index->AssumeRepresentation(Representation::Integer32());
4396 PushAndAdd(new_index);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004397 body_exit = current_block();
4398 }
4399
4400 HBasicBlock* loop_exit = CreateLoop(stmt,
4401 loop_entry,
4402 body_exit,
4403 loop_successor,
4404 break_info.break_block());
4405
4406 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004407}
4408
4409
4410void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004411 ASSERT(!HasStackOverflow());
4412 ASSERT(current_block() != NULL);
4413 ASSERT(current_block()->HasPredecessor());
4414 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004415}
4416
4417
4418void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004419 ASSERT(!HasStackOverflow());
4420 ASSERT(current_block() != NULL);
4421 ASSERT(current_block()->HasPredecessor());
4422 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004423}
4424
4425
4426void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004427 ASSERT(!HasStackOverflow());
4428 ASSERT(current_block() != NULL);
4429 ASSERT(current_block()->HasPredecessor());
4430 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004431}
4432
4433
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004434static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
4435 Code* unoptimized_code, FunctionLiteral* expr) {
4436 int start_position = expr->start_position();
4437 RelocIterator it(unoptimized_code);
4438 for (;!it.done(); it.next()) {
4439 RelocInfo* rinfo = it.rinfo();
4440 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
4441 Object* obj = rinfo->target_object();
4442 if (obj->IsSharedFunctionInfo()) {
4443 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
4444 if (shared->start_position() == start_position) {
4445 return Handle<SharedFunctionInfo>(shared);
4446 }
4447 }
4448 }
4449
4450 return Handle<SharedFunctionInfo>();
4451}
4452
4453
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004454void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004455 ASSERT(!HasStackOverflow());
4456 ASSERT(current_block() != NULL);
4457 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004458 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004459 SearchSharedFunctionInfo(info()->shared_info()->code(),
4460 expr);
4461 if (shared_info.is_null()) {
4462 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
4463 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00004464 // We also have a stack overflow if the recursive compilation did.
4465 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004466 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004467 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004468 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004469 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004470}
4471
4472
4473void HGraphBuilder::VisitSharedFunctionInfoLiteral(
4474 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004475 ASSERT(!HasStackOverflow());
4476 ASSERT(current_block() != NULL);
4477 ASSERT(current_block()->HasPredecessor());
4478 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004479}
4480
4481
4482void HGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004483 ASSERT(!HasStackOverflow());
4484 ASSERT(current_block() != NULL);
4485 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004486 HBasicBlock* cond_true = graph()->CreateBasicBlock();
4487 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004488 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004489
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004490 // Visit the true and false subexpressions in the same AST context as the
4491 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004492 if (cond_true->HasPredecessor()) {
4493 cond_true->SetJoinId(expr->ThenId());
4494 set_current_block(cond_true);
4495 CHECK_BAILOUT(Visit(expr->then_expression()));
4496 cond_true = current_block();
4497 } else {
4498 cond_true = NULL;
4499 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004500
danno@chromium.org160a7b02011-04-18 15:51:38 +00004501 if (cond_false->HasPredecessor()) {
4502 cond_false->SetJoinId(expr->ElseId());
4503 set_current_block(cond_false);
4504 CHECK_BAILOUT(Visit(expr->else_expression()));
4505 cond_false = current_block();
4506 } else {
4507 cond_false = NULL;
4508 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004509
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004510 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004511 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004512 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004513 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004514 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004515 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004516 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004517}
4518
4519
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004520HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
4521 Variable* var, LookupResult* lookup, bool is_store) {
4522 if (var->is_this() || !info()->has_global_object()) {
4523 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004524 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004525 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004526 global->Lookup(*var->name(), lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004527 if (!lookup->IsNormal() ||
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004528 (is_store && lookup->IsReadOnly()) ||
4529 lookup->holder() != *global) {
4530 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004531 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004532
4533 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004534}
4535
4536
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004537HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
4538 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004539 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004540 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004541 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004542 HInstruction* context_instruction = new(zone()) HOuterContext(context);
4543 AddInstruction(context_instruction);
4544 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004545 }
4546 return context;
4547}
4548
4549
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004550void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004551 ASSERT(!HasStackOverflow());
4552 ASSERT(current_block() != NULL);
4553 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004554 Variable* variable = expr->var();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004555 switch (variable->location()) {
4556 case Variable::UNALLOCATED: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004557 if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
4558 return Bailout("reference to global harmony declared variable");
4559 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004560 // Handle known global constants like 'undefined' specially to avoid a
4561 // load from a global cell for them.
4562 Handle<Object> constant_value =
4563 isolate()->factory()->GlobalConstantFor(variable->name());
4564 if (!constant_value.is_null()) {
4565 HConstant* instr =
4566 new(zone()) HConstant(constant_value, Representation::Tagged());
4567 return ast_context()->ReturnInstruction(instr, expr->id());
4568 }
4569
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004570 LookupResult lookup(isolate());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004571 GlobalPropertyAccess type =
4572 LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004573
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004574 if (type == kUseCell &&
4575 info()->global_object()->IsAccessCheckNeeded()) {
4576 type = kUseGeneric;
4577 }
4578
4579 if (type == kUseCell) {
4580 Handle<GlobalObject> global(info()->global_object());
4581 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004582 HLoadGlobalCell* instr =
4583 new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004584 return ast_context()->ReturnInstruction(instr, expr->id());
4585 } else {
4586 HValue* context = environment()->LookupContext();
4587 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
4588 AddInstruction(global_object);
4589 HLoadGlobalGeneric* instr =
4590 new(zone()) HLoadGlobalGeneric(context,
4591 global_object,
4592 variable->name(),
4593 ast_context()->is_for_typeof());
4594 instr->set_position(expr->position());
4595 return ast_context()->ReturnInstruction(instr, expr->id());
4596 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004597 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004598
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004599 case Variable::PARAMETER:
4600 case Variable::LOCAL: {
4601 HValue* value = environment()->Lookup(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004602 if (value == graph()->GetConstantHole()) {
4603 ASSERT(variable->mode() == CONST ||
4604 variable->mode() == CONST_HARMONY ||
4605 variable->mode() == LET);
4606 return Bailout("reference to uninitialized variable");
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004607 }
4608 return ast_context()->ReturnValue(value);
4609 }
4610
4611 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004612 HValue* context = BuildContextChainWalk(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004613 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004614 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004615 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004616
4617 case Variable::LOOKUP:
4618 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004619 }
4620}
4621
4622
4623void HGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004624 ASSERT(!HasStackOverflow());
4625 ASSERT(current_block() != NULL);
4626 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004627 HConstant* instr =
4628 new(zone()) HConstant(expr->handle(), Representation::Tagged());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004629 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004630}
4631
4632
4633void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004634 ASSERT(!HasStackOverflow());
4635 ASSERT(current_block() != NULL);
4636 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00004637 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
4638 Handle<FixedArray> literals(closure->literals());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004639 HValue* context = environment()->LookupContext();
4640
4641 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00004642 literals,
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004643 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004644 expr->flags(),
4645 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004646 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004647}
4648
4649
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004650// Determines whether the given array or object literal boilerplate satisfies
4651// all limits to be considered for fast deep-copying and computes the total
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004652// size of all objects that are part of the graph.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004653static bool IsFastLiteral(Handle<JSObject> boilerplate,
4654 int max_depth,
4655 int* max_properties,
4656 int* total_size) {
4657 ASSERT(max_depth >= 0 && *max_properties >= 0);
4658 if (max_depth == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004659
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004660 Handle<FixedArrayBase> elements(boilerplate->elements());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004661 if (elements->length() > 0 &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004662 elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) {
danno@chromium.org88aa0582012-03-23 15:11:57 +00004663 if (boilerplate->HasFastDoubleElements()) {
4664 *total_size += FixedDoubleArray::SizeFor(elements->length());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004665 } else if (boilerplate->HasFastObjectElements()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004666 Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
danno@chromium.org88aa0582012-03-23 15:11:57 +00004667 int length = elements->length();
4668 for (int i = 0; i < length; i++) {
4669 if ((*max_properties)-- == 0) return false;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004670 Handle<Object> value(fast_elements->get(i));
danno@chromium.org88aa0582012-03-23 15:11:57 +00004671 if (value->IsJSObject()) {
4672 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
4673 if (!IsFastLiteral(value_object,
4674 max_depth - 1,
4675 max_properties,
4676 total_size)) {
4677 return false;
4678 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004679 }
4680 }
danno@chromium.org88aa0582012-03-23 15:11:57 +00004681 *total_size += FixedArray::SizeFor(length);
4682 } else {
4683 return false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004684 }
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004685 }
4686
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004687 Handle<FixedArray> properties(boilerplate->properties());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004688 if (properties->length() > 0) {
4689 return false;
4690 } else {
4691 int nof = boilerplate->map()->inobject_properties();
4692 for (int i = 0; i < nof; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004693 if ((*max_properties)-- == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004694 Handle<Object> value(boilerplate->InObjectPropertyAt(i));
4695 if (value->IsJSObject()) {
4696 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004697 if (!IsFastLiteral(value_object,
4698 max_depth - 1,
4699 max_properties,
4700 total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004701 return false;
4702 }
4703 }
4704 }
4705 }
4706
4707 *total_size += boilerplate->map()->instance_size();
4708 return true;
4709}
4710
4711
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004712void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004713 ASSERT(!HasStackOverflow());
4714 ASSERT(current_block() != NULL);
4715 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004716 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004717 HValue* context = environment()->LookupContext();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004718 HInstruction* literal;
4719
4720 // Check whether to use fast or slow deep-copying for boilerplate.
4721 int total_size = 0;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004722 int max_properties = HFastLiteral::kMaxLiteralProperties;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004723 Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()));
4724 if (boilerplate->IsJSObject() &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004725 IsFastLiteral(Handle<JSObject>::cast(boilerplate),
4726 HFastLiteral::kMaxLiteralDepth,
4727 &max_properties,
4728 &total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004729 Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004730 literal = new(zone()) HFastLiteral(context,
4731 boilerplate_object,
4732 total_size,
4733 expr->literal_index(),
4734 expr->depth());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004735 } else {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004736 literal = new(zone()) HObjectLiteral(context,
4737 expr->constant_properties(),
4738 expr->fast_elements(),
4739 expr->literal_index(),
4740 expr->depth(),
4741 expr->has_function());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004742 }
4743
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004744 // The object is expected in the bailout environment during computation
4745 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004746 PushAndAdd(literal);
4747
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004748 expr->CalculateEmitStore(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004749
4750 for (int i = 0; i < expr->properties()->length(); i++) {
4751 ObjectLiteral::Property* property = expr->properties()->at(i);
4752 if (property->IsCompileTimeValue()) continue;
4753
4754 Literal* key = property->key();
4755 Expression* value = property->value();
4756
4757 switch (property->kind()) {
4758 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
4759 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
4760 // Fall through.
4761 case ObjectLiteral::Property::COMPUTED:
4762 if (key->handle()->IsSymbol()) {
4763 if (property->emit_store()) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00004764 property->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004765 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004766 HValue* value = Pop();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004767 HInstruction* store;
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00004768 CHECK_ALIVE(store = BuildStoreNamed(literal,
4769 value,
4770 property->GetReceiverType(),
4771 property->key()));
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004772 AddInstruction(store);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00004773 if (store->HasObservableSideEffects()) AddSimulate(key->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004774 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004775 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004776 }
4777 break;
4778 }
4779 // Fall through.
4780 case ObjectLiteral::Property::PROTOTYPE:
4781 case ObjectLiteral::Property::SETTER:
4782 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00004783 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004784 default: UNREACHABLE();
4785 }
4786 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004787
4788 if (expr->has_function()) {
4789 // Return the result of the transformation to fast properties
4790 // instead of the original since this operation changes the map
4791 // of the object. This makes sure that the original object won't
4792 // be used by other optimized code before it is transformed
4793 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004794 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004795 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004796 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004797 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004798 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004799 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004800}
4801
4802
4803void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004804 ASSERT(!HasStackOverflow());
4805 ASSERT(current_block() != NULL);
4806 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004807 ZoneList<Expression*>* subexprs = expr->values();
4808 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004809 HValue* context = environment()->LookupContext();
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004810 HInstruction* literal;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004811
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004812 Handle<FixedArray> literals(environment()->closure()->literals());
4813 Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
4814
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004815 if (raw_boilerplate->IsUndefined()) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004816 raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
4817 isolate(), literals, expr->constant_elements());
4818 if (raw_boilerplate.is_null()) {
4819 return Bailout("array boilerplate creation failed");
4820 }
4821 literals->set(expr->literal_index(), *raw_boilerplate);
4822 if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
4823 isolate()->heap()->fixed_cow_array_map()) {
4824 isolate()->counters()->cow_arrays_created_runtime()->Increment();
4825 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004826 }
4827
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004828 Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
4829 ElementsKind boilerplate_elements_kind =
4830 Handle<JSObject>::cast(boilerplate)->GetElementsKind();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004831
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004832 // Check whether to use fast or slow deep-copying for boilerplate.
4833 int total_size = 0;
4834 int max_properties = HFastLiteral::kMaxLiteralProperties;
4835 if (IsFastLiteral(boilerplate,
4836 HFastLiteral::kMaxLiteralDepth,
4837 &max_properties,
4838 &total_size)) {
4839 literal = new(zone()) HFastLiteral(context,
4840 boilerplate,
4841 total_size,
4842 expr->literal_index(),
4843 expr->depth());
4844 } else {
4845 literal = new(zone()) HArrayLiteral(context,
4846 boilerplate,
4847 length,
4848 expr->literal_index(),
4849 expr->depth());
4850 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004851
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004852 // The array is expected in the bailout environment during computation
4853 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004854 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004855
4856 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004857
4858 for (int i = 0; i < length; i++) {
4859 Expression* subexpr = subexprs->at(i);
4860 // If the subexpression is a literal or a simple materialized literal it
4861 // is already set in the cloned array.
4862 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
4863
danno@chromium.org160a7b02011-04-18 15:51:38 +00004864 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004865 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004866 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004867
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004868 elements = new(zone()) HLoadElements(literal);
4869 AddInstruction(elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004870
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004871 HValue* key = AddInstruction(
4872 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
4873 Representation::Integer32()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004874
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004875 switch (boilerplate_elements_kind) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004876 case FAST_SMI_ELEMENTS:
4877 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00004878 // Smi-only arrays need a smi check.
4879 AddInstruction(new(zone()) HCheckSmi(value));
4880 // Fall through.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004881 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004882 case FAST_HOLEY_ELEMENTS:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004883 AddInstruction(new(zone()) HStoreKeyedFastElement(
4884 elements,
4885 key,
4886 value,
4887 boilerplate_elements_kind));
4888 break;
4889 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004890 case FAST_HOLEY_DOUBLE_ELEMENTS:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004891 AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements,
4892 key,
4893 value));
4894 break;
4895 default:
4896 UNREACHABLE();
4897 break;
4898 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004899
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004900 AddSimulate(expr->GetIdForElement(i));
4901 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004902 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004903}
4904
4905
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004906// Sets the lookup result and returns true if the load/store can be inlined.
4907static bool ComputeLoadStoreField(Handle<Map> type,
4908 Handle<String> name,
4909 LookupResult* lookup,
4910 bool is_store) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004911 // If we directly find a field, the access can be inlined.
4912 type->LookupDescriptor(NULL, *name, lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004913 if (lookup->IsField()) return true;
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004914
4915 // For a load, we are out of luck if there is no such field.
4916 if (!is_store) return false;
4917
4918 // 2nd chance: A store into a non-existent field can still be inlined if we
4919 // have a matching transition and some room left in the object.
4920 type->LookupTransition(NULL, *name, lookup);
4921 return lookup->IsTransitionToField(*type) &&
4922 (type->unused_property_fields() > 0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004923}
4924
4925
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004926static int ComputeLoadStoreFieldIndex(Handle<Map> type,
4927 Handle<String> name,
4928 LookupResult* lookup) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00004929 ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type));
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004930 if (lookup->IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004931 return lookup->GetLocalFieldIndexFromMap(*type);
4932 } else {
4933 Map* transition = lookup->GetTransitionMapFromMap(*type);
4934 return transition->PropertyIndexFor(*name) - type->inobject_properties();
4935 }
4936}
4937
4938
4939HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
4940 Handle<String> name,
4941 HValue* value,
4942 Handle<Map> type,
4943 LookupResult* lookup,
4944 bool smi_and_map_check) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004945 ASSERT(lookup->IsFound());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004946 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004947 AddInstruction(new(zone()) HCheckNonSmi(object));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004948 AddInstruction(HCheckMaps::NewWithTransitions(object, type, zone()));
4949 }
4950
4951 // If the property does not exist yet, we have to check that it wasn't made
4952 // readonly or turned into a setter by some meanwhile modifications on the
4953 // prototype chain.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00004954 if (!lookup->IsProperty() && type->prototype()->IsJSReceiver()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004955 Object* proto = type->prototype();
4956 // First check that the prototype chain isn't affected already.
4957 LookupResult proto_result(isolate());
4958 proto->Lookup(*name, &proto_result);
4959 if (proto_result.IsProperty()) {
4960 // If the inherited property could induce readonly-ness, bail out.
4961 if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
4962 Bailout("improper object on prototype chain for store");
4963 return NULL;
4964 }
4965 // We only need to check up to the preexisting property.
4966 proto = proto_result.holder();
4967 } else {
4968 // Otherwise, find the top prototype.
4969 while (proto->GetPrototype()->IsJSObject()) proto = proto->GetPrototype();
4970 ASSERT(proto->GetPrototype()->IsNull());
4971 }
4972 ASSERT(proto->IsJSObject());
4973 AddInstruction(new(zone()) HCheckPrototypeMaps(
4974 Handle<JSObject>(JSObject::cast(type->prototype())),
4975 Handle<JSObject>(JSObject::cast(proto))));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004976 }
4977
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004978 int index = ComputeLoadStoreFieldIndex(type, name, lookup);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004979 bool is_in_object = index < 0;
4980 int offset = index * kPointerSize;
4981 if (index < 0) {
4982 // Negative property indices are in-object properties, indexed
4983 // from the end of the fixed part of the object.
4984 offset += type->instance_size();
4985 } else {
4986 offset += FixedArray::kHeaderSize;
4987 }
4988 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004989 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00004990 if (lookup->IsTransitionToField(*type)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004991 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
4992 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00004993 // TODO(fschneider): Record the new map type of the object in the IR to
4994 // enable elimination of redundant checks after the transition store.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00004995 instr->SetGVNFlag(kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004996 }
4997 return instr;
4998}
4999
5000
5001HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
5002 Handle<String> name,
5003 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005004 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005005 return new(zone()) HStoreNamedGeneric(
5006 context,
5007 object,
5008 name,
5009 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005010 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005011}
5012
5013
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005014static void LookupInPrototypes(Handle<Map> map,
5015 Handle<String> name,
5016 LookupResult* lookup) {
5017 while (map->prototype()->IsJSObject()) {
5018 Handle<JSObject> holder(JSObject::cast(map->prototype()));
5019 if (!holder->HasFastProperties()) break;
5020 map = Handle<Map>(holder->map());
5021 map->LookupDescriptor(*holder, *name, lookup);
5022 if (lookup->IsFound()) return;
5023 }
5024 lookup->NotFound();
5025}
5026
5027
5028HInstruction* HGraphBuilder::BuildCallSetter(HValue* obj,
5029 Handle<String> name,
5030 HValue* value,
5031 Handle<Map> map,
5032 Handle<Object> callback,
5033 Handle<JSObject> holder) {
5034 if (!callback->IsAccessorPair()) {
5035 return BuildStoreNamedGeneric(obj, name, value);
5036 }
5037 Handle<Object> setter(Handle<AccessorPair>::cast(callback)->setter());
5038 Handle<JSFunction> function(Handle<JSFunction>::cast(setter));
5039 AddCheckConstantFunction(holder, obj, map, true);
5040 AddInstruction(new(zone()) HPushArgument(obj));
5041 AddInstruction(new(zone()) HPushArgument(value));
5042 return new(zone()) HCallConstantFunction(function, 2);
5043}
5044
5045
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005046HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
5047 HValue* value,
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005048 Handle<Map> type,
5049 Expression* key) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005050 // If we don't know the monomorphic type, do a generic store.
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005051 Handle<String> name = Handle<String>::cast(key->AsLiteral()->handle());
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005052 if (type.is_null()) return BuildStoreNamedGeneric(object, name, value);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005053
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005054 // Handle a store to a known field.
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005055 LookupResult lookup(isolate());
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005056 if (ComputeLoadStoreField(type, name, &lookup, true)) {
5057 // true = needs smi and map check.
5058 return BuildStoreNamedField(object, name, value, type, &lookup, true);
5059 }
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005060
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005061 // Handle a known setter directly in the receiver.
5062 type->LookupDescriptor(NULL, *name, &lookup);
5063 if (lookup.IsPropertyCallbacks()) {
5064 Handle<Object> callback(lookup.GetValueFromMap(*type));
5065 Handle<JSObject> holder;
5066 return BuildCallSetter(object, name, value, type, callback, holder);
5067 }
5068
5069 // Handle a known setter somewhere in the prototype chain.
5070 LookupInPrototypes(type, name, &lookup);
5071 if (lookup.IsPropertyCallbacks()) {
5072 Handle<Object> callback(lookup.GetValue());
5073 Handle<JSObject> holder(lookup.holder());
5074 return BuildCallSetter(object, name, value, type, callback, holder);
5075 }
5076
5077 // No luck, do a generic store.
5078 return BuildStoreNamedGeneric(object, name, value);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005079}
5080
5081
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005082void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
5083 HValue* object,
5084 SmallMapList* types,
5085 Handle<String> name) {
5086 int count = 0;
5087 int previous_field_offset = 0;
5088 bool previous_field_is_in_object = false;
5089 bool is_monomorphic_field = true;
5090 Handle<Map> map;
5091 LookupResult lookup(isolate());
5092 for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
5093 map = types->at(i);
5094 if (ComputeLoadStoreField(map, name, &lookup, false)) {
5095 int index = ComputeLoadStoreFieldIndex(map, name, &lookup);
5096 bool is_in_object = index < 0;
5097 int offset = index * kPointerSize;
5098 if (index < 0) {
5099 // Negative property indices are in-object properties, indexed
5100 // from the end of the fixed part of the object.
5101 offset += map->instance_size();
5102 } else {
5103 offset += FixedArray::kHeaderSize;
5104 }
5105 if (count == 0) {
5106 previous_field_offset = offset;
5107 previous_field_is_in_object = is_in_object;
5108 } else if (is_monomorphic_field) {
5109 is_monomorphic_field = (offset == previous_field_offset) &&
5110 (is_in_object == previous_field_is_in_object);
5111 }
5112 ++count;
5113 }
5114 }
5115
5116 // Use monomorphic load if property lookup results in the same field index
5117 // for all maps. Requires special map check on the set of all handled maps.
5118 HInstruction* instr;
5119 if (count == types->length() && is_monomorphic_field) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005120 AddInstruction(new(zone()) HCheckMaps(object, types, zone()));
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005121 instr = BuildLoadNamedField(object, expr, map, &lookup, false);
5122 } else {
5123 HValue* context = environment()->LookupContext();
5124 instr = new(zone()) HLoadNamedFieldPolymorphic(context,
5125 object,
5126 types,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005127 name,
5128 zone());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005129 }
5130
5131 instr->set_position(expr->position());
5132 return ast_context()->ReturnInstruction(instr, expr->id());
5133}
5134
5135
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005136void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
5137 HValue* object,
5138 HValue* value,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005139 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005140 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005141 // TODO(ager): We should recognize when the prototype chains for different
5142 // maps are identical. In that case we can avoid repeatedly generating the
5143 // same prototype map checks.
5144 int count = 0;
5145 HBasicBlock* join = NULL;
5146 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005147 Handle<Map> map = types->at(i);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005148 LookupResult lookup(isolate());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005149 if (ComputeLoadStoreField(map, name, &lookup, true)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005150 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005151 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005152 join = graph()->CreateBasicBlock();
5153 }
5154 ++count;
5155 HBasicBlock* if_true = graph()->CreateBasicBlock();
5156 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005157 HCompareMap* compare =
5158 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005159 current_block()->Finish(compare);
5160
5161 set_current_block(if_true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005162 HInstruction* instr;
5163 CHECK_ALIVE(instr =
5164 BuildStoreNamedField(object, name, value, map, &lookup, false));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005165 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005166 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005167 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005168 if (!ast_context()->IsEffect()) Push(value);
5169 current_block()->Goto(join);
5170
5171 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005172 }
5173 }
5174
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005175 // Finish up. Unconditionally deoptimize if we've handled all the maps we
5176 // know about and do not want to handle ones we've never seen. Otherwise
5177 // use a generic IC.
5178 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005179 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005180 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005181 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005182 instr->set_position(expr->position());
5183 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005184
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005185 if (join != NULL) {
5186 if (!ast_context()->IsEffect()) Push(value);
5187 current_block()->Goto(join);
5188 } else {
5189 // The HSimulate for the store should not see the stored value in
5190 // effect contexts (it is not materialized at expr->id() in the
5191 // unoptimized code).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005192 if (instr->HasObservableSideEffects()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005193 if (ast_context()->IsEffect()) {
5194 AddSimulate(expr->id());
5195 } else {
5196 Push(value);
5197 AddSimulate(expr->id());
5198 Drop(1);
5199 }
5200 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005201 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00005202 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00005203 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005204
5205 ASSERT(join != NULL);
5206 join->SetJoinId(expr->id());
5207 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005208 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005209}
5210
5211
5212void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
5213 Property* prop = expr->target()->AsProperty();
5214 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005215 expr->RecordTypeFeedback(oracle(), zone());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005216 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005217
5218 HValue* value = NULL;
5219 HInstruction* instr = NULL;
5220
5221 if (prop->key()->IsPropertyName()) {
5222 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005223 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005224 value = Pop();
5225 HValue* object = Pop();
5226
5227 Literal* key = prop->key()->AsLiteral();
5228 Handle<String> name = Handle<String>::cast(key->handle());
5229 ASSERT(!name.is_null());
5230
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005231 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005232 if (expr->IsMonomorphic()) {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005233 CHECK_ALIVE(instr = BuildStoreNamed(object,
5234 value,
5235 types->first(),
5236 prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005237
5238 } else if (types != NULL && types->length() > 1) {
5239 HandlePolymorphicStoreNamedField(expr, object, value, types, name);
5240 return;
5241
5242 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005243 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005244 }
5245
5246 } else {
5247 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005248 CHECK_ALIVE(VisitForValue(prop->key()));
5249 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005250 value = Pop();
5251 HValue* key = Pop();
5252 HValue* object = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00005253 bool has_side_effects = false;
5254 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
5255 expr->position(),
5256 true, // is_store
5257 &has_side_effects);
5258 Push(value);
5259 ASSERT(has_side_effects); // Stores always have side effects.
5260 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005261 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005262 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005263 Push(value);
5264 instr->set_position(expr->position());
5265 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005266 if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005267 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005268}
5269
5270
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005271// Because not every expression has a position and there is not common
5272// superclass of Assignment and CountOperation, we cannot just pass the
5273// owning expression instead of position and ast_id separately.
5274void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005275 HValue* value,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005276 int position,
5277 int ast_id) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005278 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005279 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
5280 if (type == kUseCell) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005281 Handle<GlobalObject> global(info()->global_object());
5282 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005283 HInstruction* instr =
5284 new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005285 instr->set_position(position);
5286 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005287 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005288 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005289 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005290 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
5291 AddInstruction(global_object);
5292 HStoreGlobalGeneric* instr =
5293 new(zone()) HStoreGlobalGeneric(context,
5294 global_object,
5295 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005296 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005297 function_strict_mode_flag());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005298 instr->set_position(position);
5299 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005300 ASSERT(instr->HasObservableSideEffects());
5301 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005302 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005303}
5304
5305
5306void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
5307 Expression* target = expr->target();
5308 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005309 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005310 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005311
5312 // We have a second position recorded in the FullCodeGenerator to have
5313 // type feedback for the binary operation.
5314 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005315
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005316 if (proxy != NULL) {
5317 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005318 if (var->mode() == LET) {
5319 return Bailout("unsupported let compound assignment");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005320 }
5321
danno@chromium.org160a7b02011-04-18 15:51:38 +00005322 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005323
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005324 switch (var->location()) {
5325 case Variable::UNALLOCATED:
5326 HandleGlobalVariableAssignment(var,
5327 Top(),
5328 expr->position(),
5329 expr->AssignmentId());
5330 break;
5331
5332 case Variable::PARAMETER:
5333 case Variable::LOCAL:
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005334 if (var->mode() == CONST) {
5335 return Bailout("unsupported const compound assignment");
5336 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005337 Bind(var, Top());
5338 break;
5339
5340 case Variable::CONTEXT: {
5341 // Bail out if we try to mutate a parameter value in a function
5342 // using the arguments object. We do not (yet) correctly handle the
5343 // arguments property of the function.
5344 if (info()->scope()->arguments() != NULL) {
5345 // Parameters will be allocated to context slots. We have no
5346 // direct way to detect that the variable is a parameter so we do
5347 // a linear search of the parameter variables.
5348 int count = info()->scope()->num_parameters();
5349 for (int i = 0; i < count; ++i) {
5350 if (var == info()->scope()->parameter(i)) {
5351 Bailout(
5352 "assignment to parameter, function uses arguments object");
5353 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005354 }
5355 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005356
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005357 HStoreContextSlot::Mode mode;
5358
5359 switch (var->mode()) {
5360 case LET:
5361 mode = HStoreContextSlot::kCheckDeoptimize;
5362 break;
5363 case CONST:
5364 return ast_context()->ReturnValue(Pop());
5365 case CONST_HARMONY:
5366 // This case is checked statically so no need to
5367 // perform checks here
5368 UNREACHABLE();
5369 default:
5370 mode = HStoreContextSlot::kNoCheck;
5371 }
5372
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005373 HValue* context = BuildContextChainWalk(var);
5374 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005375 new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005376 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005377 if (instr->HasObservableSideEffects()) {
5378 AddSimulate(expr->AssignmentId());
5379 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005380 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005381 }
5382
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005383 case Variable::LOOKUP:
5384 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005385 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005386 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005387
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005388 } else if (prop != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005389 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005390
5391 if (prop->key()->IsPropertyName()) {
5392 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005393 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005394 HValue* obj = Top();
5395
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005396 Handle<Map> map;
5397 HInstruction* load;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005398 if (prop->IsMonomorphic()) {
5399 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005400 map = prop->GetReceiverTypes()->first();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005401 load = BuildLoadNamed(obj, prop, map, name);
5402 } else {
5403 load = BuildLoadNamedGeneric(obj, prop);
5404 }
5405 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005406 if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005407
danno@chromium.org160a7b02011-04-18 15:51:38 +00005408 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005409 HValue* right = Pop();
5410 HValue* left = Pop();
5411
5412 HInstruction* instr = BuildBinaryOperation(operation, left, right);
5413 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005414 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005415
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005416 HInstruction* store;
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005417 CHECK_ALIVE(store = BuildStoreNamed(obj, instr, map, prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005418 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005419 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005420 Drop(2);
5421 Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005422 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005423 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005424
5425 } else {
5426 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005427 CHECK_ALIVE(VisitForValue(prop->obj()));
5428 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005429 HValue* obj = environment()->ExpressionStackAt(1);
5430 HValue* key = environment()->ExpressionStackAt(0);
5431
whesse@chromium.org7b260152011-06-20 15:33:18 +00005432 bool has_side_effects = false;
5433 HValue* load = HandleKeyedElementAccess(
5434 obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
5435 false, // is_store
5436 &has_side_effects);
5437 Push(load);
5438 if (has_side_effects) AddSimulate(expr->CompoundLoadId());
5439
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005440
danno@chromium.org160a7b02011-04-18 15:51:38 +00005441 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005442 HValue* right = Pop();
5443 HValue* left = Pop();
5444
5445 HInstruction* instr = BuildBinaryOperation(operation, left, right);
5446 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005447 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005448
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005449 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005450 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
5451 RelocInfo::kNoPosition,
5452 true, // is_store
5453 &has_side_effects);
5454
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005455 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005456 Drop(3);
5457 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005458 ASSERT(has_side_effects); // Stores always have side effects.
5459 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005460 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005461 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005462
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005463 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005464 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005465 }
5466}
5467
5468
5469void HGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005470 ASSERT(!HasStackOverflow());
5471 ASSERT(current_block() != NULL);
5472 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005473 VariableProxy* proxy = expr->target()->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005474 Property* prop = expr->target()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005475 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005476
5477 if (expr->is_compound()) {
5478 HandleCompoundAssignment(expr);
5479 return;
5480 }
5481
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005482 if (prop != NULL) {
5483 HandlePropertyAssignment(expr);
5484 } else if (proxy != NULL) {
5485 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005486
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00005487 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005488 if (expr->op() != Token::INIT_CONST) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005489 CHECK_ALIVE(VisitForValue(expr->value()));
5490 return ast_context()->ReturnValue(Pop());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005491 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005492
5493 if (var->IsStackAllocated()) {
5494 // We insert a use of the old value to detect unsupported uses of const
5495 // variables (e.g. initialization inside a loop).
5496 HValue* old_value = environment()->Lookup(var);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005497 AddInstruction(new(zone()) HUseConst(old_value));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005498 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005499 } else if (var->mode() == CONST_HARMONY) {
5500 if (expr->op() != Token::INIT_CONST_HARMONY) {
5501 return Bailout("non-initializer assignment to const");
5502 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005503 }
5504
danno@chromium.org160a7b02011-04-18 15:51:38 +00005505 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005506
5507 // Handle the assignment.
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005508 switch (var->location()) {
5509 case Variable::UNALLOCATED:
5510 CHECK_ALIVE(VisitForValue(expr->value()));
5511 HandleGlobalVariableAssignment(var,
5512 Top(),
5513 expr->position(),
5514 expr->AssignmentId());
5515 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005516
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005517 case Variable::PARAMETER:
5518 case Variable::LOCAL: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005519 // Perform an initialization check for let declared variables
5520 // or parameters.
5521 if (var->mode() == LET && expr->op() == Token::ASSIGN) {
5522 HValue* env_value = environment()->Lookup(var);
5523 if (env_value == graph()->GetConstantHole()) {
5524 return Bailout("assignment to let variable before initialization");
5525 }
5526 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005527 // We do not allow the arguments object to occur in a context where it
5528 // may escape, but assignments to stack-allocated locals are
5529 // permitted.
5530 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
5531 HValue* value = Pop();
5532 Bind(var, value);
5533 return ast_context()->ReturnValue(value);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005534 }
5535
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005536 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005537 // Bail out if we try to mutate a parameter value in a function using
5538 // the arguments object. We do not (yet) correctly handle the
5539 // arguments property of the function.
5540 if (info()->scope()->arguments() != NULL) {
5541 // Parameters will rewrite to context slots. We have no direct way
5542 // to detect that the variable is a parameter.
5543 int count = info()->scope()->num_parameters();
5544 for (int i = 0; i < count; ++i) {
5545 if (var == info()->scope()->parameter(i)) {
5546 return Bailout("assignment to parameter in arguments object");
5547 }
5548 }
5549 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005550
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005551 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005552 HStoreContextSlot::Mode mode;
5553 if (expr->op() == Token::ASSIGN) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005554 switch (var->mode()) {
5555 case LET:
5556 mode = HStoreContextSlot::kCheckDeoptimize;
5557 break;
5558 case CONST:
5559 return ast_context()->ReturnValue(Pop());
5560 case CONST_HARMONY:
5561 // This case is checked statically so no need to
5562 // perform checks here
5563 UNREACHABLE();
5564 default:
5565 mode = HStoreContextSlot::kNoCheck;
5566 }
5567 } else if (expr->op() == Token::INIT_VAR ||
5568 expr->op() == Token::INIT_LET ||
5569 expr->op() == Token::INIT_CONST_HARMONY) {
5570 mode = HStoreContextSlot::kNoCheck;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005571 } else {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005572 ASSERT(expr->op() == Token::INIT_CONST);
5573
5574 mode = HStoreContextSlot::kCheckIgnoreAssignment;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005575 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005576
5577 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005578 HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
5579 context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005580 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005581 if (instr->HasObservableSideEffects()) {
5582 AddSimulate(expr->AssignmentId());
5583 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005584 return ast_context()->ReturnValue(Pop());
5585 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005586
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005587 case Variable::LOOKUP:
5588 return Bailout("assignment to LOOKUP variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005589 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005590 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005591 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005592 }
5593}
5594
5595
5596void HGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005597 ASSERT(!HasStackOverflow());
5598 ASSERT(current_block() != NULL);
5599 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005600 // We don't optimize functions with invalid left-hand sides in
5601 // assignments, count operations, or for-in. Consequently throw can
5602 // currently only occur in an effect context.
5603 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005604 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005605
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005606 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005607 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005608 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005609 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00005610 AddInstruction(instr);
5611 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005612 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005613 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005614}
5615
5616
whesse@chromium.org023421e2010-12-21 12:19:12 +00005617HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
5618 Property* expr,
5619 Handle<Map> type,
5620 LookupResult* lookup,
5621 bool smi_and_map_check) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005622 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005623 AddInstruction(new(zone()) HCheckNonSmi(object));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005624 AddInstruction(HCheckMaps::NewWithTransitions(object, type, zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005625 }
5626
5627 int index = lookup->GetLocalFieldIndexFromMap(*type);
5628 if (index < 0) {
5629 // Negative property indices are in-object properties, indexed
5630 // from the end of the fixed part of the object.
5631 int offset = (index * kPointerSize) + type->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005632 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005633 } else {
5634 // Non-negative property indices are in the properties array.
5635 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005636 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005637 }
5638}
5639
5640
5641HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
5642 Property* expr) {
ulan@chromium.org6ff65142012-03-21 09:52:17 +00005643 if (expr->IsUninitialized() && !FLAG_always_opt) {
jkummerow@chromium.org531dfe82012-03-20 13:01:16 +00005644 AddInstruction(new(zone()) HSoftDeoptimize);
5645 current_block()->MarkAsDeoptimizing();
5646 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005647 ASSERT(expr->key()->IsPropertyName());
5648 Handle<Object> name = expr->key()->AsLiteral()->handle();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005649 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005650 return new(zone()) HLoadNamedGeneric(context, obj, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005651}
5652
5653
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005654HInstruction* HGraphBuilder::BuildCallGetter(HValue* obj,
5655 Property* expr,
5656 Handle<Map> map,
5657 Handle<Object> callback,
5658 Handle<JSObject> holder) {
5659 if (!callback->IsAccessorPair()) return BuildLoadNamedGeneric(obj, expr);
5660 Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter());
5661 Handle<JSFunction> function(Handle<JSFunction>::cast(getter));
5662 AddCheckConstantFunction(holder, obj, map, true);
5663 AddInstruction(new(zone()) HPushArgument(obj));
5664 return new(zone()) HCallConstantFunction(function, 1);
5665}
5666
5667
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005668HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
5669 Property* expr,
5670 Handle<Map> map,
5671 Handle<String> name) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005672 LookupResult lookup(isolate());
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00005673 map->LookupDescriptor(NULL, *name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005674 if (lookup.IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005675 return BuildLoadNamedField(obj,
5676 expr,
5677 map,
5678 &lookup,
5679 true);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005680 } else if (lookup.IsConstantFunction()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005681 AddInstruction(new(zone()) HCheckNonSmi(obj));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005682 AddInstruction(HCheckMaps::NewWithTransitions(obj, map, zone()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005683 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005684 return new(zone()) HConstant(function, Representation::Tagged());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005685 } else if (lookup.IsPropertyCallbacks()) {
5686 Handle<Object> callback(lookup.GetValueFromMap(*map));
5687 Handle<JSObject> holder;
5688 return BuildCallGetter(obj, expr, map, callback, holder);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005689 } else {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005690 LookupInPrototypes(map, name, &lookup);
5691 if (lookup.IsPropertyCallbacks()) {
5692 Handle<Object> callback(lookup.GetValue());
5693 Handle<JSObject> holder(lookup.holder());
5694 return BuildCallGetter(obj, expr, map, callback, holder);
5695 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005696 return BuildLoadNamedGeneric(obj, expr);
5697 }
5698}
5699
5700
5701HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
5702 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005703 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005704 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005705}
5706
5707
whesse@chromium.org7b260152011-06-20 15:33:18 +00005708HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
5709 HValue* external_elements,
5710 HValue* checked_key,
5711 HValue* val,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005712 ElementsKind elements_kind,
whesse@chromium.org7b260152011-06-20 15:33:18 +00005713 bool is_store) {
5714 if (is_store) {
5715 ASSERT(val != NULL);
5716 switch (elements_kind) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005717 case EXTERNAL_PIXEL_ELEMENTS: {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005718 val = AddInstruction(new(zone()) HClampToUint8(val));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005719 break;
5720 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005721 case EXTERNAL_BYTE_ELEMENTS:
5722 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5723 case EXTERNAL_SHORT_ELEMENTS:
5724 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5725 case EXTERNAL_INT_ELEMENTS:
5726 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005727 if (!val->representation().IsInteger32()) {
5728 val = AddInstruction(new(zone()) HChange(
5729 val,
5730 Representation::Integer32(),
5731 true, // Truncate to int32.
5732 false)); // Don't deoptimize undefined (irrelevant here).
5733 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005734 break;
5735 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005736 case EXTERNAL_FLOAT_ELEMENTS:
5737 case EXTERNAL_DOUBLE_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00005738 break;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005739 case FAST_SMI_ELEMENTS:
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005740 case FAST_ELEMENTS:
5741 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005742 case FAST_HOLEY_SMI_ELEMENTS:
5743 case FAST_HOLEY_ELEMENTS:
5744 case FAST_HOLEY_DOUBLE_ELEMENTS:
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005745 case DICTIONARY_ELEMENTS:
5746 case NON_STRICT_ARGUMENTS_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00005747 UNREACHABLE();
5748 break;
5749 }
5750 return new(zone()) HStoreKeyedSpecializedArrayElement(
5751 external_elements, checked_key, val, elements_kind);
5752 } else {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005753 ASSERT(val == NULL);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005754 return new(zone()) HLoadKeyedSpecializedArrayElement(
5755 external_elements, checked_key, elements_kind);
5756 }
5757}
5758
5759
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005760HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
5761 HValue* checked_key,
5762 HValue* val,
5763 ElementsKind elements_kind,
5764 bool is_store) {
5765 if (is_store) {
5766 ASSERT(val != NULL);
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005767 switch (elements_kind) {
5768 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005769 case FAST_HOLEY_DOUBLE_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005770 return new(zone()) HStoreKeyedFastDoubleElement(
5771 elements, checked_key, val);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005772 case FAST_SMI_ELEMENTS:
5773 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005774 // Smi-only arrays need a smi check.
5775 AddInstruction(new(zone()) HCheckSmi(val));
5776 // Fall through.
5777 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005778 case FAST_HOLEY_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005779 return new(zone()) HStoreKeyedFastElement(
5780 elements, checked_key, val, elements_kind);
5781 default:
5782 UNREACHABLE();
5783 return NULL;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005784 }
5785 }
5786 // It's an element load (!is_store).
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005787 HoleCheckMode mode = IsFastPackedElementsKind(elements_kind) ?
5788 OMIT_HOLE_CHECK :
5789 PERFORM_HOLE_CHECK;
5790 if (IsFastDoubleElementsKind(elements_kind)) {
5791 return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key, mode);
5792 } else { // Smi or Object elements.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005793 return new(zone()) HLoadKeyedFastElement(elements, checked_key,
5794 elements_kind);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005795 }
5796}
5797
5798
whesse@chromium.org7b260152011-06-20 15:33:18 +00005799HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
5800 HValue* key,
5801 HValue* val,
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005802 HValue* dependency,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005803 Handle<Map> map,
whesse@chromium.org7b260152011-06-20 15:33:18 +00005804 bool is_store) {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005805 HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
5806 zone(), dependency);
5807 AddInstruction(mapcheck);
5808 if (dependency) {
5809 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
5810 }
5811 return BuildUncheckedMonomorphicElementAccess(object, key, val,
5812 mapcheck, map, is_store);
5813}
5814
5815
5816HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
5817 HValue* object,
5818 HValue* key,
5819 HValue* val,
5820 HCheckMaps* mapcheck,
5821 Handle<Map> map,
5822 bool is_store) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005823 // No GVNFlag is necessary for ElementsKind if there is an explicit dependency
5824 // on a HElementsTransition instruction. The flag can also be removed if the
5825 // map to check has FAST_HOLEY_ELEMENTS, since there can be no further
5826 // ElementsKind transitions. Finally, the dependency can be removed for stores
5827 // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
5828 // generated store code.
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005829 if ((map->elements_kind() == FAST_HOLEY_ELEMENTS) ||
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005830 (map->elements_kind() == FAST_ELEMENTS && is_store)) {
5831 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
5832 }
5833 bool fast_smi_only_elements = map->has_fast_smi_elements();
5834 bool fast_elements = map->has_fast_object_elements();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005835 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005836 if (is_store && (fast_elements || fast_smi_only_elements)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005837 HCheckMaps* check_cow_map = new(zone()) HCheckMaps(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005838 elements, isolate()->factory()->fixed_array_map(), zone());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005839 check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
5840 AddInstruction(check_cow_map);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005841 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005842 HInstruction* length = NULL;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005843 HInstruction* checked_key = NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005844 if (map->has_external_array_elements()) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005845 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005846 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
5847 HLoadExternalArrayPointer* external_elements =
5848 new(zone()) HLoadExternalArrayPointer(elements);
5849 AddInstruction(external_elements);
5850 return BuildExternalArrayElementAccess(external_elements, checked_key,
5851 val, map->elements_kind(), is_store);
5852 }
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005853 ASSERT(fast_smi_only_elements ||
5854 fast_elements ||
5855 map->has_fast_double_elements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005856 if (map->instance_type() == JS_ARRAY_TYPE) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005857 length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck,
5858 HType::Smi()));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005859 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005860 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005861 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005862 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005863 return BuildFastElementAccess(elements, checked_key, val,
5864 map->elements_kind(), is_store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005865}
5866
5867
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005868HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
5869 HValue* object,
5870 HValue* key,
5871 HValue* val,
5872 SmallMapList* maps) {
5873 // For polymorphic loads of similar elements kinds (i.e. all tagged or all
5874 // double), always use the "worst case" code without a transition. This is
5875 // much faster than transitioning the elements to the worst case, trading a
5876 // HTransitionElements for a HCheckMaps, and avoiding mutation of the array.
5877 bool has_double_maps = false;
5878 bool has_smi_or_object_maps = false;
5879 bool has_js_array_access = false;
5880 bool has_non_js_array_access = false;
5881 Handle<Map> most_general_consolidated_map;
5882 for (int i = 0; i < maps->length(); ++i) {
5883 Handle<Map> map = maps->at(i);
5884 // Don't allow mixing of JSArrays with JSObjects.
5885 if (map->instance_type() == JS_ARRAY_TYPE) {
5886 if (has_non_js_array_access) return NULL;
5887 has_js_array_access = true;
5888 } else if (has_js_array_access) {
5889 return NULL;
5890 } else {
5891 has_non_js_array_access = true;
5892 }
5893 // Don't allow mixed, incompatible elements kinds.
5894 if (map->has_fast_double_elements()) {
5895 if (has_smi_or_object_maps) return NULL;
5896 has_double_maps = true;
5897 } else if (map->has_fast_smi_or_object_elements()) {
5898 if (has_double_maps) return NULL;
5899 has_smi_or_object_maps = true;
5900 } else {
5901 return NULL;
5902 }
5903 // Remember the most general elements kind, the code for its load will
5904 // properly handle all of the more specific cases.
5905 if ((i == 0) || IsMoreGeneralElementsKindTransition(
5906 most_general_consolidated_map->elements_kind(),
5907 map->elements_kind())) {
5908 most_general_consolidated_map = map;
5909 }
5910 }
5911 if (!has_double_maps && !has_smi_or_object_maps) return NULL;
5912
5913 HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
5914 AddInstruction(check_maps);
5915 HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
5916 object, key, val, check_maps, most_general_consolidated_map, false);
5917 return instr;
5918}
5919
5920
whesse@chromium.org7b260152011-06-20 15:33:18 +00005921HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
5922 HValue* key,
5923 HValue* val,
5924 Expression* prop,
5925 int ast_id,
5926 int position,
5927 bool is_store,
5928 bool* has_side_effects) {
5929 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005930 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005931 SmallMapList* maps = prop->GetReceiverTypes();
whesse@chromium.org7b260152011-06-20 15:33:18 +00005932 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005933
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005934 if (!is_store) {
5935 HInstruction* consolidated_load =
5936 TryBuildConsolidatedElementLoad(object, key, val, maps);
5937 if (consolidated_load != NULL) {
5938 AddInstruction(consolidated_load);
5939 *has_side_effects |= consolidated_load->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005940 if (position != RelocInfo::kNoPosition) {
5941 consolidated_load->set_position(position);
5942 }
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005943 return consolidated_load;
5944 }
5945 }
5946
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005947 static const int kNumElementTypes = kElementsKindCount;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005948 bool type_todo[kNumElementTypes];
5949 for (int i = 0; i < kNumElementTypes; ++i) {
5950 type_todo[i] = false;
5951 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005952
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005953 // Elements_kind transition support.
5954 MapHandleList transition_target(maps->length());
5955 // Collect possible transition targets.
5956 MapHandleList possible_transitioned_maps(maps->length());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005957 for (int i = 0; i < maps->length(); ++i) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005958 Handle<Map> map = maps->at(i);
5959 ElementsKind elements_kind = map->elements_kind();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005960 if (IsFastElementsKind(elements_kind) &&
5961 elements_kind != GetInitialFastElementsKind()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005962 possible_transitioned_maps.Add(map);
5963 }
5964 }
5965 // Get transition target for each map (NULL == no transition).
5966 for (int i = 0; i < maps->length(); ++i) {
5967 Handle<Map> map = maps->at(i);
5968 Handle<Map> transitioned_map =
5969 map->FindTransitionedMap(&possible_transitioned_maps);
5970 transition_target.Add(transitioned_map);
5971 }
5972
5973 int num_untransitionable_maps = 0;
5974 Handle<Map> untransitionable_map;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005975 HTransitionElementsKind* transition = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005976 for (int i = 0; i < maps->length(); ++i) {
5977 Handle<Map> map = maps->at(i);
5978 ASSERT(map->IsMap());
5979 if (!transition_target.at(i).is_null()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005980 ASSERT(Map::IsValidElementsTransition(
5981 map->elements_kind(),
5982 transition_target.at(i)->elements_kind()));
5983 transition = new(zone()) HTransitionElementsKind(
5984 object, map, transition_target.at(i));
5985 AddInstruction(transition);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005986 } else {
5987 type_todo[map->elements_kind()] = true;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005988 if (IsExternalArrayElementsKind(map->elements_kind())) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005989 todo_external_array = true;
5990 }
5991 num_untransitionable_maps++;
5992 untransitionable_map = map;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005993 }
5994 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005995
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005996 // If only one map is left after transitioning, handle this case
5997 // monomorphically.
5998 if (num_untransitionable_maps == 1) {
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005999 HInstruction* instr = NULL;
6000 if (untransitionable_map->has_slow_elements_kind()) {
6001 instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
6002 : BuildLoadKeyedGeneric(object, key));
6003 } else {
6004 instr = AddInstruction(BuildMonomorphicElementAccess(
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006005 object, key, val, transition, untransitionable_map, is_store));
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006006 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006007 *has_side_effects |= instr->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006008 if (position != RelocInfo::kNoPosition) instr->set_position(position);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006009 return is_store ? NULL : instr;
6010 }
6011
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006012 AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006013 HBasicBlock* join = graph()->CreateBasicBlock();
6014
6015 HInstruction* elements_kind_instr =
6016 AddInstruction(new(zone()) HElementsKind(object));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006017 HCompareConstantEqAndBranch* elements_kind_branch = NULL;
6018 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006019 HLoadExternalArrayPointer* external_elements = NULL;
6020 HInstruction* checked_key = NULL;
6021
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006022 // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds
6023 // are handled before external arrays.
6024 STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
6025 STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006026 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
6027 STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006028
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006029 for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006030 elements_kind <= LAST_ELEMENTS_KIND;
6031 elements_kind = ElementsKind(elements_kind + 1)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006032 // After having handled FAST_* and DICTIONARY_ELEMENTS, we need to add some
6033 // code that's executed for all external array cases.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006034 STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
6035 LAST_ELEMENTS_KIND);
6036 if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006037 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00006038 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006039 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006040 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
6041 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
6042 AddInstruction(external_elements);
6043 }
6044 if (type_todo[elements_kind]) {
6045 HBasicBlock* if_true = graph()->CreateBasicBlock();
6046 HBasicBlock* if_false = graph()->CreateBasicBlock();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006047 elements_kind_branch = new(zone()) HCompareConstantEqAndBranch(
6048 elements_kind_instr, elements_kind, Token::EQ_STRICT);
6049 elements_kind_branch->SetSuccessorAt(0, if_true);
6050 elements_kind_branch->SetSuccessorAt(1, if_false);
6051 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006052
6053 set_current_block(if_true);
6054 HInstruction* access;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006055 if (IsFastElementsKind(elements_kind)) {
6056 if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006057 AddInstruction(new(zone()) HCheckMaps(
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006058 elements, isolate()->factory()->fixed_array_map(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006059 zone(), elements_kind_branch));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006060 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006061 // TODO(jkummerow): The need for these two blocks could be avoided
6062 // in one of two ways:
6063 // (1) Introduce ElementsKinds for JSArrays that are distinct from
6064 // those for fast objects.
6065 // (2) Put the common instructions into a third "join" block. This
6066 // requires additional AST IDs that we can deopt to from inside
6067 // that join block. They must be added to the Property class (when
6068 // it's a keyed property) and registered in the full codegen.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006069 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
6070 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006071 HHasInstanceTypeAndBranch* typecheck =
6072 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
6073 typecheck->SetSuccessorAt(0, if_jsarray);
6074 typecheck->SetSuccessorAt(1, if_fastobject);
6075 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006076
6077 set_current_block(if_jsarray);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006078 HInstruction* length;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006079 length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck,
6080 HType::Smi()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006081 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006082 access = AddInstruction(BuildFastElementAccess(
6083 elements, checked_key, val, elements_kind, is_store));
6084 if (!is_store) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00006085 Push(access);
6086 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006087
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006088 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006089 if (position != -1) {
6090 access->set_position(position);
6091 }
6092 if_jsarray->Goto(join);
6093
6094 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006095 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006096 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006097 access = AddInstruction(BuildFastElementAccess(
6098 elements, checked_key, val, elements_kind, is_store));
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006099 } else if (elements_kind == DICTIONARY_ELEMENTS) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006100 if (is_store) {
6101 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
6102 } else {
6103 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
6104 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006105 } else { // External array elements.
6106 access = AddInstruction(BuildExternalArrayElementAccess(
6107 external_elements, checked_key, val, elements_kind, is_store));
6108 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006109 *has_side_effects |= access->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006110 if (position != RelocInfo::kNoPosition) access->set_position(position);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006111 if (!is_store) {
6112 Push(access);
6113 }
6114 current_block()->Goto(join);
6115 set_current_block(if_false);
6116 }
6117 }
6118
6119 // Deopt if none of the cases matched.
6120 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
6121 join->SetJoinId(ast_id);
6122 set_current_block(join);
6123 return is_store ? NULL : Pop();
6124}
6125
6126
6127HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
6128 HValue* key,
6129 HValue* val,
6130 Expression* expr,
6131 int ast_id,
6132 int position,
6133 bool is_store,
6134 bool* has_side_effects) {
6135 ASSERT(!expr->IsPropertyName());
6136 HInstruction* instr = NULL;
6137 if (expr->IsMonomorphic()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006138 Handle<Map> map = expr->GetMonomorphicReceiverType();
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006139 if (map->has_slow_elements_kind()) {
6140 instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
6141 : BuildLoadKeyedGeneric(obj, key);
6142 } else {
6143 AddInstruction(new(zone()) HCheckNonSmi(obj));
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006144 instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006145 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006146 } else if (expr->GetReceiverTypes() != NULL &&
6147 !expr->GetReceiverTypes()->is_empty()) {
6148 return HandlePolymorphicElementAccess(
6149 obj, key, val, expr, ast_id, position, is_store, has_side_effects);
6150 } else {
6151 if (is_store) {
6152 instr = BuildStoreKeyedGeneric(obj, key, val);
6153 } else {
6154 instr = BuildLoadKeyedGeneric(obj, key);
6155 }
6156 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006157 if (position != RelocInfo::kNoPosition) instr->set_position(position);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006158 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006159 *has_side_effects = instr->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006160 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006161}
6162
6163
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006164HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
6165 HValue* key,
6166 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006167 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006168 return new(zone()) HStoreKeyedGeneric(
6169 context,
6170 object,
6171 key,
6172 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006173 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006174}
6175
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006176
6177void HGraphBuilder::EnsureArgumentsArePushedForAccess() {
6178 // Outermost function already has arguments on the stack.
6179 if (function_state()->outer() == NULL) return;
6180
6181 if (function_state()->arguments_pushed()) return;
6182
6183 // Push arguments when entering inlined function.
6184 HEnterInlined* entry = function_state()->entry();
6185
6186 ZoneList<HValue*>* arguments_values = entry->arguments_values();
6187
6188 HInstruction* insert_after = entry;
6189 for (int i = 0; i < arguments_values->length(); i++) {
6190 HValue* argument = arguments_values->at(i);
6191 HInstruction* push_argument = new(zone()) HPushArgument(argument);
6192 push_argument->InsertAfter(insert_after);
6193 insert_after = push_argument;
6194 }
6195
6196 HArgumentsElements* arguments_elements =
6197 new(zone()) HArgumentsElements(true);
6198 arguments_elements->ClearFlag(HValue::kUseGVN);
6199 arguments_elements->InsertAfter(insert_after);
6200 function_state()->set_arguments_elements(arguments_elements);
6201}
6202
6203
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006204bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
6205 VariableProxy* proxy = expr->obj()->AsVariableProxy();
6206 if (proxy == NULL) return false;
6207 if (!proxy->var()->IsStackAllocated()) return false;
6208 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
6209 return false;
6210 }
6211
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006212 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006213 if (expr->key()->IsPropertyName()) {
6214 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
6215 if (!name->IsEqualTo(CStrVector("length"))) return false;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006216
6217 if (function_state()->outer() == NULL) {
6218 HInstruction* elements = AddInstruction(
6219 new(zone()) HArgumentsElements(false));
6220 result = new(zone()) HArgumentsLength(elements);
6221 } else {
6222 // Number of arguments without receiver.
6223 int argument_count = environment()->
6224 arguments_environment()->parameter_count() - 1;
6225 result = new(zone()) HConstant(
6226 Handle<Object>(Smi::FromInt(argument_count)),
6227 Representation::Integer32());
6228 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006229 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006230 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006231 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006232 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006233 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006234 Drop(1); // Arguments object.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006235 if (function_state()->outer() == NULL) {
6236 HInstruction* elements = AddInstruction(
6237 new(zone()) HArgumentsElements(false));
6238 HInstruction* length = AddInstruction(
6239 new(zone()) HArgumentsLength(elements));
6240 HInstruction* checked_key =
6241 AddInstruction(new(zone()) HBoundsCheck(key, length));
6242 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
6243 } else {
6244 EnsureArgumentsArePushedForAccess();
6245
6246 // Number of arguments without receiver.
6247 HInstruction* elements = function_state()->arguments_elements();
6248 int argument_count = environment()->
6249 arguments_environment()->parameter_count() - 1;
6250 HInstruction* length = AddInstruction(new(zone()) HConstant(
6251 Handle<Object>(Smi::FromInt(argument_count)),
6252 Representation::Integer32()));
6253 HInstruction* checked_key =
6254 AddInstruction(new(zone()) HBoundsCheck(key, length));
6255 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
6256 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006257 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006258 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006259 return true;
6260}
6261
6262
6263void HGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006264 ASSERT(!HasStackOverflow());
6265 ASSERT(current_block() != NULL);
6266 ASSERT(current_block()->HasPredecessor());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006267 expr->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006268
6269 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006270
danno@chromium.org160a7b02011-04-18 15:51:38 +00006271 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006272
6273 HInstruction* instr = NULL;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006274 if (expr->AsProperty()->IsArrayLength()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006275 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006276 AddInstruction(new(zone()) HCheckNonSmi(array));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006277 HInstruction* mapcheck =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006278 AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone()));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006279 instr = new(zone()) HJSArrayLength(array, mapcheck);
ager@chromium.org378b34e2011-01-28 08:04:38 +00006280 } else if (expr->IsStringLength()) {
6281 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006282 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006283 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006284 instr = new(zone()) HStringLength(string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006285 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006286 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006287 HValue* index = Pop();
6288 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006289 HValue* context = environment()->LookupContext();
6290 HStringCharCodeAt* char_code =
6291 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006292 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006293 instr = new(zone()) HStringCharFromCode(context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00006294
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006295 } else if (expr->IsFunctionPrototype()) {
6296 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006297 AddInstruction(new(zone()) HCheckNonSmi(function));
6298 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006299
6300 } else if (expr->key()->IsPropertyName()) {
6301 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006302 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006303
6304 HValue* obj = Pop();
6305 if (expr->IsMonomorphic()) {
6306 instr = BuildLoadNamed(obj, expr, types->first(), name);
6307 } else if (types != NULL && types->length() > 1) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006308 AddInstruction(new(zone()) HCheckNonSmi(obj));
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006309 HandlePolymorphicLoadNamedField(expr, obj, types, name);
6310 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006311 } else {
6312 instr = BuildLoadNamedGeneric(obj, expr);
6313 }
6314
6315 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006316 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006317
6318 HValue* key = Pop();
6319 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006320
6321 bool has_side_effects = false;
6322 HValue* load = HandleKeyedElementAccess(
6323 obj, key, NULL, expr, expr->id(), expr->position(),
6324 false, // is_store
6325 &has_side_effects);
6326 if (has_side_effects) {
6327 if (ast_context()->IsEffect()) {
6328 AddSimulate(expr->id());
6329 } else {
6330 Push(load);
6331 AddSimulate(expr->id());
6332 Drop(1);
6333 }
6334 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006335 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006336 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006337 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006338 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006339}
6340
6341
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006342void HGraphBuilder::AddCheckConstantFunction(Handle<JSObject> holder,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006343 HValue* receiver,
6344 Handle<Map> receiver_map,
6345 bool smi_and_map_check) {
6346 // Constant functions have the nice property that the map will change if they
6347 // are overwritten. Therefore it is enough to check the map of the holder and
6348 // its prototypes.
6349 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006350 AddInstruction(new(zone()) HCheckNonSmi(receiver));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006351 AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map,
6352 zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006353 }
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006354 if (!holder.is_null()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006355 AddInstruction(new(zone()) HCheckPrototypeMaps(
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006356 Handle<JSObject>(JSObject::cast(receiver_map->prototype())), holder));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006357 }
6358}
6359
6360
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006361class FunctionSorter {
6362 public:
6363 FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
6364 FunctionSorter(int index, int ticks, int ast_length, int src_length)
6365 : index_(index),
6366 ticks_(ticks),
6367 ast_length_(ast_length),
6368 src_length_(src_length) { }
6369
6370 int index() const { return index_; }
6371 int ticks() const { return ticks_; }
6372 int ast_length() const { return ast_length_; }
6373 int src_length() const { return src_length_; }
6374
6375 private:
6376 int index_;
6377 int ticks_;
6378 int ast_length_;
6379 int src_length_;
6380};
6381
6382
6383static int CompareHotness(void const* a, void const* b) {
6384 FunctionSorter const* function1 = reinterpret_cast<FunctionSorter const*>(a);
6385 FunctionSorter const* function2 = reinterpret_cast<FunctionSorter const*>(b);
6386 int diff = function1->ticks() - function2->ticks();
6387 if (diff != 0) return -diff;
6388 diff = function1->ast_length() - function2->ast_length();
6389 if (diff != 0) return diff;
6390 return function1->src_length() - function2->src_length();
6391}
6392
6393
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006394void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
6395 HValue* receiver,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006396 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006397 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006398 // TODO(ager): We should recognize when the prototype chains for different
6399 // maps are identical. In that case we can avoid repeatedly generating the
6400 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006401 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006402 HBasicBlock* join = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006403 FunctionSorter order[kMaxCallPolymorphism];
6404 int ordered_functions = 0;
6405 for (int i = 0;
6406 i < types->length() && ordered_functions < kMaxCallPolymorphism;
6407 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006408 Handle<Map> map = types->at(i);
6409 if (expr->ComputeTarget(map, name)) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006410 order[ordered_functions++] =
6411 FunctionSorter(i,
6412 expr->target()->shared()->profiler_ticks(),
6413 InliningAstSize(expr->target()),
6414 expr->target()->shared()->SourceSize());
danno@chromium.org2c26cb12012-05-03 09:06:43 +00006415 }
danno@chromium.org2c26cb12012-05-03 09:06:43 +00006416 }
6417
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006418 qsort(reinterpret_cast<void*>(&order[0]),
6419 ordered_functions,
6420 sizeof(order[0]),
6421 &CompareHotness);
6422
6423 for (int fn = 0; fn < ordered_functions; ++fn) {
6424 int i = order[fn].index();
6425 Handle<Map> map = types->at(i);
6426 if (fn == 0) {
6427 // Only needed once.
6428 AddInstruction(new(zone()) HCheckNonSmi(receiver));
6429 join = graph()->CreateBasicBlock();
6430 }
6431 HBasicBlock* if_true = graph()->CreateBasicBlock();
6432 HBasicBlock* if_false = graph()->CreateBasicBlock();
6433 HCompareMap* compare =
6434 new(zone()) HCompareMap(receiver, map, if_true, if_false);
6435 current_block()->Finish(compare);
6436
6437 set_current_block(if_true);
6438 expr->ComputeTarget(map, name);
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006439 AddCheckConstantFunction(expr->holder(), receiver, map, false);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006440 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
6441 Handle<JSFunction> caller = info()->closure();
6442 SmartArrayPointer<char> caller_name =
6443 caller->shared()->DebugName()->ToCString();
6444 PrintF("Trying to inline the polymorphic call to %s from %s\n",
6445 *name->ToCString(),
6446 *caller_name);
6447 }
6448 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
6449 // Trying to inline will signal that we should bailout from the
6450 // entire compilation by setting stack overflow on the visitor.
6451 if (HasStackOverflow()) return;
6452 } else {
6453 HCallConstantFunction* call =
6454 new(zone()) HCallConstantFunction(expr->target(), argument_count);
6455 call->set_position(expr->position());
6456 PreProcessCall(call);
6457 AddInstruction(call);
6458 if (!ast_context()->IsEffect()) Push(call);
6459 }
6460
6461 if (current_block() != NULL) current_block()->Goto(join);
6462 set_current_block(if_false);
6463 }
6464
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006465 // Finish up. Unconditionally deoptimize if we've handled all the maps we
6466 // know about and do not want to handle ones we've never seen. Otherwise
6467 // use a generic IC.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006468 if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006469 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006470 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006471 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006472 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006473 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006474 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00006475
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006476 if (join != NULL) {
6477 AddInstruction(call);
6478 if (!ast_context()->IsEffect()) Push(call);
6479 current_block()->Goto(join);
6480 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006481 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00006482 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006483 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006484
6485 // We assume that control flow is always live after an expression. So
6486 // even without predecessors to the join block, we set it as the exit
6487 // block and continue by adding instructions there.
6488 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006489 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006490 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006491 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006492 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006493 } else {
6494 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006495 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006496}
6497
6498
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006499void HGraphBuilder::TraceInline(Handle<JSFunction> target,
6500 Handle<JSFunction> caller,
6501 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006502 if (FLAG_trace_inlining) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006503 SmartArrayPointer<char> target_name =
6504 target->shared()->DebugName()->ToCString();
6505 SmartArrayPointer<char> caller_name =
6506 caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006507 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006508 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006509 } else {
6510 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006511 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006512 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006513 }
6514}
6515
6516
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006517static const int kNotInlinable = 1000000000;
6518
6519
6520int HGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
6521 if (!FLAG_use_inlining) return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006522
6523 // Precondition: call is monomorphic and we have found a target with the
6524 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006525 Handle<JSFunction> caller = info()->closure();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006526 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006527
6528 // Do a quick check on source code length to avoid parsing large
6529 // inlining candidates.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006530 if (target_shared->SourceSize() >
6531 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006532 TraceInline(target, caller, "target text too big");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006533 return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006534 }
6535
6536 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006537 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006538 TraceInline(target, caller, "target not inlineable");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006539 return kNotInlinable;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006540 }
yangguo@chromium.org56454712012-02-16 15:33:53 +00006541 if (target_shared->dont_inline() || target_shared->dont_optimize()) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006542 TraceInline(target, caller, "target contains unsupported syntax [early]");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006543 return kNotInlinable;
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006544 }
6545
6546 int nodes_added = target_shared->ast_node_count();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006547 return nodes_added;
6548}
6549
6550
6551bool HGraphBuilder::TryInline(CallKind call_kind,
6552 Handle<JSFunction> target,
6553 ZoneList<Expression*>* arguments,
6554 HValue* receiver,
6555 int ast_id,
6556 int return_id,
6557 ReturnHandlingFlag return_handling) {
6558 int nodes_added = InliningAstSize(target);
6559 if (nodes_added == kNotInlinable) return false;
6560
6561 Handle<JSFunction> caller = info()->closure();
6562
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006563 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006564 TraceInline(target, caller, "target AST is too large [early]");
6565 return false;
6566 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006567
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006568 Handle<SharedFunctionInfo> target_shared(target->shared());
6569
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006570#if !defined(V8_TARGET_ARCH_IA32)
6571 // Target must be able to use caller's context.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006572 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006573 if (target->context() != outer_info->closure()->context() ||
6574 outer_info->scope()->contains_with() ||
6575 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006576 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006577 return false;
6578 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006579#endif
6580
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006581
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006582 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006583 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006584 int current_level = 1;
6585 while (env->outer() != NULL) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006586 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006587 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006588 return false;
6589 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00006590 if (env->outer()->frame_type() == JS_FUNCTION) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006591 current_level++;
6592 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006593 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006594 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006595
6596 // Don't inline recursive functions.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006597 for (FunctionState* state = function_state();
6598 state != NULL;
6599 state = state->outer()) {
6600 if (state->compilation_info()->closure()->shared() == *target_shared) {
6601 TraceInline(target, caller, "target is recursive");
6602 return false;
6603 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006604 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006605
6606 // We don't want to add more than a certain number of nodes from inlining.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006607 if (inlined_count_ > Min(FLAG_max_inlined_nodes_cumulative,
6608 kUnlimitedMaxInlinedNodesCumulative)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006609 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006610 return false;
6611 }
6612
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006613 // Parse and allocate variables.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00006614 CompilationInfo target_info(target, zone());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006615 if (!ParserApi::Parse(&target_info, kNoParsingFlags) ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006616 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006617 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006618 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006619 SetStackOverflow();
yangguo@chromium.org56454712012-02-16 15:33:53 +00006620 target_shared->DisableOptimization();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006621 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006622 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006623 return false;
6624 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006625
6626 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006627 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006628 return false;
6629 }
6630 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006631
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006632 // The following conditions must be checked again after re-parsing, because
6633 // earlier the information might not have been complete due to lazy parsing.
6634 nodes_added = function->ast_node_count();
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006635 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006636 TraceInline(target, caller, "target AST is too large [late]");
6637 return false;
6638 }
6639 AstProperties::Flags* flags(function->flags());
6640 if (flags->Contains(kDontInline) || flags->Contains(kDontOptimize)) {
6641 TraceInline(target, caller, "target contains unsupported syntax [late]");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006642 return false;
6643 }
6644
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006645 // If the function uses the arguments object check that inlining of functions
6646 // with arguments object is enabled and the arguments-variable is
6647 // stack allocated.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006648 if (function->scope()->arguments() != NULL) {
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006649 if (!FLAG_inline_arguments) {
6650 TraceInline(target, caller, "target uses arguments object");
6651 return false;
6652 }
6653
6654 if (!function->scope()->arguments()->IsStackAllocated()) {
6655 TraceInline(target,
6656 caller,
6657 "target uses non-stackallocated arguments object");
6658 return false;
6659 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006660 }
6661
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006662 // All declarations must be inlineable.
6663 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
6664 int decl_count = decls->length();
6665 for (int i = 0; i < decl_count; ++i) {
6666 if (!decls->at(i)->IsInlineable()) {
6667 TraceInline(target, caller, "target has non-trivial declaration");
6668 return false;
6669 }
6670 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006671
6672 // Generate the deoptimization data for the unoptimized version of
6673 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006674 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006675 // Note that we compile here using the same AST that we will use for
6676 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006677 target_info.EnableDeoptimizationSupport();
6678 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006679 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006680 return false;
6681 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006682 if (target_shared->scope_info() == ScopeInfo::Empty()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006683 // The scope info might not have been set if a lazily compiled
6684 // function is inlined before being called for the first time.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006685 Handle<ScopeInfo> target_scope_info =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006686 ScopeInfo::Create(target_info.scope(), zone());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006687 target_shared->set_scope_info(*target_scope_info);
6688 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006689 target_shared->EnableDeoptimizationSupport(*target_info.code());
6690 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
6691 &target_info,
6692 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006693 }
6694
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006695 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006696 // After this point, we've made a decision to inline this function (so
6697 // TryInline should always return true).
6698
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006699 // Save the pending call context and type feedback oracle. Set up new ones
6700 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006701 ASSERT(target_shared->has_deoptimization_support());
6702 TypeFeedbackOracle target_oracle(
6703 Handle<Code>(target_shared->code()),
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00006704 Handle<Context>(target->context()->global_context()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006705 isolate(),
6706 zone());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006707 // The function state is new-allocated because we need to delete it
6708 // in two different places.
ulan@chromium.org967e2702012-02-28 09:49:15 +00006709 FunctionState* target_state = new FunctionState(
6710 this, &target_info, &target_oracle, return_handling);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006711
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006712 HConstant* undefined = graph()->GetConstantUndefined();
6713 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00006714 environment()->CopyForInlining(target,
ulan@chromium.org967e2702012-02-28 09:49:15 +00006715 arguments->length(),
lrn@chromium.org1c092762011-05-09 09:42:16 +00006716 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00006717 undefined,
ulan@chromium.org967e2702012-02-28 09:49:15 +00006718 call_kind,
6719 function_state()->is_construct());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006720#ifdef V8_TARGET_ARCH_IA32
6721 // IA32 only, overwrite the caller's context in the deoptimization
6722 // environment with the correct one.
6723 //
6724 // TODO(kmillikin): implement the same inlining on other platforms so we
6725 // can remove the unsightly ifdefs in this function.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006726 HConstant* context =
6727 new(zone()) HConstant(Handle<Context>(target->context()),
6728 Representation::Tagged());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006729 AddInstruction(context);
6730 inner_env->BindContext(context);
6731#endif
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006732
danno@chromium.orgb2a1c072012-03-23 15:47:56 +00006733 AddSimulate(return_id);
6734 current_block()->UpdateEnvironment(inner_env);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006735
6736 ZoneList<HValue*>* arguments_values = NULL;
6737
6738 // If the function uses arguments copy current arguments values
6739 // to use them for materialization.
6740 if (function->scope()->arguments() != NULL) {
6741 HEnvironment* arguments_env = inner_env->arguments_environment();
6742 int arguments_count = arguments_env->parameter_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006743 arguments_values = new(zone()) ZoneList<HValue*>(arguments_count, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006744 for (int i = 0; i < arguments_count; i++) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006745 arguments_values->Add(arguments_env->Lookup(i), zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006746 }
6747 }
6748
6749 HEnterInlined* enter_inlined =
6750 new(zone()) HEnterInlined(target,
6751 arguments->length(),
6752 function,
6753 call_kind,
6754 function_state()->is_construct(),
6755 function->scope()->arguments(),
6756 arguments_values);
6757 function_state()->set_entry(enter_inlined);
6758 AddInstruction(enter_inlined);
6759
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006760 // If the function uses arguments object create and bind one.
6761 if (function->scope()->arguments() != NULL) {
6762 ASSERT(function->scope()->arguments()->IsStackAllocated());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006763 inner_env->Bind(function->scope()->arguments(),
6764 graph()->GetArgumentsObject());
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006765 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006766
6767
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006768 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006769 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006770 if (HasStackOverflow()) {
6771 // Bail out if the inline function did, as we cannot residualize a call
6772 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006773 TraceInline(target, caller, "inline graph construction failed");
yangguo@chromium.org56454712012-02-16 15:33:53 +00006774 target_shared->DisableOptimization();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006775 inline_bailout_ = true;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006776 delete target_state;
danno@chromium.org160a7b02011-04-18 15:51:38 +00006777 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006778 }
6779
6780 // Update inlined nodes count.
6781 inlined_count_ += nodes_added;
6782
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006783 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006784
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006785 if (current_block() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006786 // Add default return value (i.e. undefined for normals calls or the newly
6787 // allocated receiver for construct calls) if control can fall off the
6788 // body. In a test context, undefined is false and any JSObject is true.
6789 if (call_context()->IsValue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006790 ASSERT(function_return() != NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00006791 HValue* return_value = function_state()->is_construct()
6792 ? receiver
6793 : undefined;
6794 current_block()->AddLeaveInlined(return_value,
6795 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006796 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00006797 } else if (call_context()->IsEffect()) {
6798 ASSERT(function_return() != NULL);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006799 current_block()->Goto(function_return(), function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006800 } else {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006801 ASSERT(call_context()->IsTest());
ulan@chromium.org967e2702012-02-28 09:49:15 +00006802 ASSERT(inlined_test_context() != NULL);
6803 HBasicBlock* target = function_state()->is_construct()
6804 ? inlined_test_context()->if_true()
6805 : inlined_test_context()->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006806 current_block()->Goto(target, function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006807 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006808 }
6809
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006810 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006811 if (inlined_test_context() != NULL) {
6812 HBasicBlock* if_true = inlined_test_context()->if_true();
6813 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006814
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006815 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006816 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006817 ClearInlinedTestContext();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006818 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006819
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006820 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006821 if (if_true->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006822 if_true->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006823 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006824 if_true->Goto(true_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006825 }
6826 if (if_false->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006827 if_false->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006828 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006829 if_false->Goto(false_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006830 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006831 set_current_block(NULL);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006832 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006833
danno@chromium.org160a7b02011-04-18 15:51:38 +00006834 } else if (function_return()->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006835 function_return()->SetJoinId(ast_id);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006836 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006837 } else {
6838 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006839 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006840 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006841 return true;
6842}
6843
6844
ulan@chromium.org967e2702012-02-28 09:49:15 +00006845bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
6846 // The function call we are inlining is a method call if the call
6847 // is a property call.
6848 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
6849 ? CALL_AS_FUNCTION
6850 : CALL_AS_METHOD;
6851
6852 return TryInline(call_kind,
6853 expr->target(),
6854 expr->arguments(),
6855 NULL,
6856 expr->id(),
6857 expr->ReturnId(),
6858 drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
6859}
6860
6861
6862bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) {
6863 return TryInline(CALL_AS_FUNCTION,
6864 expr->target(),
6865 expr->arguments(),
6866 receiver,
6867 expr->id(),
6868 expr->ReturnId(),
6869 CONSTRUCT_CALL_RETURN);
6870}
6871
6872
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006873bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) {
6874 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
6875 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
6876 switch (id) {
6877 case kMathRound:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006878 case kMathAbs:
6879 case kMathSqrt:
6880 case kMathLog:
6881 case kMathSin:
6882 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006883 case kMathTan:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006884 if (expr->arguments()->length() == 1) {
6885 HValue* argument = Pop();
6886 HValue* context = environment()->LookupContext();
6887 Drop(1); // Receiver.
6888 HUnaryMathOperation* op =
6889 new(zone()) HUnaryMathOperation(context, argument, id);
6890 op->set_position(expr->position());
6891 if (drop_extra) Drop(1); // Optionally drop the function.
6892 ast_context()->ReturnInstruction(op, expr->id());
6893 return true;
6894 }
6895 break;
6896 default:
6897 // Not supported for inlining yet.
6898 break;
6899 }
6900 return false;
6901}
6902
6903
6904bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr,
6905 HValue* receiver,
6906 Handle<Map> receiver_map,
6907 CheckType check_type) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006908 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006909 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006910 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006911 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006912 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
6913 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006914 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006915 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006916 if (argument_count == 2 && check_type == STRING_CHECK) {
6917 HValue* index = Pop();
6918 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006919 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006920 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006921 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006922 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
6923 expr->holder()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006924 HStringCharCodeAt* char_code =
6925 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006926 if (id == kStringCharCodeAt) {
6927 ast_context()->ReturnInstruction(char_code, expr->id());
6928 return true;
6929 }
6930 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006931 HStringCharFromCode* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006932 new(zone()) HStringCharFromCode(context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006933 ast_context()->ReturnInstruction(result, expr->id());
6934 return true;
6935 }
6936 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006937 case kMathRound:
6938 case kMathFloor:
6939 case kMathAbs:
6940 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006941 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00006942 case kMathSin:
6943 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006944 case kMathTan:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006945 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006946 AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006947 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006948 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006949 Drop(1); // Receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006950 HUnaryMathOperation* op =
6951 new(zone()) HUnaryMathOperation(context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006952 op->set_position(expr->position());
6953 ast_context()->ReturnInstruction(op, expr->id());
6954 return true;
6955 }
6956 break;
6957 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006958 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006959 AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006960 HValue* right = Pop();
6961 HValue* left = Pop();
6962 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006963 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006964 HInstruction* result = NULL;
6965 // Use sqrt() if exponent is 0.5 or -0.5.
6966 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
6967 double exponent = HConstant::cast(right)->DoubleValue();
6968 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006969 result =
6970 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006971 } else if (exponent == -0.5) {
6972 HConstant* double_one =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006973 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
6974 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006975 AddInstruction(double_one);
6976 HUnaryMathOperation* square_root =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006977 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006978 AddInstruction(square_root);
6979 // MathPowHalf doesn't have side effects so there's no need for
6980 // an environment simulation here.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006981 ASSERT(!square_root->HasObservableSideEffects());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006982 result = new(zone()) HDiv(context, double_one, square_root);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006983 } else if (exponent == 2.0) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006984 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006985 }
6986 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006987 HConstant::cast(right)->HasInteger32Value() &&
6988 HConstant::cast(right)->Integer32Value() == 2) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006989 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006990 }
6991
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006992 if (result == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006993 result = new(zone()) HPower(left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006994 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006995 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006996 return true;
6997 }
6998 break;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006999 case kMathRandom:
7000 if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007001 AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007002 Drop(1); // Receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007003 HValue* context = environment()->LookupContext();
7004 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
7005 AddInstruction(global_object);
7006 HRandom* result = new(zone()) HRandom(global_object);
7007 ast_context()->ReturnInstruction(result, expr->id());
7008 return true;
7009 }
7010 break;
7011 case kMathMax:
7012 case kMathMin:
7013 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007014 AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007015 HValue* right = Pop();
7016 HValue* left = Pop();
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007017 Pop(); // Pop receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007018
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00007019 HValue* left_operand = left;
7020 HValue* right_operand = right;
7021
7022 // If we do not have two integers, we convert to double for comparison.
7023 if (!left->representation().IsInteger32() ||
7024 !right->representation().IsInteger32()) {
7025 if (!left->representation().IsDouble()) {
7026 HChange* left_convert = new(zone()) HChange(
7027 left,
7028 Representation::Double(),
7029 false, // Do not truncate when converting to double.
7030 true); // Deoptimize for undefined.
7031 left_convert->SetFlag(HValue::kBailoutOnMinusZero);
7032 left_operand = AddInstruction(left_convert);
7033 }
7034 if (!right->representation().IsDouble()) {
7035 HChange* right_convert = new(zone()) HChange(
7036 right,
7037 Representation::Double(),
7038 false, // Do not truncate when converting to double.
7039 true); // Deoptimize for undefined.
7040 right_convert->SetFlag(HValue::kBailoutOnMinusZero);
7041 right_operand = AddInstruction(right_convert);
7042 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007043 }
7044
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00007045 ASSERT(left_operand->representation().Equals(
7046 right_operand->representation()));
7047 ASSERT(!left_operand->representation().IsTagged());
7048
7049 Token::Value op = (id == kMathMin) ? Token::LT : Token::GT;
7050
7051 HCompareIDAndBranch* compare =
7052 new(zone()) HCompareIDAndBranch(left_operand, right_operand, op);
7053 compare->SetInputRepresentation(left_operand->representation());
7054
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007055 HBasicBlock* return_left = graph()->CreateBasicBlock();
7056 HBasicBlock* return_right = graph()->CreateBasicBlock();
7057
7058 compare->SetSuccessorAt(0, return_left);
7059 compare->SetSuccessorAt(1, return_right);
7060 current_block()->Finish(compare);
7061
7062 set_current_block(return_left);
7063 Push(left);
7064 set_current_block(return_right);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00007065 // The branch above always returns the right operand if either of
7066 // them is NaN, but the spec requires that max/min(NaN, X) = NaN.
7067 // We add another branch that checks if the left operand is NaN or not.
7068 if (left_operand->representation().IsDouble()) {
7069 // If left_operand != left_operand then it is NaN.
7070 HCompareIDAndBranch* compare_nan = new(zone()) HCompareIDAndBranch(
7071 left_operand, left_operand, Token::EQ);
7072 compare_nan->SetInputRepresentation(left_operand->representation());
7073 HBasicBlock* left_is_number = graph()->CreateBasicBlock();
7074 HBasicBlock* left_is_nan = graph()->CreateBasicBlock();
7075 compare_nan->SetSuccessorAt(0, left_is_number);
7076 compare_nan->SetSuccessorAt(1, left_is_nan);
7077 current_block()->Finish(compare_nan);
7078 set_current_block(left_is_nan);
7079 Push(left);
7080 set_current_block(left_is_number);
7081 Push(right);
7082 return_right = CreateJoin(left_is_number, left_is_nan, expr->id());
7083 } else {
7084 Push(right);
7085 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007086
7087 HBasicBlock* join = CreateJoin(return_left, return_right, expr->id());
7088 set_current_block(join);
7089 ast_context()->ReturnValue(Pop());
7090 return true;
7091 }
7092 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007093 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007094 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007095 break;
7096 }
7097 return false;
7098}
7099
7100
7101bool HGraphBuilder::TryCallApply(Call* expr) {
7102 Expression* callee = expr->expression();
7103 Property* prop = callee->AsProperty();
7104 ASSERT(prop != NULL);
7105
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00007106 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
7107 return false;
7108 }
7109 Handle<Map> function_map = expr->GetReceiverTypes()->first();
7110 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
7111 !expr->target()->shared()->HasBuiltinFunctionId() ||
7112 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
7113 return false;
7114 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007115
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00007116 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007117
7118 ZoneList<Expression*>* args = expr->arguments();
7119 if (args->length() != 2) return false;
7120
7121 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00007122 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007123 HValue* arg_two_value = environment()->Lookup(arg_two->var());
7124 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
7125
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007126 // Found pattern f.apply(receiver, arguments).
7127 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007128 if (HasStackOverflow() || current_block() == NULL) return true;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00007129 HValue* function = Top();
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007130 AddCheckConstantFunction(expr->holder(), function, function_map, true);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00007131 Drop(1);
7132
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007133 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00007134 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007135 HValue* receiver = Pop();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007136
7137 if (function_state()->outer() == NULL) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007138 HInstruction* elements = AddInstruction(
7139 new(zone()) HArgumentsElements(false));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007140 HInstruction* length =
7141 AddInstruction(new(zone()) HArgumentsLength(elements));
7142 HValue* wrapped_receiver =
7143 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
7144 HInstruction* result =
7145 new(zone()) HApplyArguments(function,
7146 wrapped_receiver,
7147 length,
7148 elements);
7149 result->set_position(expr->position());
7150 ast_context()->ReturnInstruction(result, expr->id());
7151 return true;
7152 } else {
7153 // We are inside inlined function and we know exactly what is inside
7154 // arguments object.
7155 HValue* context = environment()->LookupContext();
7156
7157 HValue* wrapped_receiver =
7158 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
7159 PushAndAdd(new(zone()) HPushArgument(wrapped_receiver));
7160
ulan@chromium.org6ff65142012-03-21 09:52:17 +00007161 HEnvironment* arguments_env = environment()->arguments_environment();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007162
ulan@chromium.org6ff65142012-03-21 09:52:17 +00007163 int parameter_count = arguments_env->parameter_count();
7164 for (int i = 1; i < arguments_env->parameter_count(); i++) {
7165 PushAndAdd(new(zone()) HPushArgument(arguments_env->Lookup(i)));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007166 }
7167
7168 HInvokeFunction* call = new(zone()) HInvokeFunction(
7169 context,
7170 function,
7171 parameter_count);
7172 Drop(parameter_count);
7173 call->set_position(expr->position());
7174 ast_context()->ReturnInstruction(call, expr->id());
7175 return true;
7176 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007177}
7178
7179
7180void HGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007181 ASSERT(!HasStackOverflow());
7182 ASSERT(current_block() != NULL);
7183 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007184 Expression* callee = expr->expression();
7185 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007186 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007187
7188 Property* prop = callee->AsProperty();
7189 if (prop != NULL) {
7190 if (!prop->key()->IsPropertyName()) {
7191 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007192 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007193
danno@chromium.org160a7b02011-04-18 15:51:38 +00007194 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007195 // Push receiver and key like the non-optimized code generator expects it.
7196 HValue* key = Pop();
7197 HValue* receiver = Pop();
7198 Push(key);
7199 Push(receiver);
7200
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007201 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007202
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007203 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007204 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007205 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007206 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007207 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007208 }
7209
7210 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00007211 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007212
7213 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007214
danno@chromium.org160a7b02011-04-18 15:51:38 +00007215 CHECK_ALIVE(VisitForValue(prop->obj()));
7216 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007217
7218 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
7219
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007220 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007221
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007222 HValue* receiver =
7223 environment()->ExpressionStackAt(expr->arguments()->length());
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007224 if (expr->IsMonomorphic()) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007225 Handle<Map> receiver_map = (types == NULL || types->is_empty())
7226 ? Handle<Map>::null()
7227 : types->first();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007228 if (TryInlineBuiltinMethodCall(expr,
7229 receiver,
7230 receiver_map,
7231 expr->check_type())) {
7232 if (FLAG_trace_inlining) {
7233 PrintF("Inlining builtin ");
7234 expr->target()->ShortPrint();
7235 PrintF("\n");
7236 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007237 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007238 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007239
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007240 if (CallStubCompiler::HasCustomCallGenerator(expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007241 expr->check_type() != RECEIVER_MAP_CHECK) {
7242 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007243 // because it is likely to generate better code. Also use the IC
7244 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007245 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007246 call = PreProcessCall(
7247 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007248 } else {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007249 AddCheckConstantFunction(expr->holder(), receiver, receiver_map, true);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007250
ulan@chromium.org967e2702012-02-28 09:49:15 +00007251 if (TryInlineCall(expr)) return;
danno@chromium.org160a7b02011-04-18 15:51:38 +00007252 call = PreProcessCall(
7253 new(zone()) HCallConstantFunction(expr->target(),
7254 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007255 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007256 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007257 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007258 HandlePolymorphicCallNamed(expr, receiver, types, name);
7259 return;
7260
7261 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007262 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007263 call = PreProcessCall(
7264 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007265 }
7266
7267 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007268 expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007269 VariableProxy* proxy = expr->expression()->AsVariableProxy();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007270 bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007271
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007272 if (proxy != NULL && proxy->var()->is_possibly_eval()) {
7273 return Bailout("possible direct call to eval");
7274 }
7275
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007276 if (global_call) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007277 Variable* var = proxy->var();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007278 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007279 // If there is a global property cell for the name at compile time and
7280 // access check is not enabled we assume that the function will not change
7281 // and generate optimized code for calling the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007282 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007283 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
7284 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007285 !info()->global_object()->IsAccessCheckNeeded()) {
7286 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007287 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007288 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007289 if (known_global_function) {
7290 // Push the global object instead of the global receiver because
7291 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007292 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007293 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007294 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007295 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007296
danno@chromium.org160a7b02011-04-18 15:51:38 +00007297 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007298 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007299 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007300
7301 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007302 HGlobalReceiver* global_receiver =
7303 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007304 // Index of the receiver from the top of the expression stack.
7305 const int receiver_index = argument_count - 1;
7306 AddInstruction(global_receiver);
7307 ASSERT(environment()->ExpressionStackAt(receiver_index)->
7308 IsGlobalObject());
7309 environment()->SetExpressionStackAt(receiver_index, global_receiver);
7310
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007311 if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
7312 if (FLAG_trace_inlining) {
7313 PrintF("Inlining builtin ");
7314 expr->target()->ShortPrint();
7315 PrintF("\n");
7316 }
7317 return;
7318 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00007319 if (TryInlineCall(expr)) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007320
7321 if (expr->target().is_identical_to(info()->closure())) {
7322 graph()->MarkRecursive();
7323 }
7324
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007325 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00007326 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007327 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007328 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007329 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
7330 AddInstruction(receiver);
7331 PushAndAdd(new(zone()) HPushArgument(receiver));
7332 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007333
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007334 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
7335 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007336 }
7337
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007338 } else if (expr->IsMonomorphic()) {
7339 // The function is on the stack in the unoptimized code during
7340 // evaluation of the arguments.
7341 CHECK_ALIVE(VisitForValue(expr->expression()));
7342 HValue* function = Top();
7343 HValue* context = environment()->LookupContext();
7344 HGlobalObject* global = new(zone()) HGlobalObject(context);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007345 AddInstruction(global);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007346 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007347 PushAndAdd(receiver);
7348 CHECK_ALIVE(VisitExpressions(expr->arguments()));
7349 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007350
7351 if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
7352 if (FLAG_trace_inlining) {
7353 PrintF("Inlining builtin ");
7354 expr->target()->ShortPrint();
7355 PrintF("\n");
7356 }
7357 return;
7358 }
7359
ulan@chromium.org967e2702012-02-28 09:49:15 +00007360 if (TryInlineCall(expr, true)) { // Drop function from environment.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007361 return;
7362 } else {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00007363 call = PreProcessCall(
7364 new(zone()) HInvokeFunction(context,
7365 function,
7366 expr->target(),
7367 argument_count));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007368 Drop(1); // The function.
7369 }
7370
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007371 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00007372 CHECK_ALIVE(VisitForValue(expr->expression()));
7373 HValue* function = Top();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007374 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007375 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007376 AddInstruction(global_object);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007377 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007378 AddInstruction(receiver);
7379 PushAndAdd(new(zone()) HPushArgument(receiver));
7380 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007381
danno@chromium.orgc612e022011-11-10 11:38:15 +00007382 call = new(zone()) HCallFunction(context, function, argument_count);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007383 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007384 }
7385 }
7386
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007387 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007388 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007389}
7390
7391
ulan@chromium.org967e2702012-02-28 09:49:15 +00007392// Checks whether allocation using the given constructor can be inlined.
7393static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
7394 return constructor->has_initial_map() &&
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00007395 constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
7396 constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize;
ulan@chromium.org967e2702012-02-28 09:49:15 +00007397}
7398
7399
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007400void HGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007401 ASSERT(!HasStackOverflow());
7402 ASSERT(current_block() != NULL);
7403 ASSERT(current_block()->HasPredecessor());
ulan@chromium.org967e2702012-02-28 09:49:15 +00007404 expr->RecordTypeFeedback(oracle());
7405 int argument_count = expr->arguments()->length() + 1; // Plus constructor.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007406 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007407
ulan@chromium.org967e2702012-02-28 09:49:15 +00007408 if (FLAG_inline_construct &&
7409 expr->IsMonomorphic() &&
7410 IsAllocationInlineable(expr->target())) {
7411 // The constructor function is on the stack in the unoptimized code
7412 // during evaluation of the arguments.
7413 CHECK_ALIVE(VisitForValue(expr->expression()));
7414 HValue* function = Top();
7415 CHECK_ALIVE(VisitExpressions(expr->arguments()));
7416 Handle<JSFunction> constructor = expr->target();
fschneider@chromium.org35814e52012-03-01 15:43:35 +00007417 HValue* check = AddInstruction(
7418 new(zone()) HCheckFunction(function, constructor));
7419
7420 // Force completion of inobject slack tracking before generating
7421 // allocation code to finalize instance size.
7422 if (constructor->shared()->IsInobjectSlackTrackingInProgress()) {
7423 constructor->shared()->CompleteInobjectSlackTracking();
7424 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00007425
7426 // Replace the constructor function with a newly allocated receiver.
7427 HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
7428 // Index of the receiver from the top of the expression stack.
7429 const int receiver_index = argument_count - 1;
7430 AddInstruction(receiver);
7431 ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
7432 environment()->SetExpressionStackAt(receiver_index, receiver);
7433
7434 if (TryInlineConstruct(expr, receiver)) return;
7435
7436 // TODO(mstarzinger): For now we remove the previous HAllocateObject and
7437 // add HPushArgument for the arguments in case inlining failed. What we
7438 // actually should do is emit HInvokeFunction on the constructor instead
7439 // of using HCallNew as a fallback.
7440 receiver->DeleteAndReplaceWith(NULL);
fschneider@chromium.org35814e52012-03-01 15:43:35 +00007441 check->DeleteAndReplaceWith(NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00007442 environment()->SetExpressionStackAt(receiver_index, function);
7443 HInstruction* call = PreProcessCall(
7444 new(zone()) HCallNew(context, function, argument_count));
7445 call->set_position(expr->position());
7446 return ast_context()->ReturnInstruction(call, expr->id());
7447 } else {
7448 // The constructor function is both an operand to the instruction and an
7449 // argument to the construct call.
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007450 CHECK_ALIVE(VisitArgument(expr->expression()));
7451 HValue* constructor = HPushArgument::cast(Top())->argument();
ulan@chromium.org967e2702012-02-28 09:49:15 +00007452 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
7453 HInstruction* call =
7454 new(zone()) HCallNew(context, constructor, argument_count);
7455 Drop(argument_count);
7456 call->set_position(expr->position());
7457 return ast_context()->ReturnInstruction(call, expr->id());
7458 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007459}
7460
7461
7462// Support for generating inlined runtime functions.
7463
7464// Lookup table for generators for runtime calls that are generated inline.
7465// Elements of the table are member pointers to functions of HGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007466#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007467 &HGraphBuilder::Generate##Name,
7468
7469const HGraphBuilder::InlineFunctionGenerator
7470 HGraphBuilder::kInlineFunctionGenerators[] = {
7471 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
7472 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
7473};
7474#undef INLINE_FUNCTION_GENERATOR_ADDRESS
7475
7476
7477void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007478 ASSERT(!HasStackOverflow());
7479 ASSERT(current_block() != NULL);
7480 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007481 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007482 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007483 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007484
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007485 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007486 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007487 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007488 ASSERT(expr->name()->length() > 0);
7489 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007490 // Call to an inline function.
7491 int lookup_index = static_cast<int>(function->function_id) -
7492 static_cast<int>(Runtime::kFirstInlineFunction);
7493 ASSERT(lookup_index >= 0);
7494 ASSERT(static_cast<size_t>(lookup_index) <
7495 ARRAY_SIZE(kInlineFunctionGenerators));
7496 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
7497
7498 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007499 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007500 } else {
7501 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007502 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007503
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007504 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007505 Handle<String> name = expr->name();
7506 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007507 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007508 new(zone()) HCallRuntime(context, name, function, argument_count);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007509 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007510 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007511 }
7512}
7513
7514
7515void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007516 ASSERT(!HasStackOverflow());
7517 ASSERT(current_block() != NULL);
7518 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007519 switch (expr->op()) {
7520 case Token::DELETE: return VisitDelete(expr);
7521 case Token::VOID: return VisitVoid(expr);
7522 case Token::TYPEOF: return VisitTypeof(expr);
7523 case Token::ADD: return VisitAdd(expr);
7524 case Token::SUB: return VisitSub(expr);
7525 case Token::BIT_NOT: return VisitBitNot(expr);
7526 case Token::NOT: return VisitNot(expr);
7527 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007528 }
7529}
7530
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007531void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
7532 Property* prop = expr->expression()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007533 VariableProxy* proxy = expr->expression()->AsVariableProxy();
7534 if (prop != NULL) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00007535 CHECK_ALIVE(VisitForValue(prop->obj()));
7536 CHECK_ALIVE(VisitForValue(prop->key()));
7537 HValue* key = Pop();
7538 HValue* obj = Pop();
7539 HValue* context = environment()->LookupContext();
7540 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
7541 return ast_context()->ReturnInstruction(instr, expr->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007542 } else if (proxy != NULL) {
7543 Variable* var = proxy->var();
7544 if (var->IsUnallocated()) {
7545 Bailout("delete with global variable");
7546 } else if (var->IsStackAllocated() || var->IsContextSlot()) {
7547 // Result of deleting non-global variables is false. 'this' is not
7548 // really a variable, though we implement it as one. The
7549 // subexpression does not have side effects.
7550 HValue* value = var->is_this()
7551 ? graph()->GetConstantTrue()
7552 : graph()->GetConstantFalse();
7553 return ast_context()->ReturnValue(value);
7554 } else {
7555 Bailout("delete with non-global variable");
7556 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007557 } else {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007558 // Result of deleting non-property, non-variable reference is true.
7559 // Evaluate the subexpression for side effects.
7560 CHECK_ALIVE(VisitForEffect(expr->expression()));
7561 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007562 }
7563}
7564
7565
7566void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
7567 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007568 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007569}
7570
7571
7572void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
7573 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
7574 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007575 HValue* context = environment()->LookupContext();
7576 HInstruction* instr = new(zone()) HTypeof(context, value);
7577 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007578}
7579
7580
7581void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
7582 CHECK_ALIVE(VisitForValue(expr->expression()));
7583 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007584 HValue* context = environment()->LookupContext();
7585 HInstruction* instr =
7586 new(zone()) HMul(context, value, graph_->GetConstant1());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007587 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007588}
7589
7590
7591void HGraphBuilder::VisitSub(UnaryOperation* expr) {
7592 CHECK_ALIVE(VisitForValue(expr->expression()));
7593 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007594 HValue* context = environment()->LookupContext();
7595 HInstruction* instr =
7596 new(zone()) HMul(context, value, graph_->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007597 TypeInfo info = oracle()->UnaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007598 if (info.IsUninitialized()) {
7599 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007600 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007601 info = TypeInfo::Unknown();
7602 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007603 Representation rep = ToRepresentation(info);
7604 TraceRepresentation(expr->op(), info, instr, rep);
7605 instr->AssumeRepresentation(rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007606 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007607}
7608
7609
7610void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
7611 CHECK_ALIVE(VisitForValue(expr->expression()));
7612 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007613 TypeInfo info = oracle()->UnaryType(expr);
7614 if (info.IsUninitialized()) {
7615 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007616 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007617 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007618 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007619 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007620}
7621
7622
7623void HGraphBuilder::VisitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007624 if (ast_context()->IsTest()) {
7625 TestContext* context = TestContext::cast(ast_context());
7626 VisitForControl(expr->expression(),
7627 context->if_false(),
7628 context->if_true());
7629 return;
7630 }
7631
7632 if (ast_context()->IsEffect()) {
7633 VisitForEffect(expr->expression());
7634 return;
7635 }
7636
7637 ASSERT(ast_context()->IsValue());
7638 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
7639 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
7640 CHECK_BAILOUT(VisitForControl(expr->expression(),
7641 materialize_false,
7642 materialize_true));
7643
7644 if (materialize_false->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007645 materialize_false->SetJoinId(expr->MaterializeFalseId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007646 set_current_block(materialize_false);
7647 Push(graph()->GetConstantFalse());
7648 } else {
7649 materialize_false = NULL;
7650 }
7651
7652 if (materialize_true->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007653 materialize_true->SetJoinId(expr->MaterializeTrueId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007654 set_current_block(materialize_true);
7655 Push(graph()->GetConstantTrue());
7656 } else {
7657 materialize_true = NULL;
7658 }
7659
7660 HBasicBlock* join =
7661 CreateJoin(materialize_false, materialize_true, expr->id());
7662 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007663 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007664}
7665
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007666
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007667HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00007668 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007669 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007670 TypeInfo info = oracle()->IncrementType(expr);
7671 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00007672 if (rep.IsTagged()) {
7673 rep = Representation::Integer32();
7674 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007675
7676 if (returns_original_input) {
7677 // We need an explicit HValue representing ToNumber(input). The
7678 // actual HChange instruction we need is (sometimes) added in a later
7679 // phase, so it is not available now to be used as an input to HAdd and
7680 // as the return value.
7681 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
7682 AddInstruction(number_input);
7683 Push(number_input);
7684 }
7685
7686 // The addition has no side effects, so we do not need
7687 // to simulate the expression stack after this instruction.
7688 // Any later failures deopt to the load of the input or earlier.
7689 HConstant* delta = (expr->op() == Token::INC)
7690 ? graph_->GetConstant1()
7691 : graph_->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007692 HValue* context = environment()->LookupContext();
7693 HInstruction* instr = new(zone()) HAdd(context, Top(), delta);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007694 TraceRepresentation(expr->op(), info, instr, rep);
7695 instr->AssumeRepresentation(rep);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007696 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007697 return instr;
7698}
7699
7700
7701void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007702 ASSERT(!HasStackOverflow());
7703 ASSERT(current_block() != NULL);
7704 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007705 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007706 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007707 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007708 if (proxy == NULL && prop == NULL) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007709 return Bailout("invalid lhs in count operation");
7710 }
7711
7712 // Match the full code generator stack by simulating an extra stack
7713 // element for postfix operations in a non-effect context. The return
7714 // value is ToNumber(input).
7715 bool returns_original_input =
7716 expr->is_postfix() && !ast_context()->IsEffect();
7717 HValue* input = NULL; // ToNumber(original_input).
7718 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007719
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007720 if (proxy != NULL) {
7721 Variable* var = proxy->var();
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00007722 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007723 return Bailout("unsupported count operation with const");
7724 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007725 // Argument of the count operation is a variable, not a property.
7726 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007727 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007728
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007729 after = BuildIncrement(returns_original_input, expr);
7730 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007731 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007732
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007733 switch (var->location()) {
7734 case Variable::UNALLOCATED:
7735 HandleGlobalVariableAssignment(var,
7736 after,
7737 expr->position(),
7738 expr->AssignmentId());
7739 break;
7740
7741 case Variable::PARAMETER:
7742 case Variable::LOCAL:
7743 Bind(var, after);
7744 break;
7745
7746 case Variable::CONTEXT: {
7747 // Bail out if we try to mutate a parameter value in a function
7748 // using the arguments object. We do not (yet) correctly handle the
7749 // arguments property of the function.
7750 if (info()->scope()->arguments() != NULL) {
7751 // Parameters will rewrite to context slots. We have no direct
7752 // way to detect that the variable is a parameter so we use a
7753 // linear search of the parameter list.
7754 int count = info()->scope()->num_parameters();
7755 for (int i = 0; i < count; ++i) {
7756 if (var == info()->scope()->parameter(i)) {
7757 return Bailout("assignment to parameter in arguments object");
7758 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007759 }
7760 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007761
7762 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007763 HStoreContextSlot::Mode mode =
7764 (var->mode() == LET || var->mode() == CONST_HARMONY)
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007765 ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007766 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007767 new(zone()) HStoreContextSlot(context, var->index(), mode, after);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007768 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007769 if (instr->HasObservableSideEffects()) {
7770 AddSimulate(expr->AssignmentId());
7771 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007772 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00007773 }
7774
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007775 case Variable::LOOKUP:
7776 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007777 }
7778
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007779 } else {
7780 // Argument of the count operation is a property.
7781 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007782 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007783
7784 if (prop->key()->IsPropertyName()) {
7785 // Named property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007786 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007787
danno@chromium.org160a7b02011-04-18 15:51:38 +00007788 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007789 HValue* obj = Top();
7790
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007791 Handle<Map> map;
7792 HInstruction* load;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007793 if (prop->IsMonomorphic()) {
7794 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007795 map = prop->GetReceiverTypes()->first();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007796 load = BuildLoadNamed(obj, prop, map, name);
7797 } else {
7798 load = BuildLoadNamedGeneric(obj, prop);
7799 }
7800 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007801 if (load->HasObservableSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007802
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007803 after = BuildIncrement(returns_original_input, expr);
7804 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007805
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007806 HInstruction* store;
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007807 CHECK_ALIVE(store = BuildStoreNamed(obj, after, map, prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007808 AddInstruction(store);
7809
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007810 // Overwrite the receiver in the bailout environment with the result
7811 // of the operation, and the placeholder with the original value if
7812 // necessary.
7813 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007814 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007815 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007816
7817 } else {
7818 // Keyed property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007819 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007820
danno@chromium.org160a7b02011-04-18 15:51:38 +00007821 CHECK_ALIVE(VisitForValue(prop->obj()));
7822 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007823 HValue* obj = environment()->ExpressionStackAt(1);
7824 HValue* key = environment()->ExpressionStackAt(0);
7825
whesse@chromium.org7b260152011-06-20 15:33:18 +00007826 bool has_side_effects = false;
7827 HValue* load = HandleKeyedElementAccess(
7828 obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
7829 false, // is_store
7830 &has_side_effects);
7831 Push(load);
7832 if (has_side_effects) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007833
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007834 after = BuildIncrement(returns_original_input, expr);
7835 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007836
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007837 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00007838 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
7839 RelocInfo::kNoPosition,
7840 true, // is_store
7841 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007842
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007843 // Drop the key from the bailout environment. Overwrite the receiver
7844 // with the result of the operation, and the placeholder with the
7845 // original value if necessary.
7846 Drop(1);
7847 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007848 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007849 ASSERT(has_side_effects); // Stores always have side effects.
7850 AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007851 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007852 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007853
7854 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007855 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007856}
7857
7858
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007859HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context,
7860 HValue* string,
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007861 HValue* index) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007862 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007863 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007864 HStringLength* length = new(zone()) HStringLength(string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007865 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007866 HInstruction* checked_index =
7867 AddInstruction(new(zone()) HBoundsCheck(index, length));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007868 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007869}
7870
7871
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007872HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
7873 HValue* left,
7874 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007875 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00007876 TypeInfo info = oracle()->BinaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007877 if (info.IsUninitialized()) {
7878 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007879 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007880 info = TypeInfo::Unknown();
7881 }
7882 HInstruction* instr = NULL;
7883 switch (expr->op()) {
7884 case Token::ADD:
7885 if (info.IsString()) {
7886 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007887 AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007888 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007889 AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007890 instr = new(zone()) HStringAdd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007891 } else {
erikcorry0ad885c2011-11-21 13:51:57 +00007892 instr = HAdd::NewHAdd(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007893 }
7894 break;
7895 case Token::SUB:
erikcorry0ad885c2011-11-21 13:51:57 +00007896 instr = HSub::NewHSub(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007897 break;
7898 case Token::MUL:
erikcorry0ad885c2011-11-21 13:51:57 +00007899 instr = HMul::NewHMul(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007900 break;
7901 case Token::MOD:
erikcorry0ad885c2011-11-21 13:51:57 +00007902 instr = HMod::NewHMod(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007903 break;
7904 case Token::DIV:
erikcorry0ad885c2011-11-21 13:51:57 +00007905 instr = HDiv::NewHDiv(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007906 break;
7907 case Token::BIT_XOR:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007908 case Token::BIT_AND:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007909 case Token::BIT_OR:
erikcorry0ad885c2011-11-21 13:51:57 +00007910 instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007911 break;
7912 case Token::SAR:
erikcorry0ad885c2011-11-21 13:51:57 +00007913 instr = HSar::NewHSar(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007914 break;
7915 case Token::SHR:
erikcorry0ad885c2011-11-21 13:51:57 +00007916 instr = HShr::NewHShr(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007917 break;
7918 case Token::SHL:
erikcorry0ad885c2011-11-21 13:51:57 +00007919 instr = HShl::NewHShl(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007920 break;
7921 default:
7922 UNREACHABLE();
7923 }
7924
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007925 // If we hit an uninitialized binary op stub we will get type info
7926 // for a smi operation. If one of the operands is a constant string
7927 // do not generate code assuming it is a smi operation.
7928 if (info.IsSmi() &&
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007929 ((left->IsConstant() && HConstant::cast(left)->handle()->IsString()) ||
7930 (right->IsConstant() && HConstant::cast(right)->handle()->IsString()))) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007931 return instr;
7932 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007933 Representation rep = ToRepresentation(info);
7934 // We only generate either int32 or generic tagged bitwise operations.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007935 if (instr->IsBitwiseBinaryOperation()) {
7936 HBitwiseBinaryOperation::cast(instr)->
7937 InitializeObservedInputRepresentation(rep);
7938 if (rep.IsDouble()) rep = Representation::Integer32();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007939 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007940 TraceRepresentation(expr->op(), info, instr, rep);
7941 instr->AssumeRepresentation(rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007942 return instr;
7943}
7944
7945
7946// Check for the form (%_ClassOf(foo) === 'BarClass').
7947static bool IsClassOfTest(CompareOperation* expr) {
7948 if (expr->op() != Token::EQ_STRICT) return false;
7949 CallRuntime* call = expr->left()->AsCallRuntime();
7950 if (call == NULL) return false;
7951 Literal* literal = expr->right()->AsLiteral();
7952 if (literal == NULL) return false;
7953 if (!literal->handle()->IsString()) return false;
7954 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
7955 ASSERT(call->arguments()->length() == 1);
7956 return true;
7957}
7958
7959
7960void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007961 ASSERT(!HasStackOverflow());
7962 ASSERT(current_block() != NULL);
7963 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007964 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007965 case Token::COMMA:
7966 return VisitComma(expr);
7967 case Token::OR:
7968 case Token::AND:
7969 return VisitLogicalExpression(expr);
7970 default:
7971 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007972 }
7973}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007974
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007975
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007976void HGraphBuilder::VisitComma(BinaryOperation* expr) {
7977 CHECK_ALIVE(VisitForEffect(expr->left()));
7978 // Visit the right subexpression in the same AST context as the entire
7979 // expression.
7980 Visit(expr->right());
7981}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007982
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007983
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007984void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
7985 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007986 if (ast_context()->IsTest()) {
7987 TestContext* context = TestContext::cast(ast_context());
7988 // Translate left subexpression.
7989 HBasicBlock* eval_right = graph()->CreateBasicBlock();
7990 if (is_logical_and) {
7991 CHECK_BAILOUT(VisitForControl(expr->left(),
7992 eval_right,
7993 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007994 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007995 CHECK_BAILOUT(VisitForControl(expr->left(),
7996 context->if_true(),
7997 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007998 }
7999
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008000 // Translate right subexpression by visiting it in the same AST
8001 // context as the entire expression.
8002 if (eval_right->HasPredecessor()) {
8003 eval_right->SetJoinId(expr->RightId());
8004 set_current_block(eval_right);
8005 Visit(expr->right());
8006 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008007
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008008 } else if (ast_context()->IsValue()) {
8009 CHECK_ALIVE(VisitForValue(expr->left()));
8010 ASSERT(current_block() != NULL);
8011
8012 // We need an extra block to maintain edge-split form.
8013 HBasicBlock* empty_block = graph()->CreateBasicBlock();
8014 HBasicBlock* eval_right = graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008015 unsigned test_id = expr->left()->test_id();
8016 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008017 HBranch* test = is_logical_and
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008018 ? new(zone()) HBranch(Top(), eval_right, empty_block, expected)
8019 : new(zone()) HBranch(Top(), empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008020 current_block()->Finish(test);
8021
8022 set_current_block(eval_right);
8023 Drop(1); // Value of the left subexpression.
8024 CHECK_BAILOUT(VisitForValue(expr->right()));
8025
8026 HBasicBlock* join_block =
8027 CreateJoin(empty_block, current_block(), expr->id());
8028 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008029 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008030
8031 } else {
8032 ASSERT(ast_context()->IsEffect());
8033 // In an effect context, we don't need the value of the left subexpression,
8034 // only its control flow and side effects. We need an extra block to
8035 // maintain edge-split form.
8036 HBasicBlock* empty_block = graph()->CreateBasicBlock();
8037 HBasicBlock* right_block = graph()->CreateBasicBlock();
8038 if (is_logical_and) {
8039 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
8040 } else {
8041 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
8042 }
8043
8044 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
8045 // actually two empty blocks (one here and one inserted by
8046 // TestContext::BuildBranch, and that they both have an HSimulate though the
8047 // second one is not a merge node, and that we really have no good AST ID to
8048 // put on that first HSimulate.
8049
8050 if (empty_block->HasPredecessor()) {
8051 empty_block->SetJoinId(expr->id());
8052 } else {
8053 empty_block = NULL;
8054 }
8055
8056 if (right_block->HasPredecessor()) {
8057 right_block->SetJoinId(expr->RightId());
8058 set_current_block(right_block);
8059 CHECK_BAILOUT(VisitForEffect(expr->right()));
8060 right_block = current_block();
8061 } else {
8062 right_block = NULL;
8063 }
8064
8065 HBasicBlock* join_block =
8066 CreateJoin(empty_block, right_block, expr->id());
8067 set_current_block(join_block);
8068 // We did not materialize any value in the predecessor environments,
8069 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008070 }
8071}
8072
8073
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00008074void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008075 CHECK_ALIVE(VisitForValue(expr->left()));
8076 CHECK_ALIVE(VisitForValue(expr->right()));
8077 HValue* right = Pop();
8078 HValue* left = Pop();
8079 HInstruction* instr = BuildBinaryOperation(expr, left, right);
8080 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008081 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008082}
8083
8084
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008085void HGraphBuilder::TraceRepresentation(Token::Value op,
8086 TypeInfo info,
8087 HValue* value,
8088 Representation rep) {
8089 if (!FLAG_trace_representation) return;
8090 // TODO(svenpanne) Under which circumstances are we actually not flexible?
8091 // At first glance, this looks a bit weird...
8092 bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
8093 PrintF("Operation %s has type info %s, %schange representation assumption "
8094 "for %s (ID %d) from %s to %s\n",
8095 Token::Name(op),
8096 info.ToString(),
8097 flexible ? "" : " DO NOT ",
8098 value->Mnemonic(),
8099 graph_->GetMaximumValueID(),
8100 value->representation().Mnemonic(),
8101 rep.Mnemonic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008102}
8103
8104
8105Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
8106 if (info.IsSmi()) return Representation::Integer32();
8107 if (info.IsInteger32()) return Representation::Integer32();
8108 if (info.IsDouble()) return Representation::Double();
8109 if (info.IsNumber()) return Representation::Double();
8110 return Representation::Tagged();
8111}
8112
8113
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008114void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008115 HTypeof* typeof_expr,
ager@chromium.org04921a82011-06-27 13:21:41 +00008116 Handle<String> check) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008117 // Note: The HTypeof itself is removed during canonicalization, if possible.
8118 HValue* value = typeof_expr->value();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008119 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
8120 instr->set_position(expr->position());
8121 return ast_context()->ReturnControl(instr, expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00008122}
8123
8124
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008125static bool MatchLiteralCompareNil(HValue* left,
8126 Token::Value op,
8127 HValue* right,
8128 Handle<Object> nil,
8129 HValue** expr) {
8130 if (left->IsConstant() &&
8131 HConstant::cast(left)->handle().is_identical_to(nil) &&
8132 Token::IsEqualityOp(op)) {
8133 *expr = right;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008134 return true;
8135 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008136 return false;
ager@chromium.org04921a82011-06-27 13:21:41 +00008137}
8138
8139
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008140static bool MatchLiteralCompareTypeof(HValue* left,
8141 Token::Value op,
8142 HValue* right,
8143 HTypeof** typeof_expr,
8144 Handle<String>* check) {
8145 if (left->IsTypeof() &&
8146 Token::IsEqualityOp(op) &&
8147 right->IsConstant() &&
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00008148 HConstant::cast(right)->handle()->IsString()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008149 *typeof_expr = HTypeof::cast(left);
8150 *check = Handle<String>::cast(HConstant::cast(right)->handle());
8151 return true;
8152 }
8153 return false;
8154}
8155
8156
8157static bool IsLiteralCompareTypeof(HValue* left,
8158 Token::Value op,
8159 HValue* right,
8160 HTypeof** typeof_expr,
8161 Handle<String>* check) {
8162 return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) ||
8163 MatchLiteralCompareTypeof(right, op, left, typeof_expr, check);
8164}
8165
8166
8167static bool IsLiteralCompareNil(HValue* left,
8168 Token::Value op,
8169 HValue* right,
8170 Handle<Object> nil,
8171 HValue** expr) {
8172 return MatchLiteralCompareNil(left, op, right, nil, expr) ||
8173 MatchLiteralCompareNil(right, op, left, nil, expr);
8174}
8175
8176
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00008177static bool IsLiteralCompareBool(HValue* left,
8178 Token::Value op,
8179 HValue* right) {
8180 return op == Token::EQ_STRICT &&
8181 ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) ||
8182 (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean()));
8183}
8184
8185
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008186void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008187 ASSERT(!HasStackOverflow());
8188 ASSERT(current_block() != NULL);
8189 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008190 if (IsClassOfTest(expr)) {
8191 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008192 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008193 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008194 HValue* value = Pop();
8195 Literal* literal = expr->right()->AsLiteral();
8196 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008197 HClassOfTestAndBranch* instr =
8198 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008199 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008200 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008201 }
8202
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008203 TypeInfo type_info = oracle()->CompareType(expr);
8204 // Check if this expression was ever executed according to type feedback.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008205 // Note that for the special typeof/null/undefined cases we get unknown here.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008206 if (type_info.IsUninitialized()) {
8207 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008208 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008209 type_info = TypeInfo::Unknown();
8210 }
8211
danno@chromium.org160a7b02011-04-18 15:51:38 +00008212 CHECK_ALIVE(VisitForValue(expr->left()));
8213 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008214
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008215 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008216 HValue* right = Pop();
8217 HValue* left = Pop();
8218 Token::Value op = expr->op();
8219
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008220 HTypeof* typeof_expr = NULL;
8221 Handle<String> check;
8222 if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) {
8223 return HandleLiteralCompareTypeof(expr, typeof_expr, check);
8224 }
8225 HValue* sub_expr = NULL;
8226 Factory* f = graph()->isolate()->factory();
8227 if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) {
8228 return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
8229 }
8230 if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) {
8231 return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
8232 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00008233 if (IsLiteralCompareBool(left, op, right)) {
8234 HCompareObjectEqAndBranch* result =
8235 new(zone()) HCompareObjectEqAndBranch(left, right);
8236 result->set_position(expr->position());
8237 return ast_context()->ReturnControl(result, expr->id());
8238 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008239
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008240 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008241 // Check to see if the rhs of the instanceof is a global function not
8242 // residing in new space. If it is we assume that the function will stay the
8243 // same.
8244 Handle<JSFunction> target = Handle<JSFunction>::null();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008245 VariableProxy* proxy = expr->right()->AsVariableProxy();
8246 bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008247 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008248 info()->has_global_object() &&
8249 !info()->global_object()->IsAccessCheckNeeded()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008250 Handle<String> name = proxy->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008251 Handle<GlobalObject> global(info()->global_object());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008252 LookupResult lookup(isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008253 global->Lookup(*name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00008254 if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008255 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
8256 // If the function is in new space we assume it's more likely to
8257 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008258 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008259 target = candidate;
8260 }
8261 }
8262 }
8263
8264 // If the target is not null we have found a known global function that is
8265 // assumed to stay the same for this instanceof.
8266 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008267 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
8268 result->set_position(expr->position());
8269 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008270 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008271 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008272 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008273 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008274 result->set_position(expr->position());
8275 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008276 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008277 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008278 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008279 result->set_position(expr->position());
8280 return ast_context()->ReturnInstruction(result, expr->id());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008281 } else if (type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008282 switch (op) {
8283 case Token::EQ:
8284 case Token::EQ_STRICT: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008285 // Can we get away with map check and not instance type check?
8286 Handle<Map> map = oracle()->GetCompareMap(expr);
8287 if (!map.is_null()) {
8288 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008289 AddInstruction(HCheckMaps::NewWithTransitions(left, map, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008290 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008291 AddInstruction(HCheckMaps::NewWithTransitions(right, map, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008292 HCompareObjectEqAndBranch* result =
8293 new(zone()) HCompareObjectEqAndBranch(left, right);
8294 result->set_position(expr->position());
8295 return ast_context()->ReturnControl(result, expr->id());
8296 } else {
8297 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008298 AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008299 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008300 AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008301 HCompareObjectEqAndBranch* result =
8302 new(zone()) HCompareObjectEqAndBranch(left, right);
8303 result->set_position(expr->position());
8304 return ast_context()->ReturnControl(result, expr->id());
8305 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008306 }
8307 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00008308 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008309 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008310 } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
8311 (op == Token::EQ || op == Token::EQ_STRICT)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00008312 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008313 AddInstruction(HCheckInstanceType::NewIsSymbol(left, zone()));
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00008314 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008315 AddInstruction(HCheckInstanceType::NewIsSymbol(right, zone()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008316 HCompareObjectEqAndBranch* result =
8317 new(zone()) HCompareObjectEqAndBranch(left, right);
8318 result->set_position(expr->position());
8319 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008320 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008321 Representation r = ToRepresentation(type_info);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008322 if (r.IsTagged()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008323 HCompareGeneric* result =
8324 new(zone()) HCompareGeneric(context, left, right, op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008325 result->set_position(expr->position());
8326 return ast_context()->ReturnInstruction(result, expr->id());
8327 } else {
8328 HCompareIDAndBranch* result =
8329 new(zone()) HCompareIDAndBranch(left, right, op);
8330 result->set_position(expr->position());
8331 result->SetInputRepresentation(r);
8332 return ast_context()->ReturnControl(result, expr->id());
8333 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008334 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008335}
8336
8337
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008338void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008339 HValue* value,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008340 NilValue nil) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008341 ASSERT(!HasStackOverflow());
8342 ASSERT(current_block() != NULL);
8343 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008344 EqualityKind kind =
8345 expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
8346 HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
8347 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008348 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008349}
8350
8351
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00008352HInstruction* HGraphBuilder::BuildThisFunction() {
8353 // If we share optimized code between different closures, the
8354 // this-function is not a constant, except inside an inlined body.
8355 if (function_state()->outer() != NULL) {
8356 return new(zone()) HConstant(
8357 function_state()->compilation_info()->closure(),
8358 Representation::Tagged());
8359 } else {
8360 return new(zone()) HThisFunction;
8361 }
8362}
8363
8364
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008365void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008366 ASSERT(!HasStackOverflow());
8367 ASSERT(current_block() != NULL);
8368 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00008369 HInstruction* instr = BuildThisFunction();
8370 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008371}
8372
8373
yangguo@chromium.org56454712012-02-16 15:33:53 +00008374void HGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008375 ASSERT(globals_.is_empty());
8376 AstVisitor::VisitDeclarations(declarations);
8377 if (!globals_.is_empty()) {
yangguo@chromium.org56454712012-02-16 15:33:53 +00008378 Handle<FixedArray> array =
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008379 isolate()->factory()->NewFixedArray(globals_.length(), TENURED);
8380 for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i));
yangguo@chromium.org56454712012-02-16 15:33:53 +00008381 int flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
8382 DeclareGlobalsNativeFlag::encode(info()->is_native()) |
8383 DeclareGlobalsLanguageMode::encode(info()->language_mode());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008384 HInstruction* result = new(zone()) HDeclareGlobals(
8385 environment()->LookupContext(), array, flags);
yangguo@chromium.org56454712012-02-16 15:33:53 +00008386 AddInstruction(result);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008387 globals_.Clear();
yangguo@chromium.org56454712012-02-16 15:33:53 +00008388 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008389}
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008390
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008391
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008392void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* declaration) {
8393 VariableProxy* proxy = declaration->proxy();
8394 VariableMode mode = declaration->mode();
8395 Variable* variable = proxy->var();
8396 bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
8397 switch (variable->location()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008398 case Variable::UNALLOCATED:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008399 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008400 globals_.Add(variable->binding_needs_init()
8401 ? isolate()->factory()->the_hole_value()
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008402 : isolate()->factory()->undefined_value(), zone());
yangguo@chromium.org56454712012-02-16 15:33:53 +00008403 return;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008404 case Variable::PARAMETER:
8405 case Variable::LOCAL:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008406 if (hole_init) {
8407 HValue* value = graph()->GetConstantHole();
8408 environment()->Bind(variable, value);
8409 }
8410 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008411 case Variable::CONTEXT:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008412 if (hole_init) {
8413 HValue* value = graph()->GetConstantHole();
8414 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008415 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008416 context, variable->index(), HStoreContextSlot::kNoCheck, value);
8417 AddInstruction(store);
8418 if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008419 }
8420 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008421 case Variable::LOOKUP:
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008422 return Bailout("unsupported lookup slot in declaration");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008423 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008424}
8425
8426
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008427void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) {
8428 VariableProxy* proxy = declaration->proxy();
8429 Variable* variable = proxy->var();
8430 switch (variable->location()) {
8431 case Variable::UNALLOCATED: {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008432 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008433 Handle<SharedFunctionInfo> function =
8434 Compiler::BuildFunctionInfo(declaration->fun(), info()->script());
8435 // Check for stack-overflow exception.
8436 if (function.is_null()) return SetStackOverflow();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008437 globals_.Add(function, zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008438 return;
8439 }
8440 case Variable::PARAMETER:
8441 case Variable::LOCAL: {
8442 CHECK_ALIVE(VisitForValue(declaration->fun()));
8443 HValue* value = Pop();
8444 environment()->Bind(variable, value);
8445 break;
8446 }
8447 case Variable::CONTEXT: {
8448 CHECK_ALIVE(VisitForValue(declaration->fun()));
8449 HValue* value = Pop();
8450 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008451 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008452 context, variable->index(), HStoreContextSlot::kNoCheck, value);
8453 AddInstruction(store);
8454 if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
8455 break;
8456 }
8457 case Variable::LOOKUP:
8458 return Bailout("unsupported lookup slot in declaration");
8459 }
8460}
8461
8462
8463void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008464 UNREACHABLE();
8465}
8466
8467
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008468void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008469 UNREACHABLE();
8470}
8471
8472
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008473void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008474 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008475}
8476
8477
8478void HGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008479 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008480}
8481
8482
8483void HGraphBuilder::VisitModuleVariable(ModuleVariable* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008484 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008485}
8486
8487
8488void HGraphBuilder::VisitModulePath(ModulePath* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008489 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008490}
8491
8492
8493void HGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008494 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008495}
8496
8497
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008498// Generators for inline runtime functions.
8499// Support for types.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008500void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
8501 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008502 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008503 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008504 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
8505 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008506}
8507
8508
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008509void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
8510 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008511 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008512 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008513 HHasInstanceTypeAndBranch* result =
8514 new(zone()) HHasInstanceTypeAndBranch(value,
8515 FIRST_SPEC_OBJECT_TYPE,
8516 LAST_SPEC_OBJECT_TYPE);
8517 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008518}
8519
8520
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008521void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
8522 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008523 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008524 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008525 HHasInstanceTypeAndBranch* result =
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008526 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008527 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008528}
8529
8530
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008531void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
8532 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008533 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008534 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008535 HHasCachedArrayIndexAndBranch* result =
8536 new(zone()) HHasCachedArrayIndexAndBranch(value);
8537 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008538}
8539
8540
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008541void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
8542 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008543 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008544 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008545 HHasInstanceTypeAndBranch* result =
8546 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
8547 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008548}
8549
8550
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008551void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
8552 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008553 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008554 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008555 HHasInstanceTypeAndBranch* result =
8556 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
8557 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008558}
8559
8560
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008561void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
8562 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008563 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008564 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008565 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
8566 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008567}
8568
8569
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008570void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008571 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008572}
8573
8574
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008575void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008576 ASSERT(call->arguments()->length() == 1);
8577 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8578 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008579 HIsUndetectableAndBranch* result =
8580 new(zone()) HIsUndetectableAndBranch(value);
8581 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008582}
8583
8584
8585void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008586 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008587 return Bailout(
8588 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008589}
8590
8591
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008592// Support for construct call checks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008593void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
8594 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008595 if (function_state()->outer() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00008596 // We are generating graph for inlined function.
8597 HValue* value = function_state()->is_construct()
8598 ? graph()->GetConstantTrue()
8599 : graph()->GetConstantFalse();
8600 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008601 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008602 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
8603 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008604 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008605}
8606
8607
8608// Support for arguments.length and arguments[?].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008609void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008610 // Our implementation of arguments (based on this stack frame or an
8611 // adapter below it) does not work for inlined functions. This runtime
8612 // function is blacklisted by AstNode::IsInlineable.
8613 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008614 ASSERT(call->arguments()->length() == 0);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008615 HInstruction* elements = AddInstruction(
8616 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008617 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008618 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008619}
8620
8621
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008622void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008623 // Our implementation of arguments (based on this stack frame or an
8624 // adapter below it) does not work for inlined functions. This runtime
8625 // function is blacklisted by AstNode::IsInlineable.
8626 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008627 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008628 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008629 HValue* index = Pop();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008630 HInstruction* elements = AddInstruction(
8631 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008632 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
8633 HAccessArgumentsAt* result =
8634 new(zone()) HAccessArgumentsAt(elements, length, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008635 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008636}
8637
8638
8639// Support for accessing the class and value fields of an object.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008640void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008641 // The special form detected by IsClassOfTest is detected before we get here
8642 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00008643 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008644}
8645
8646
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008647void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
8648 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008649 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008650 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008651 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008652 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008653}
8654
8655
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00008656void HGraphBuilder::GenerateDateField(CallRuntime* call) {
8657 ASSERT(call->arguments()->length() == 2);
8658 ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
8659 Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
8660 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8661 HValue* date = Pop();
8662 HDateField* result = new(zone()) HDateField(date, index);
8663 return ast_context()->ReturnInstruction(result, call->id());
8664}
8665
8666
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008667void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org2c456792011-11-11 12:00:53 +00008668 ASSERT(call->arguments()->length() == 2);
8669 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8670 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
8671 HValue* value = Pop();
8672 HValue* object = Pop();
8673 // Check if object is a not a smi.
8674 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object);
8675 HBasicBlock* if_smi = graph()->CreateBasicBlock();
8676 HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
8677 HBasicBlock* join = graph()->CreateBasicBlock();
8678 smicheck->SetSuccessorAt(0, if_smi);
8679 smicheck->SetSuccessorAt(1, if_heap_object);
8680 current_block()->Finish(smicheck);
8681 if_smi->Goto(join);
8682
8683 // Check if object is a JSValue.
8684 set_current_block(if_heap_object);
8685 HHasInstanceTypeAndBranch* typecheck =
8686 new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE);
8687 HBasicBlock* if_js_value = graph()->CreateBasicBlock();
8688 HBasicBlock* not_js_value = graph()->CreateBasicBlock();
8689 typecheck->SetSuccessorAt(0, if_js_value);
8690 typecheck->SetSuccessorAt(1, not_js_value);
8691 current_block()->Finish(typecheck);
8692 not_js_value->Goto(join);
8693
8694 // Create in-object property store to kValueOffset.
8695 set_current_block(if_js_value);
8696 Handle<String> name = isolate()->factory()->undefined_symbol();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008697 AddInstruction(new(zone()) HStoreNamedField(object,
8698 name,
8699 value,
8700 true, // in-object store.
8701 JSValue::kValueOffset));
danno@chromium.org2c456792011-11-11 12:00:53 +00008702 if_js_value->Goto(join);
8703 join->SetJoinId(call->id());
8704 set_current_block(join);
8705 return ast_context()->ReturnValue(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008706}
8707
8708
8709// Fast support for charCodeAt(n).
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008710void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
8711 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008712 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8713 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008714 HValue* index = Pop();
8715 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008716 HValue* context = environment()->LookupContext();
8717 HStringCharCodeAt* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008718 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008719}
8720
8721
8722// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008723void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008724 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008725 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008726 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008727 HValue* context = environment()->LookupContext();
8728 HStringCharFromCode* result =
8729 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008730 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008731}
8732
8733
8734// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008735void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008736 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008737 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8738 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008739 HValue* index = Pop();
8740 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008741 HValue* context = environment()->LookupContext();
8742 HStringCharCodeAt* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008743 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008744 HStringCharFromCode* result =
8745 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008746 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008747}
8748
8749
8750// Fast support for object equality testing.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008751void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
8752 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008753 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8754 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008755 HValue* right = Pop();
8756 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008757 HCompareObjectEqAndBranch* result =
8758 new(zone()) HCompareObjectEqAndBranch(left, right);
8759 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008760}
8761
8762
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008763void HGraphBuilder::GenerateLog(CallRuntime* call) {
8764 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008765 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008766}
8767
8768
8769// Fast support for Math.random().
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008770void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008771 HValue* context = environment()->LookupContext();
8772 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
8773 AddInstruction(global_object);
8774 HRandom* result = new(zone()) HRandom(global_object);
8775 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008776}
8777
8778
8779// Fast support for StringAdd.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008780void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
8781 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008782 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008783 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008784 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008785 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008786 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008787}
8788
8789
8790// Fast support for SubString.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008791void HGraphBuilder::GenerateSubString(CallRuntime* call) {
8792 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008793 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008794 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008795 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008796 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008797 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008798}
8799
8800
8801// Fast support for StringCompare.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008802void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
8803 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008804 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008805 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008806 HCallStub* result =
8807 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008808 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008809 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008810}
8811
8812
8813// Support for direct calls from JavaScript to native RegExp code.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008814void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
8815 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008816 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008817 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008818 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008819 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008820 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008821}
8822
8823
8824// Construct a RegExp exec result with two in-object properties.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008825void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
8826 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008827 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008828 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008829 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008830 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008831 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008832 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008833}
8834
8835
8836// Support for fast native caches.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008837void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008838 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008839}
8840
8841
8842// Fast support for number to string.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008843void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
8844 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008845 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008846 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008847 HCallStub* result =
8848 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008849 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008850 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008851}
8852
8853
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008854// Fast call for custom callbacks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008855void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008856 // 1 ~ The function to call is not itself an argument to the call.
8857 int arg_count = call->arguments()->length() - 1;
8858 ASSERT(arg_count >= 1); // There's always at least a receiver.
8859
8860 for (int i = 0; i < arg_count; ++i) {
8861 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
8862 }
8863 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
danno@chromium.orgc612e022011-11-10 11:38:15 +00008864
danno@chromium.org160a7b02011-04-18 15:51:38 +00008865 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008866 HValue* context = environment()->LookupContext();
danno@chromium.orgc612e022011-11-10 11:38:15 +00008867
8868 // Branch for function proxies, or other non-functions.
8869 HHasInstanceTypeAndBranch* typecheck =
8870 new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
8871 HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
8872 HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
8873 HBasicBlock* join = graph()->CreateBasicBlock();
8874 typecheck->SetSuccessorAt(0, if_jsfunction);
8875 typecheck->SetSuccessorAt(1, if_nonfunction);
8876 current_block()->Finish(typecheck);
8877
8878 set_current_block(if_jsfunction);
8879 HInstruction* invoke_result = AddInstruction(
8880 new(zone()) HInvokeFunction(context, function, arg_count));
danno@chromium.org160a7b02011-04-18 15:51:38 +00008881 Drop(arg_count);
danno@chromium.orgc612e022011-11-10 11:38:15 +00008882 Push(invoke_result);
8883 if_jsfunction->Goto(join);
8884
8885 set_current_block(if_nonfunction);
8886 HInstruction* call_result = AddInstruction(
8887 new(zone()) HCallFunction(context, function, arg_count));
8888 Drop(arg_count);
8889 Push(call_result);
8890 if_nonfunction->Goto(join);
8891
8892 set_current_block(join);
8893 join->SetJoinId(call->id());
8894 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008895}
8896
8897
8898// Fast call to math functions.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008899void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
8900 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008901 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8902 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008903 HValue* right = Pop();
8904 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008905 HPower* result = new(zone()) HPower(left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008906 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008907}
8908
8909
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008910void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
8911 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008912 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008913 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008914 HCallStub* result =
8915 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008916 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008917 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008918 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008919}
8920
8921
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008922void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
8923 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008924 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008925 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008926 HCallStub* result =
8927 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008928 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008929 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008930 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008931}
8932
8933
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00008934void HGraphBuilder::GenerateMathTan(CallRuntime* call) {
8935 ASSERT_EQ(1, call->arguments()->length());
8936 CHECK_ALIVE(VisitArgumentList(call->arguments()));
8937 HValue* context = environment()->LookupContext();
8938 HCallStub* result =
8939 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
8940 result->set_transcendental_type(TranscendentalCache::TAN);
8941 Drop(1);
8942 return ast_context()->ReturnInstruction(result, call->id());
8943}
8944
8945
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008946void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
8947 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008948 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008949 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008950 HCallStub* result =
8951 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008952 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008953 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008954 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008955}
8956
8957
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008958void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008959 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008960}
8961
8962
8963// Check whether two RegExps are equivalent
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008964void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008965 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008966}
8967
8968
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008969void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
8970 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008971 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00008972 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008973 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008974 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008975}
8976
8977
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008978void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008979 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008980}
8981
8982
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008983#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +00008984#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008985
8986
8987HEnvironment::HEnvironment(HEnvironment* outer,
8988 Scope* scope,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008989 Handle<JSFunction> closure,
8990 Zone* zone)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008991 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008992 values_(0, zone),
8993 assigned_variables_(4, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00008994 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008995 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008996 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008997 local_count_(0),
8998 outer_(outer),
8999 pop_count_(0),
9000 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009001 ast_id_(AstNode::kNoNumber),
9002 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009003 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
9004}
9005
9006
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009007HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone)
9008 : values_(0, zone),
9009 assigned_variables_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00009010 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009011 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009012 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009013 local_count_(0),
9014 outer_(NULL),
9015 pop_count_(0),
9016 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009017 ast_id_(other->ast_id()),
9018 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009019 Initialize(other);
9020}
9021
9022
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009023HEnvironment::HEnvironment(HEnvironment* outer,
9024 Handle<JSFunction> closure,
ulan@chromium.org967e2702012-02-28 09:49:15 +00009025 FrameType frame_type,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009026 int arguments,
9027 Zone* zone)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009028 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009029 values_(arguments, zone),
9030 assigned_variables_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00009031 frame_type_(frame_type),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009032 parameter_count_(arguments),
9033 local_count_(0),
9034 outer_(outer),
9035 pop_count_(0),
9036 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009037 ast_id_(AstNode::kNoNumber),
9038 zone_(zone) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009039}
9040
9041
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009042void HEnvironment::Initialize(int parameter_count,
9043 int local_count,
9044 int stack_height) {
9045 parameter_count_ = parameter_count;
9046 local_count_ = local_count;
9047
9048 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009049 int total = parameter_count + specials_count_ + local_count + stack_height;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009050 values_.Initialize(total + 4, zone());
9051 for (int i = 0; i < total; ++i) values_.Add(NULL, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009052}
9053
9054
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009055void HEnvironment::Initialize(const HEnvironment* other) {
9056 closure_ = other->closure();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009057 values_.AddAll(other->values_, zone());
9058 assigned_variables_.AddAll(other->assigned_variables_, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +00009059 frame_type_ = other->frame_type_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009060 parameter_count_ = other->parameter_count_;
9061 local_count_ = other->local_count_;
9062 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
9063 pop_count_ = other->pop_count_;
9064 push_count_ = other->push_count_;
9065 ast_id_ = other->ast_id_;
9066}
9067
9068
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009069void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
9070 ASSERT(!block->IsLoopHeader());
9071 ASSERT(values_.length() == other->values_.length());
9072
9073 int length = values_.length();
9074 for (int i = 0; i < length; ++i) {
9075 HValue* value = values_[i];
9076 if (value != NULL && value->IsPhi() && value->block() == block) {
9077 // There is already a phi for the i'th value.
9078 HPhi* phi = HPhi::cast(value);
9079 // Assert index is correct and that we haven't missed an incoming edge.
9080 ASSERT(phi->merged_index() == i);
9081 ASSERT(phi->OperandCount() == block->predecessors()->length());
9082 phi->AddInput(other->values_[i]);
9083 } else if (values_[i] != other->values_[i]) {
9084 // There is a fresh value on the incoming edge, a phi is needed.
9085 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009086 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009087 HValue* old_value = values_[i];
9088 for (int j = 0; j < block->predecessors()->length(); j++) {
9089 phi->AddInput(old_value);
9090 }
9091 phi->AddInput(other->values_[i]);
9092 this->values_[i] = phi;
9093 block->AddPhi(phi);
9094 }
9095 }
9096}
9097
9098
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009099void HEnvironment::Bind(int index, HValue* value) {
9100 ASSERT(value != NULL);
9101 if (!assigned_variables_.Contains(index)) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009102 assigned_variables_.Add(index, zone());
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009103 }
9104 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009105}
9106
9107
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009108bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009109 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009110}
9111
9112
9113bool HEnvironment::ExpressionStackIsEmpty() const {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00009114 ASSERT(length() >= first_expression_index());
9115 return length() == first_expression_index();
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009116}
9117
9118
9119void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
9120 int count = index_from_top + 1;
9121 int index = values_.length() - count;
9122 ASSERT(HasExpressionAt(index));
9123 // The push count must include at least the element in question or else
9124 // the new value will not be included in this environment's history.
9125 if (push_count_ < count) {
9126 // This is the same effect as popping then re-pushing 'count' elements.
9127 pop_count_ += (count - push_count_);
9128 push_count_ = count;
9129 }
9130 values_[index] = value;
9131}
9132
9133
9134void HEnvironment::Drop(int count) {
9135 for (int i = 0; i < count; ++i) {
9136 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009137 }
9138}
9139
9140
9141HEnvironment* HEnvironment::Copy() const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009142 return new(zone()) HEnvironment(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009143}
9144
9145
9146HEnvironment* HEnvironment::CopyWithoutHistory() const {
9147 HEnvironment* result = Copy();
9148 result->ClearHistory();
9149 return result;
9150}
9151
9152
9153HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
9154 HEnvironment* new_env = Copy();
9155 for (int i = 0; i < values_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009156 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009157 phi->AddInput(values_[i]);
9158 new_env->values_[i] = phi;
9159 loop_header->AddPhi(phi);
9160 }
9161 new_env->ClearHistory();
9162 return new_env;
9163}
9164
9165
ulan@chromium.org967e2702012-02-28 09:49:15 +00009166HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
9167 Handle<JSFunction> target,
9168 FrameType frame_type,
9169 int arguments) const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009170 HEnvironment* new_env =
9171 new(zone()) HEnvironment(outer, target, frame_type,
9172 arguments + 1, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +00009173 for (int i = 0; i <= arguments; ++i) { // Include receiver.
9174 new_env->Push(ExpressionStackAt(arguments - i));
9175 }
9176 new_env->ClearHistory();
9177 return new_env;
9178}
9179
9180
danno@chromium.org40cb8782011-05-25 07:58:50 +00009181HEnvironment* HEnvironment::CopyForInlining(
9182 Handle<JSFunction> target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009183 int arguments,
danno@chromium.org40cb8782011-05-25 07:58:50 +00009184 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00009185 HConstant* undefined,
ulan@chromium.org967e2702012-02-28 09:49:15 +00009186 CallKind call_kind,
9187 bool is_construct) const {
9188 ASSERT(frame_type() == JS_FUNCTION);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009189
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009190 // Outer environment is a copy of this one without the arguments.
9191 int arity = function->scope()->num_parameters();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009192
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009193 HEnvironment* outer = Copy();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009194 outer->Drop(arguments + 1); // Including receiver.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009195 outer->ClearHistory();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009196
ulan@chromium.org967e2702012-02-28 09:49:15 +00009197 if (is_construct) {
9198 // Create artificial constructor stub environment. The receiver should
9199 // actually be the constructor function, but we pass the newly allocated
9200 // object instead, DoComputeConstructStubFrame() relies on that.
9201 outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
9202 }
9203
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009204 if (arity != arguments) {
9205 // Create artificial arguments adaptation environment.
ulan@chromium.org967e2702012-02-28 09:49:15 +00009206 outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009207 }
9208
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009209 HEnvironment* inner =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00009210 new(zone()) HEnvironment(outer, function->scope(), target, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009211 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009212 for (int i = 0; i <= arity; ++i) { // Include receiver.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009213 HValue* push = (i <= arguments) ?
9214 ExpressionStackAt(arguments - i) : undefined;
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009215 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009216 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00009217 // If the function we are inlining is a strict mode function or a
9218 // builtin function, pass undefined as the receiver for function
9219 // calls (instead of the global receiver).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00009220 if ((target->shared()->native() || !function->is_classic_mode()) &&
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00009221 call_kind == CALL_AS_FUNCTION && !is_construct) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00009222 inner->SetValueAt(0, undefined);
9223 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009224 inner->SetValueAt(arity + 1, LookupContext());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009225 for (int i = arity + 2; i < inner->length(); ++i) {
9226 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009227 }
9228
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00009229 inner->set_ast_id(AstNode::kFunctionEntryId);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009230 return inner;
9231}
9232
9233
9234void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009235 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009236 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009237 if (i == parameter_count()) stream->Add("specials\n");
9238 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
9239 if (i == parameter_count() + specials_count() + local_count()) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009240 stream->Add("expressions\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009241 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009242 HValue* val = values_.at(i);
9243 stream->Add("%d: ", i);
9244 if (val != NULL) {
9245 val->PrintNameTo(stream);
9246 } else {
9247 stream->Add("NULL");
9248 }
9249 stream->Add("\n");
9250 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009251 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009252}
9253
9254
9255void HEnvironment::PrintToStd() {
9256 HeapStringAllocator string_allocator;
9257 StringStream trace(&string_allocator);
9258 PrintTo(&trace);
9259 PrintF("%s", *trace.ToCString());
9260}
9261
9262
9263void HTracer::TraceCompilation(FunctionLiteral* function) {
9264 Tag tag(this, "compilation");
9265 Handle<String> name = function->debug_name();
9266 PrintStringProperty("name", *name->ToCString());
9267 PrintStringProperty("method", *name->ToCString());
9268 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
9269}
9270
9271
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00009272void HTracer::TraceLithium(const char* name, LChunk* chunk) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009273 Trace(name, chunk->graph(), chunk);
9274}
9275
9276
9277void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
9278 Trace(name, graph, NULL);
9279}
9280
9281
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00009282void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009283 Tag tag(this, "cfg");
9284 PrintStringProperty("name", name);
9285 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
9286 for (int i = 0; i < blocks->length(); i++) {
9287 HBasicBlock* current = blocks->at(i);
9288 Tag block_tag(this, "block");
9289 PrintBlockProperty("name", current->block_id());
9290 PrintIntProperty("from_bci", -1);
9291 PrintIntProperty("to_bci", -1);
9292
9293 if (!current->predecessors()->is_empty()) {
9294 PrintIndent();
9295 trace_.Add("predecessors");
9296 for (int j = 0; j < current->predecessors()->length(); ++j) {
9297 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
9298 }
9299 trace_.Add("\n");
9300 } else {
9301 PrintEmptyProperty("predecessors");
9302 }
9303
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00009304 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009305 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00009306 } else {
9307 PrintIndent();
9308 trace_.Add("successors");
9309 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
9310 trace_.Add(" \"B%d\"", it.Current()->block_id());
9311 }
9312 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009313 }
9314
9315 PrintEmptyProperty("xhandlers");
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009316 const char* flags = current->IsLoopSuccessorDominator()
9317 ? "dom-loop-succ"
9318 : "";
9319 PrintStringProperty("flags", flags);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009320
9321 if (current->dominator() != NULL) {
9322 PrintBlockProperty("dominator", current->dominator()->block_id());
9323 }
9324
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00009325 PrintIntProperty("loop_depth", current->LoopNestingDepth());
9326
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009327 if (chunk != NULL) {
9328 int first_index = current->first_instruction_index();
9329 int last_index = current->last_instruction_index();
9330 PrintIntProperty(
9331 "first_lir_id",
9332 LifetimePosition::FromInstructionIndex(first_index).Value());
9333 PrintIntProperty(
9334 "last_lir_id",
9335 LifetimePosition::FromInstructionIndex(last_index).Value());
9336 }
9337
9338 {
9339 Tag states_tag(this, "states");
9340 Tag locals_tag(this, "locals");
9341 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009342 PrintIntProperty("size", current->phis()->length());
9343 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009344 for (int j = 0; j < total; ++j) {
9345 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009346 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009347 trace_.Add("%d ", phi->merged_index());
9348 phi->PrintNameTo(&trace_);
9349 trace_.Add(" ");
9350 phi->PrintTo(&trace_);
9351 trace_.Add("\n");
9352 }
9353 }
9354
9355 {
9356 Tag HIR_tag(this, "HIR");
9357 HInstruction* instruction = current->first();
9358 while (instruction != NULL) {
9359 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009360 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009361 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009362 trace_.Add("%d %d ", bci, uses);
9363 instruction->PrintNameTo(&trace_);
9364 trace_.Add(" ");
9365 instruction->PrintTo(&trace_);
9366 trace_.Add(" <|@\n");
9367 instruction = instruction->next();
9368 }
9369 }
9370
9371
9372 if (chunk != NULL) {
9373 Tag LIR_tag(this, "LIR");
9374 int first_index = current->first_instruction_index();
9375 int last_index = current->last_instruction_index();
9376 if (first_index != -1 && last_index != -1) {
9377 const ZoneList<LInstruction*>* instructions = chunk->instructions();
9378 for (int i = first_index; i <= last_index; ++i) {
9379 LInstruction* linstr = instructions->at(i);
9380 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009381 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009382 trace_.Add("%d ",
9383 LifetimePosition::FromInstructionIndex(i).Value());
9384 linstr->PrintTo(&trace_);
9385 trace_.Add(" <|@\n");
9386 }
9387 }
9388 }
9389 }
9390 }
9391}
9392
9393
9394void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
9395 Tag tag(this, "intervals");
9396 PrintStringProperty("name", name);
9397
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009398 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009399 for (int i = 0; i < fixed_d->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009400 TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009401 }
9402
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009403 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009404 for (int i = 0; i < fixed->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009405 TraceLiveRange(fixed->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009406 }
9407
9408 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
9409 for (int i = 0; i < live_ranges->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009410 TraceLiveRange(live_ranges->at(i), "object", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009411 }
9412}
9413
9414
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009415void HTracer::TraceLiveRange(LiveRange* range, const char* type,
9416 Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009417 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009418 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009419 trace_.Add("%d %s", range->id(), type);
9420 if (range->HasRegisterAssigned()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009421 LOperand* op = range->CreateAssignedOperand(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009422 int assigned_reg = op->index();
9423 if (op->IsDoubleRegister()) {
9424 trace_.Add(" \"%s\"",
9425 DoubleRegister::AllocationIndexToString(assigned_reg));
9426 } else {
9427 ASSERT(op->IsRegister());
9428 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
9429 }
9430 } else if (range->IsSpilled()) {
9431 LOperand* op = range->TopLevel()->GetSpillOperand();
9432 if (op->IsDoubleStackSlot()) {
9433 trace_.Add(" \"double_stack:%d\"", op->index());
9434 } else {
9435 ASSERT(op->IsStackSlot());
9436 trace_.Add(" \"stack:%d\"", op->index());
9437 }
9438 }
9439 int parent_index = -1;
9440 if (range->IsChild()) {
9441 parent_index = range->parent()->id();
9442 } else {
9443 parent_index = range->id();
9444 }
9445 LOperand* op = range->FirstHint();
9446 int hint_index = -1;
danno@chromium.orgfa458e42012-02-01 10:48:36 +00009447 if (op != NULL && op->IsUnallocated()) {
9448 hint_index = LUnallocated::cast(op)->virtual_register();
9449 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009450 trace_.Add(" %d %d", parent_index, hint_index);
9451 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009452 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009453 trace_.Add(" [%d, %d[",
9454 cur_interval->start().Value(),
9455 cur_interval->end().Value());
9456 cur_interval = cur_interval->next();
9457 }
9458
9459 UsePosition* current_pos = range->first_pos();
9460 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009461 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009462 trace_.Add(" %d M", current_pos->pos().Value());
9463 }
9464 current_pos = current_pos->next();
9465 }
9466
9467 trace_.Add(" \"\"\n");
9468 }
9469}
9470
9471
9472void HTracer::FlushToFile() {
9473 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
9474 trace_.Reset();
9475}
9476
9477
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009478void HStatistics::Initialize(CompilationInfo* info) {
9479 source_size_ += info->shared_info()->SourceSize();
9480}
9481
9482
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009483void HStatistics::Print() {
9484 PrintF("Timing results:\n");
9485 int64_t sum = 0;
9486 for (int i = 0; i < timing_.length(); ++i) {
9487 sum += timing_[i];
9488 }
9489
9490 for (int i = 0; i < names_.length(); ++i) {
9491 PrintF("%30s", names_[i]);
9492 double ms = static_cast<double>(timing_[i]) / 1000;
9493 double percent = static_cast<double>(timing_[i]) * 100 / sum;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009494 PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
9495
9496 unsigned size = sizes_[i];
9497 double size_percent = static_cast<double>(size) * 100 / total_size_;
9498 PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009499 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009500 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00009501 double normalized_time = source_size_in_kb > 0
9502 ? (static_cast<double>(sum) / 1000) / source_size_in_kb
9503 : 0;
9504 double normalized_bytes = source_size_in_kb > 0
9505 ? total_size_ / source_size_in_kb
9506 : 0;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009507 PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00009508 normalized_time, normalized_bytes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009509 PrintF("---------------------------------------------------------------\n");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009510 PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009511 "Total",
9512 static_cast<double>(total_) / 1000,
9513 static_cast<double>(total_) / full_code_gen_);
9514}
9515
9516
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009517void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009518 if (name == HPhase::kFullCodeGen) {
9519 full_code_gen_ += ticks;
9520 } else if (name == HPhase::kTotal) {
9521 total_ += ticks;
9522 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009523 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009524 for (int i = 0; i < names_.length(); ++i) {
9525 if (names_[i] == name) {
9526 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009527 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009528 return;
9529 }
9530 }
9531 names_.Add(name);
9532 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009533 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009534 }
9535}
9536
9537
9538const char* const HPhase::kFullCodeGen = "Full code generator";
9539const char* const HPhase::kTotal = "Total";
9540
9541
9542void HPhase::Begin(const char* name,
9543 HGraph* graph,
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00009544 LChunk* chunk,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009545 LAllocator* allocator) {
9546 name_ = name;
9547 graph_ = graph;
9548 chunk_ = chunk;
9549 allocator_ = allocator;
9550 if (allocator != NULL && chunk_ == NULL) {
9551 chunk_ = allocator->chunk();
9552 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009553 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009554 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009555}
9556
9557
9558void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009559 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009560 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009561 unsigned size = Zone::allocation_size_ - start_allocation_size_;
9562 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009563 }
9564
fschneider@chromium.org35814e52012-03-01 15:43:35 +00009565 // Produce trace output if flag is set so that the first letter of the
9566 // phase name matches the command line parameter FLAG_trace_phase.
9567 if (FLAG_trace_hydrogen &&
9568 OS::StrChr(const_cast<char*>(FLAG_trace_phase), name_[0]) != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009569 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
9570 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
9571 if (allocator_ != NULL) {
9572 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
9573 }
9574 }
9575
9576#ifdef DEBUG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009577 if (graph_ != NULL) graph_->Verify(false); // No full verify.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009578 if (allocator_ != NULL) allocator_->Verify();
9579#endif
9580}
9581
9582} } // namespace v8::internal