blob: 2d671839e1d5951a0e2b0d13ed54bcfd819c3782 [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
582HConstant* HGraph::GetConstant1() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000583 return GetConstant(&constant_1_, Handle<Smi>(Smi::FromInt(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000584}
585
586
587HConstant* HGraph::GetConstantMinus1() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000588 return GetConstant(&constant_minus1_, Handle<Smi>(Smi::FromInt(-1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000589}
590
591
592HConstant* HGraph::GetConstantTrue() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000593 return GetConstant(&constant_true_, isolate()->factory()->true_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000594}
595
596
597HConstant* HGraph::GetConstantFalse() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000598 return GetConstant(&constant_false_, isolate()->factory()->false_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000599}
600
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000601
602HConstant* HGraph::GetConstantHole() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000603 return GetConstant(&constant_hole_, isolate()->factory()->the_hole_value());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000604}
605
606
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000607HGraphBuilder::HGraphBuilder(CompilationInfo* info,
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000608 TypeFeedbackOracle* oracle)
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000609 : function_state_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +0000610 initial_function_state_(this, info, oracle, NORMAL_RETURN),
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000611 ast_context_(NULL),
612 break_scope_(NULL),
613 graph_(NULL),
614 current_block_(NULL),
615 inlined_count_(0),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000616 globals_(10, info->zone()),
617 zone_(info->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000618 inline_bailout_(false) {
619 // This is not initialized in the initializer list because the
620 // constructor for the initial state relies on function_state_ == NULL
621 // to know it's the initial state.
622 function_state_= &initial_function_state_;
623}
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000624
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000625HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
626 HBasicBlock* second,
627 int join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000628 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000629 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000630 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000631 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000632 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000633 HBasicBlock* join_block = graph_->CreateBasicBlock();
634 first->Goto(join_block);
635 second->Goto(join_block);
636 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000637 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000638 }
639}
640
641
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000642HBasicBlock* HGraphBuilder::JoinContinue(IterationStatement* statement,
643 HBasicBlock* exit_block,
644 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000645 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000646 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000647 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000648 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000649 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000650 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000651}
652
653
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000654HBasicBlock* HGraphBuilder::CreateLoop(IterationStatement* statement,
655 HBasicBlock* loop_entry,
656 HBasicBlock* body_exit,
657 HBasicBlock* loop_successor,
658 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +0000659 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000660 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000661 if (break_block != NULL) {
662 if (loop_successor != NULL) loop_successor->Goto(break_block);
663 break_block->SetJoinId(statement->ExitId());
664 return break_block;
665 }
666 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000667}
668
669
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000670void HBasicBlock::FinishExit(HControlInstruction* instruction) {
671 Finish(instruction);
672 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000673}
674
675
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000676HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000677 : isolate_(info->isolate()),
678 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000679 entry_block_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000680 blocks_(8, info->zone()),
681 values_(16, info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000682 phi_list_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000683 info_(info),
684 zone_(info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000685 is_recursive_(false) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000686 start_environment_ =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000687 new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000688 start_environment_->set_ast_id(AstNode::kFunctionEntryId);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000689 entry_block_ = CreateBasicBlock();
690 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000691}
692
693
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000694Handle<Code> HGraph::Compile() {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000695 int values = GetMaximumValueID();
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000696 if (values > LUnallocated::kMaxVirtualRegisters) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000697 if (FLAG_trace_bailout) {
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000698 PrintF("Not enough virtual registers for (values).\n");
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000699 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000700 return Handle<Code>::null();
701 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000702 LAllocator allocator(values, this);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000703 LChunkBuilder builder(info(), this, &allocator);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000704 LChunk* chunk = builder.Build();
705 if (chunk == NULL) return Handle<Code>::null();
706
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000707 if (!allocator.Allocate(chunk)) {
708 if (FLAG_trace_bailout) {
709 PrintF("Not enough virtual registers (regalloc).\n");
710 }
711 return Handle<Code>::null();
712 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000713
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000714 MacroAssembler assembler(isolate(), NULL, 0);
715 LCodeGen generator(chunk, &assembler, info());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000716
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000717 chunk->MarkEmptyBlocks();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000718
719 if (generator.GenerateCode()) {
720 if (FLAG_trace_codegen) {
721 PrintF("Crankshaft Compiler - ");
722 }
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000723 CodeGenerator::MakeCodePrologue(info());
lrn@chromium.org34e60782011-09-15 07:25:40 +0000724 Code::Flags flags = Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000725 Handle<Code> code =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000726 CodeGenerator::MakeCodeEpilogue(&assembler, flags, info());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000727 generator.FinishCode(code);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000728 CodeGenerator::PrintCode(code, info());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000729 return code;
730 }
731 return Handle<Code>::null();
732}
733
734
735HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000736 HBasicBlock* result = new(zone()) HBasicBlock(this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000737 blocks_.Add(result, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000738 return result;
739}
740
741
742void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000743 if (!FLAG_use_canonicalizing) return;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000744 HPhase phase("H_Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000745 for (int i = 0; i < blocks()->length(); ++i) {
746 HInstruction* instr = blocks()->at(i)->first();
747 while (instr != NULL) {
748 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000749 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000750 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000751 }
752 }
753}
754
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000755// Block ordering was implemented with two mutually recursive methods,
756// HGraph::Postorder and HGraph::PostorderLoopBlocks.
757// The recursion could lead to stack overflow so the algorithm has been
758// implemented iteratively.
759// At a high level the algorithm looks like this:
760//
761// Postorder(block, loop_header) : {
762// if (block has already been visited or is of another loop) return;
763// mark block as visited;
764// if (block is a loop header) {
765// VisitLoopMembers(block, loop_header);
766// VisitSuccessorsOfLoopHeader(block);
767// } else {
768// VisitSuccessors(block)
769// }
770// put block in result list;
771// }
772//
773// VisitLoopMembers(block, outer_loop_header) {
774// foreach (block b in block loop members) {
775// VisitSuccessorsOfLoopMember(b, outer_loop_header);
776// if (b is loop header) VisitLoopMembers(b);
777// }
778// }
779//
780// VisitSuccessorsOfLoopMember(block, outer_loop_header) {
781// foreach (block b in block successors) Postorder(b, outer_loop_header)
782// }
783//
784// VisitSuccessorsOfLoopHeader(block) {
785// foreach (block b in block successors) Postorder(b, block)
786// }
787//
788// VisitSuccessors(block, loop_header) {
789// foreach (block b in block successors) Postorder(b, loop_header)
790// }
791//
792// The ordering is started calling Postorder(entry, NULL).
793//
794// Each instance of PostorderProcessor represents the "stack frame" of the
795// recursion, and particularly keeps the state of the loop (iteration) of the
796// "Visit..." function it represents.
797// To recycle memory we keep all the frames in a double linked list but
798// this means that we cannot use constructors to initialize the frames.
799//
800class PostorderProcessor : public ZoneObject {
801 public:
802 // Back link (towards the stack bottom).
803 PostorderProcessor* parent() {return father_; }
804 // Forward link (towards the stack top).
805 PostorderProcessor* child() {return child_; }
806 HBasicBlock* block() { return block_; }
807 HLoopInformation* loop() { return loop_; }
808 HBasicBlock* loop_header() { return loop_header_; }
809
810 static PostorderProcessor* CreateEntryProcessor(Zone* zone,
811 HBasicBlock* block,
812 BitVector* visited) {
813 PostorderProcessor* result = new(zone) PostorderProcessor(NULL);
814 return result->SetupSuccessors(zone, block, NULL, visited);
815 }
816
817 PostorderProcessor* PerformStep(Zone* zone,
818 BitVector* visited,
819 ZoneList<HBasicBlock*>* order) {
820 PostorderProcessor* next =
821 PerformNonBacktrackingStep(zone, visited, order);
822 if (next != NULL) {
823 return next;
824 } else {
825 return Backtrack(zone, visited, order);
826 }
827 }
828
829 private:
830 explicit PostorderProcessor(PostorderProcessor* father)
831 : father_(father), child_(NULL), successor_iterator(NULL) { }
832
833 // Each enum value states the cycle whose state is kept by this instance.
834 enum LoopKind {
835 NONE,
836 SUCCESSORS,
837 SUCCESSORS_OF_LOOP_HEADER,
838 LOOP_MEMBERS,
839 SUCCESSORS_OF_LOOP_MEMBER
840 };
841
842 // Each "Setup..." method is like a constructor for a cycle state.
843 PostorderProcessor* SetupSuccessors(Zone* zone,
844 HBasicBlock* block,
845 HBasicBlock* loop_header,
846 BitVector* visited) {
847 if (block == NULL || visited->Contains(block->block_id()) ||
848 block->parent_loop_header() != loop_header) {
849 kind_ = NONE;
850 block_ = NULL;
851 loop_ = NULL;
852 loop_header_ = NULL;
853 return this;
854 } else {
855 block_ = block;
856 loop_ = NULL;
857 visited->Add(block->block_id());
858
859 if (block->IsLoopHeader()) {
860 kind_ = SUCCESSORS_OF_LOOP_HEADER;
861 loop_header_ = block;
862 InitializeSuccessors();
863 PostorderProcessor* result = Push(zone);
864 return result->SetupLoopMembers(zone, block, block->loop_information(),
865 loop_header);
866 } else {
867 ASSERT(block->IsFinished());
868 kind_ = SUCCESSORS;
869 loop_header_ = loop_header;
870 InitializeSuccessors();
871 return this;
872 }
873 }
874 }
875
876 PostorderProcessor* SetupLoopMembers(Zone* zone,
877 HBasicBlock* block,
878 HLoopInformation* loop,
879 HBasicBlock* loop_header) {
880 kind_ = LOOP_MEMBERS;
881 block_ = block;
882 loop_ = loop;
883 loop_header_ = loop_header;
884 InitializeLoopMembers();
885 return this;
886 }
887
888 PostorderProcessor* SetupSuccessorsOfLoopMember(
889 HBasicBlock* block,
890 HLoopInformation* loop,
891 HBasicBlock* loop_header) {
892 kind_ = SUCCESSORS_OF_LOOP_MEMBER;
893 block_ = block;
894 loop_ = loop;
895 loop_header_ = loop_header;
896 InitializeSuccessors();
897 return this;
898 }
899
900 // This method "allocates" a new stack frame.
901 PostorderProcessor* Push(Zone* zone) {
902 if (child_ == NULL) {
903 child_ = new(zone) PostorderProcessor(this);
904 }
905 return child_;
906 }
907
908 void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) {
909 ASSERT(block_->end()->FirstSuccessor() == NULL ||
910 order->Contains(block_->end()->FirstSuccessor()) ||
911 block_->end()->FirstSuccessor()->IsLoopHeader());
912 ASSERT(block_->end()->SecondSuccessor() == NULL ||
913 order->Contains(block_->end()->SecondSuccessor()) ||
914 block_->end()->SecondSuccessor()->IsLoopHeader());
915 order->Add(block_, zone);
916 }
917
918 // This method is the basic block to walk up the stack.
919 PostorderProcessor* Pop(Zone* zone,
920 BitVector* visited,
921 ZoneList<HBasicBlock*>* order) {
922 switch (kind_) {
923 case SUCCESSORS:
924 case SUCCESSORS_OF_LOOP_HEADER:
925 ClosePostorder(order, zone);
926 return father_;
927 case LOOP_MEMBERS:
928 return father_;
929 case SUCCESSORS_OF_LOOP_MEMBER:
930 if (block()->IsLoopHeader() && block() != loop_->loop_header()) {
931 // In this case we need to perform a LOOP_MEMBERS cycle so we
932 // initialize it and return this instead of father.
933 return SetupLoopMembers(zone, block(),
934 block()->loop_information(), loop_header_);
935 } else {
936 return father_;
937 }
938 case NONE:
939 return father_;
940 }
941 UNREACHABLE();
942 return NULL;
943 }
944
945 // Walks up the stack.
946 PostorderProcessor* Backtrack(Zone* zone,
947 BitVector* visited,
948 ZoneList<HBasicBlock*>* order) {
949 PostorderProcessor* parent = Pop(zone, visited, order);
950 while (parent != NULL) {
951 PostorderProcessor* next =
952 parent->PerformNonBacktrackingStep(zone, visited, order);
953 if (next != NULL) {
954 return next;
955 } else {
956 parent = parent->Pop(zone, visited, order);
957 }
958 }
959 return NULL;
960 }
961
962 PostorderProcessor* PerformNonBacktrackingStep(
963 Zone* zone,
964 BitVector* visited,
965 ZoneList<HBasicBlock*>* order) {
966 HBasicBlock* next_block;
967 switch (kind_) {
968 case SUCCESSORS:
969 next_block = AdvanceSuccessors();
970 if (next_block != NULL) {
971 PostorderProcessor* result = Push(zone);
972 return result->SetupSuccessors(zone, next_block,
973 loop_header_, visited);
974 }
975 break;
976 case SUCCESSORS_OF_LOOP_HEADER:
977 next_block = AdvanceSuccessors();
978 if (next_block != NULL) {
979 PostorderProcessor* result = Push(zone);
980 return result->SetupSuccessors(zone, next_block,
981 block(), visited);
982 }
983 break;
984 case LOOP_MEMBERS:
985 next_block = AdvanceLoopMembers();
986 if (next_block != NULL) {
987 PostorderProcessor* result = Push(zone);
988 return result->SetupSuccessorsOfLoopMember(next_block,
989 loop_, loop_header_);
990 }
991 break;
992 case SUCCESSORS_OF_LOOP_MEMBER:
993 next_block = AdvanceSuccessors();
994 if (next_block != NULL) {
995 PostorderProcessor* result = Push(zone);
996 return result->SetupSuccessors(zone, next_block,
997 loop_header_, visited);
998 }
999 break;
1000 case NONE:
1001 return NULL;
1002 }
1003 return NULL;
1004 }
1005
1006 // The following two methods implement a "foreach b in successors" cycle.
1007 void InitializeSuccessors() {
1008 loop_index = 0;
1009 loop_length = 0;
1010 successor_iterator = HSuccessorIterator(block_->end());
1011 }
1012
1013 HBasicBlock* AdvanceSuccessors() {
1014 if (!successor_iterator.Done()) {
1015 HBasicBlock* result = successor_iterator.Current();
1016 successor_iterator.Advance();
1017 return result;
1018 }
1019 return NULL;
1020 }
1021
1022 // The following two methods implement a "foreach b in loop members" cycle.
1023 void InitializeLoopMembers() {
1024 loop_index = 0;
1025 loop_length = loop_->blocks()->length();
1026 }
1027
1028 HBasicBlock* AdvanceLoopMembers() {
1029 if (loop_index < loop_length) {
1030 HBasicBlock* result = loop_->blocks()->at(loop_index);
1031 loop_index++;
1032 return result;
1033 } else {
1034 return NULL;
1035 }
1036 }
1037
1038 LoopKind kind_;
1039 PostorderProcessor* father_;
1040 PostorderProcessor* child_;
1041 HLoopInformation* loop_;
1042 HBasicBlock* block_;
1043 HBasicBlock* loop_header_;
1044 int loop_index;
1045 int loop_length;
1046 HSuccessorIterator successor_iterator;
1047};
1048
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001049
1050void HGraph::OrderBlocks() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001051 HPhase phase("H_Block ordering");
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001052 BitVector visited(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001053
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001054 ZoneList<HBasicBlock*> reverse_result(8, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001055 HBasicBlock* start = blocks_[0];
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001056 PostorderProcessor* postorder =
1057 PostorderProcessor::CreateEntryProcessor(zone(), start, &visited);
1058 while (postorder != NULL) {
1059 postorder = postorder->PerformStep(zone(), &visited, &reverse_result);
1060 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001061 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001062 int index = 0;
1063 for (int i = reverse_result.length() - 1; i >= 0; --i) {
1064 HBasicBlock* b = reverse_result[i];
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001065 blocks_.Add(b, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001066 b->set_block_id(index++);
1067 }
1068}
1069
1070
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001071void HGraph::AssignDominators() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001072 HPhase phase("H_Assign dominators", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001073 for (int i = 0; i < blocks_.length(); ++i) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001074 HBasicBlock* block = blocks_[i];
1075 if (block->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001076 // Only the first predecessor of a loop header is from outside the loop.
1077 // All others are back edges, and thus cannot dominate the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001078 block->AssignCommonDominator(block->predecessors()->first());
1079 block->AssignLoopSuccessorDominators();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001080 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00001081 for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001082 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
1083 }
1084 }
1085 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001086}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001087
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001088// Mark all blocks that are dominated by an unconditional soft deoptimize to
1089// prevent code motion across those blocks.
1090void HGraph::PropagateDeoptimizingMark() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001091 HPhase phase("H_Propagate deoptimizing mark", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001092 MarkAsDeoptimizingRecursively(entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001093}
1094
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001095void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
1096 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1097 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1098 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
1099 MarkAsDeoptimizingRecursively(dominated);
1100 }
1101}
1102
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001103void HGraph::EliminateRedundantPhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001104 HPhase phase("H_Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001105
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001106 // Worklist of phis that can potentially be eliminated. Initialized with
1107 // all phi nodes. When elimination of a phi node modifies another phi node
1108 // the modified phi node is added to the worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001109 ZoneList<HPhi*> worklist(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001110 for (int i = 0; i < blocks_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001111 worklist.AddAll(*blocks_[i]->phis(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001112 }
1113
1114 while (!worklist.is_empty()) {
1115 HPhi* phi = worklist.RemoveLast();
1116 HBasicBlock* block = phi->block();
1117
1118 // Skip phi node if it was already replaced.
1119 if (block == NULL) continue;
1120
1121 // Get replacement value if phi is redundant.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001122 HValue* replacement = phi->GetRedundantReplacement();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001123
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001124 if (replacement != NULL) {
1125 // Iterate through the uses and replace them all.
1126 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1127 HValue* value = it.value();
1128 value->SetOperandAt(it.index(), replacement);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001129 if (value->IsPhi()) worklist.Add(HPhi::cast(value), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001130 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001131 block->RemovePhi(phi);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001132 }
1133 }
1134}
1135
1136
1137void HGraph::EliminateUnreachablePhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001138 HPhase phase("H_Unreachable phi elimination", this);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001139
1140 // Initialize worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001141 ZoneList<HPhi*> phi_list(blocks_.length(), zone());
1142 ZoneList<HPhi*> worklist(blocks_.length(), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001143 for (int i = 0; i < blocks_.length(); ++i) {
1144 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
1145 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001146 phi_list.Add(phi, zone());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001147 // We can't eliminate phis in the receiver position in the environment
1148 // because in case of throwing an error we need this value to
1149 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001150 if (phi->HasRealUses() || phi->IsReceiver()) {
1151 phi->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001152 worklist.Add(phi, zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001153 }
1154 }
1155 }
1156
1157 // Iteratively mark live phis.
1158 while (!worklist.is_empty()) {
1159 HPhi* phi = worklist.RemoveLast();
1160 for (int i = 0; i < phi->OperandCount(); i++) {
1161 HValue* operand = phi->OperandAt(i);
1162 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
1163 HPhi::cast(operand)->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001164 worklist.Add(HPhi::cast(operand), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001165 }
1166 }
1167 }
1168
1169 // Remove unreachable phis.
1170 for (int i = 0; i < phi_list.length(); i++) {
1171 HPhi* phi = phi_list[i];
1172 if (!phi->is_live()) {
1173 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001174 block->RemovePhi(phi);
1175 block->RecordDeletedPhi(phi->merged_index());
1176 }
1177 }
1178}
1179
1180
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001181bool HGraph::CheckArgumentsPhiUses() {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00001182 int block_count = blocks_.length();
1183 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);
1186 // We don't support phi uses of arguments for now.
1187 if (phi->CheckFlag(HValue::kIsArguments)) return false;
1188 }
1189 }
1190 return true;
1191}
1192
1193
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001194bool HGraph::CheckConstPhiUses() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001195 int block_count = blocks_.length();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001196 for (int i = 0; i < block_count; ++i) {
1197 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1198 HPhi* phi = blocks_[i]->phis()->at(j);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001199 // Check for the hole value (from an uninitialized const).
1200 for (int k = 0; k < phi->OperandCount(); k++) {
1201 if (phi->OperandAt(k) == GetConstantHole()) return false;
1202 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001203 }
1204 }
1205 return true;
1206}
1207
1208
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001209void HGraph::CollectPhis() {
1210 int block_count = blocks_.length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001211 phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001212 for (int i = 0; i < block_count; ++i) {
1213 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1214 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001215 phi_list_->Add(phi, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001216 }
1217 }
1218}
1219
1220
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001221void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001222 BitVector in_worklist(GetMaximumValueID(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001223 for (int i = 0; i < worklist->length(); ++i) {
1224 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
1225 in_worklist.Add(worklist->at(i)->id());
1226 }
1227
1228 while (!worklist->is_empty()) {
1229 HValue* current = worklist->RemoveLast();
1230 in_worklist.Remove(current->id());
1231 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001232 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
1233 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001234 if (!in_worklist.Contains(use->id())) {
1235 in_worklist.Add(use->id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001236 worklist->Add(use, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001237 }
1238 }
1239 }
1240 }
1241}
1242
1243
1244class HRangeAnalysis BASE_EMBEDDED {
1245 public:
ulan@chromium.org812308e2012-02-29 15:58:45 +00001246 explicit HRangeAnalysis(HGraph* graph) :
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001247 graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001248
1249 void Analyze();
1250
1251 private:
1252 void TraceRange(const char* msg, ...);
1253 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001254 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
1255 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001256 void InferRange(HValue* value);
1257 void RollBackTo(int index);
1258 void AddRange(HValue* value, Range* range);
1259
1260 HGraph* graph_;
ulan@chromium.org812308e2012-02-29 15:58:45 +00001261 Zone* zone_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001262 ZoneList<HValue*> changed_ranges_;
1263};
1264
1265
1266void HRangeAnalysis::TraceRange(const char* msg, ...) {
1267 if (FLAG_trace_range) {
1268 va_list arguments;
1269 va_start(arguments, msg);
1270 OS::VPrint(msg, arguments);
1271 va_end(arguments);
1272 }
1273}
1274
1275
1276void HRangeAnalysis::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001277 HPhase phase("H_Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001278 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001279}
1280
1281
1282void HRangeAnalysis::Analyze(HBasicBlock* block) {
1283 TraceRange("Analyzing block B%d\n", block->block_id());
1284
1285 int last_changed_range = changed_ranges_.length() - 1;
1286
1287 // Infer range based on control flow.
1288 if (block->predecessors()->length() == 1) {
1289 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001290 if (pred->end()->IsCompareIDAndBranch()) {
1291 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001292 }
1293 }
1294
1295 // Process phi instructions.
1296 for (int i = 0; i < block->phis()->length(); ++i) {
1297 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001298 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001299 }
1300
1301 // Go through all instructions of the current block.
1302 HInstruction* instr = block->first();
1303 while (instr != block->end()) {
1304 InferRange(instr);
1305 instr = instr->next();
1306 }
1307
1308 // Continue analysis in all dominated blocks.
1309 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1310 Analyze(block->dominated_blocks()->at(i));
1311 }
1312
1313 RollBackTo(last_changed_range);
1314}
1315
1316
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001317void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
1318 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001319 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001320 if (test->GetInputRepresentation().IsInteger32()) {
1321 Token::Value op = test->token();
1322 if (test->SecondSuccessor() == dest) {
1323 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001324 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001325 Token::Value inverted_op = Token::InvertCompareOp(op);
1326 UpdateControlFlowRange(op, test->left(), test->right());
1327 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001328 }
1329}
1330
1331
1332// We know that value [op] other. Use this information to update the range on
1333// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001334void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
1335 HValue* value,
1336 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001337 Range temp_range;
1338 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001339 Range* new_range = NULL;
1340
1341 TraceRange("Control flow range infer %d %s %d\n",
1342 value->id(),
1343 Token::Name(op),
1344 other->id());
1345
1346 if (op == Token::EQ || op == Token::EQ_STRICT) {
1347 // The same range has to apply for value.
ulan@chromium.org812308e2012-02-29 15:58:45 +00001348 new_range = range->Copy(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001349 } else if (op == Token::LT || op == Token::LTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001350 new_range = range->CopyClearLower(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001351 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001352 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001353 }
1354 } else if (op == Token::GT || op == Token::GTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001355 new_range = range->CopyClearUpper(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001356 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001357 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001358 }
1359 }
1360
1361 if (new_range != NULL && !new_range->IsMostGeneric()) {
1362 AddRange(value, new_range);
1363 }
1364}
1365
1366
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001367void HRangeAnalysis::InferRange(HValue* value) {
1368 ASSERT(!value->HasRange());
1369 if (!value->representation().IsNone()) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001370 value->ComputeInitialRange(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001371 Range* range = value->range();
1372 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1373 value->id(),
1374 value->Mnemonic(),
1375 range->lower(),
1376 range->upper());
1377 }
1378}
1379
1380
1381void HRangeAnalysis::RollBackTo(int index) {
1382 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1383 changed_ranges_[i]->RemoveLastAddedRange();
1384 }
1385 changed_ranges_.Rewind(index + 1);
1386}
1387
1388
1389void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1390 Range* original_range = value->range();
ulan@chromium.org812308e2012-02-29 15:58:45 +00001391 value->AddNewRange(range, zone_);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001392 changed_ranges_.Add(value, zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001393 Range* new_range = value->range();
1394 TraceRange("Updated range of %d set to [%d,%d]\n",
1395 value->id(),
1396 new_range->lower(),
1397 new_range->upper());
1398 if (original_range != NULL) {
1399 TraceRange("Original range was [%d,%d]\n",
1400 original_range->lower(),
1401 original_range->upper());
1402 }
1403 TraceRange("New information was [%d,%d]\n",
1404 range->lower(),
1405 range->upper());
1406}
1407
1408
1409void TraceGVN(const char* msg, ...) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001410 va_list arguments;
1411 va_start(arguments, msg);
1412 OS::VPrint(msg, arguments);
1413 va_end(arguments);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001414}
1415
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001416// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when
1417// --trace-gvn is off.
1418#define TRACE_GVN_1(msg, a1) \
1419 if (FLAG_trace_gvn) { \
1420 TraceGVN(msg, a1); \
1421 }
1422
1423#define TRACE_GVN_2(msg, a1, a2) \
1424 if (FLAG_trace_gvn) { \
1425 TraceGVN(msg, a1, a2); \
1426 }
1427
1428#define TRACE_GVN_3(msg, a1, a2, a3) \
1429 if (FLAG_trace_gvn) { \
1430 TraceGVN(msg, a1, a2, a3); \
1431 }
1432
1433#define TRACE_GVN_4(msg, a1, a2, a3, a4) \
1434 if (FLAG_trace_gvn) { \
1435 TraceGVN(msg, a1, a2, a3, a4); \
1436 }
1437
1438#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \
1439 if (FLAG_trace_gvn) { \
1440 TraceGVN(msg, a1, a2, a3, a4, a5); \
1441 }
1442
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001443
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001444HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001445 : array_size_(other->array_size_),
1446 lists_size_(other->lists_size_),
1447 count_(other->count_),
1448 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001449 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
1450 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001451 free_list_head_(other->free_list_head_) {
1452 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
1453 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
1454}
1455
1456
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001457void HValueMap::Kill(GVNFlagSet flags) {
1458 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags);
1459 if (!present_flags_.ContainsAnyOf(depends_flags)) return;
1460 present_flags_.RemoveAll();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001461 for (int i = 0; i < array_size_; ++i) {
1462 HValue* value = array_[i].value;
1463 if (value != NULL) {
1464 // Clear list of collisions first, so we know if it becomes empty.
1465 int kept = kNil; // List of kept elements.
1466 int next;
1467 for (int current = array_[i].next; current != kNil; current = next) {
1468 next = lists_[current].next;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001469 HValue* value = lists_[current].value;
1470 if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001471 // Drop it.
1472 count_--;
1473 lists_[current].next = free_list_head_;
1474 free_list_head_ = current;
1475 } else {
1476 // Keep it.
1477 lists_[current].next = kept;
1478 kept = current;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001479 present_flags_.Add(value->gvn_flags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001480 }
1481 }
1482 array_[i].next = kept;
1483
1484 // Now possibly drop directly indexed element.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001485 value = array_[i].value;
1486 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001487 count_--;
1488 int head = array_[i].next;
1489 if (head == kNil) {
1490 array_[i].value = NULL;
1491 } else {
1492 array_[i].value = lists_[head].value;
1493 array_[i].next = lists_[head].next;
1494 lists_[head].next = free_list_head_;
1495 free_list_head_ = head;
1496 }
1497 } else {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001498 present_flags_.Add(value->gvn_flags()); // Keep it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001499 }
1500 }
1501 }
1502}
1503
1504
1505HValue* HValueMap::Lookup(HValue* value) const {
1506 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
1507 uint32_t pos = Bound(hash);
1508 if (array_[pos].value != NULL) {
1509 if (array_[pos].value->Equals(value)) return array_[pos].value;
1510 int next = array_[pos].next;
1511 while (next != kNil) {
1512 if (lists_[next].value->Equals(value)) return lists_[next].value;
1513 next = lists_[next].next;
1514 }
1515 }
1516 return NULL;
1517}
1518
1519
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001520void HValueMap::Resize(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001521 ASSERT(new_size > count_);
1522 // Hashing the values into the new array has no more collisions than in the
1523 // old hash map, so we can use the existing lists_ array, if we are careful.
1524
1525 // Make sure we have at least one free element.
1526 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001527 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001528 }
1529
1530 HValueMapListElement* new_array =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001531 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001532 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
1533
1534 HValueMapListElement* old_array = array_;
1535 int old_size = array_size_;
1536
1537 int old_count = count_;
1538 count_ = 0;
1539 // Do not modify present_flags_. It is currently correct.
1540 array_size_ = new_size;
1541 array_ = new_array;
1542
1543 if (old_array != NULL) {
1544 // Iterate over all the elements in lists, rehashing them.
1545 for (int i = 0; i < old_size; ++i) {
1546 if (old_array[i].value != NULL) {
1547 int current = old_array[i].next;
1548 while (current != kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001549 Insert(lists_[current].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001550 int next = lists_[current].next;
1551 lists_[current].next = free_list_head_;
1552 free_list_head_ = current;
1553 current = next;
1554 }
1555 // Rehash the directly stored value.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001556 Insert(old_array[i].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001557 }
1558 }
1559 }
1560 USE(old_count);
1561 ASSERT(count_ == old_count);
1562}
1563
1564
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001565void HValueMap::ResizeLists(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001566 ASSERT(new_size > lists_size_);
1567
1568 HValueMapListElement* new_lists =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001569 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001570 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
1571
1572 HValueMapListElement* old_lists = lists_;
1573 int old_size = lists_size_;
1574
1575 lists_size_ = new_size;
1576 lists_ = new_lists;
1577
1578 if (old_lists != NULL) {
1579 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
1580 }
1581 for (int i = old_size; i < lists_size_; ++i) {
1582 lists_[i].next = free_list_head_;
1583 free_list_head_ = i;
1584 }
1585}
1586
1587
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001588void HValueMap::Insert(HValue* value, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001589 ASSERT(value != NULL);
1590 // Resizing when half of the hashtable is filled up.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001591 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001592 ASSERT(count_ < array_size_);
1593 count_++;
1594 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
1595 if (array_[pos].value == NULL) {
1596 array_[pos].value = value;
1597 array_[pos].next = kNil;
1598 } else {
1599 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001600 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001601 }
1602 int new_element_pos = free_list_head_;
1603 ASSERT(new_element_pos != kNil);
1604 free_list_head_ = lists_[free_list_head_].next;
1605 lists_[new_element_pos].value = value;
1606 lists_[new_element_pos].next = array_[pos].next;
1607 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
1608 array_[pos].next = new_element_pos;
1609 }
1610}
1611
1612
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001613HSideEffectMap::HSideEffectMap() : count_(0) {
1614 memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize);
1615}
1616
1617
1618HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001619 *this = *other; // Calls operator=.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001620}
1621
1622
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001623HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001624 if (this != &other) {
1625 memcpy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize);
1626 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001627 return *this;
1628}
1629
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001630void HSideEffectMap::Kill(GVNFlagSet flags) {
1631 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
1632 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
1633 if (flags.Contains(changes_flag)) {
1634 if (data_[i] != NULL) count_--;
1635 data_[i] = NULL;
1636 }
1637 }
1638}
1639
1640
1641void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) {
1642 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
1643 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
1644 if (flags.Contains(changes_flag)) {
1645 if (data_[i] == NULL) count_++;
1646 data_[i] = instr;
1647 }
1648 }
1649}
1650
1651
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001652class HStackCheckEliminator BASE_EMBEDDED {
1653 public:
1654 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
1655
1656 void Process();
1657
1658 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001659 HGraph* graph_;
1660};
1661
1662
1663void HStackCheckEliminator::Process() {
1664 // For each loop block walk the dominator tree from the backwards branch to
1665 // the loop header. If a call instruction is encountered the backwards branch
1666 // is dominated by a call and the stack check in the backwards branch can be
1667 // removed.
1668 for (int i = 0; i < graph_->blocks()->length(); i++) {
1669 HBasicBlock* block = graph_->blocks()->at(i);
1670 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001671 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
1672 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00001673 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001674 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001675 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001676 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00001677 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001678 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001679 }
1680 instr = instr->next();
1681 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00001682
1683 // Done when the loop header is processed.
1684 if (dominator == block) break;
1685
1686 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001687 dominator = dominator->dominator();
1688 }
1689 }
1690 }
1691}
1692
1693
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001694// Simple sparse set with O(1) add, contains, and clear.
1695class SparseSet {
1696 public:
1697 SparseSet(Zone* zone, int capacity)
1698 : capacity_(capacity),
1699 length_(0),
1700 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001701 sparse_(zone->NewArray<int>(capacity)) {
1702#ifndef NVALGRIND
1703 // Initialize the sparse array to make valgrind happy.
1704 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
1705#endif
1706 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001707
1708 bool Contains(int n) const {
1709 ASSERT(0 <= n && n < capacity_);
1710 int d = sparse_[n];
1711 return 0 <= d && d < length_ && dense_[d] == n;
1712 }
1713
1714 bool Add(int n) {
1715 if (Contains(n)) return false;
1716 dense_[length_] = n;
1717 sparse_[n] = length_;
1718 ++length_;
1719 return true;
1720 }
1721
1722 void Clear() { length_ = 0; }
1723
1724 private:
1725 int capacity_;
1726 int length_;
1727 int* dense_;
1728 int* sparse_;
1729
1730 DISALLOW_COPY_AND_ASSIGN(SparseSet);
1731};
1732
1733
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001734class HGlobalValueNumberer BASE_EMBEDDED {
1735 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001736 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001737 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001738 info_(info),
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001739 removed_side_effects_(false),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001740 block_side_effects_(graph->blocks()->length(), graph->zone()),
1741 loop_side_effects_(graph->blocks()->length(), graph->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001742 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001743 ASSERT(info->isolate()->heap()->allow_allocation(false));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001744 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
1745 graph_->zone());
1746 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
1747 graph_->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001748 }
1749 ~HGlobalValueNumberer() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001750 ASSERT(!info_->isolate()->heap()->allow_allocation(true));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001751 }
1752
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001753 // Returns true if values with side effects are removed.
1754 bool Analyze();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001755
1756 private:
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001757 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
1758 HBasicBlock* dominator,
1759 HBasicBlock* dominated);
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001760 void AnalyzeGraph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001761 void ComputeBlockSideEffects();
1762 void LoopInvariantCodeMotion();
1763 void ProcessLoopBlock(HBasicBlock* block,
1764 HBasicBlock* before_loop,
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001765 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001766 GVNFlagSet* accumulated_first_time_depends,
1767 GVNFlagSet* accumulated_first_time_changes);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001768 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001769 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
1770
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001771 HGraph* graph() { return graph_; }
1772 CompilationInfo* info() { return info_; }
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001773 Zone* zone() const { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001774
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001775 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001776 CompilationInfo* info_;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001777 bool removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001778
1779 // A map of block IDs to their side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001780 ZoneList<GVNFlagSet> block_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001781
1782 // A map of loop header block IDs to their loop's side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001783 ZoneList<GVNFlagSet> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001784
1785 // Used when collecting side effects on paths from dominator to
1786 // dominated.
1787 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001788};
1789
1790
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001791bool HGlobalValueNumberer::Analyze() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001792 removed_side_effects_ = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001793 ComputeBlockSideEffects();
1794 if (FLAG_loop_invariant_code_motion) {
1795 LoopInvariantCodeMotion();
1796 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001797 AnalyzeGraph();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001798 return removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001799}
1800
1801
1802void HGlobalValueNumberer::ComputeBlockSideEffects() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001803 // The Analyze phase of GVN can be called multiple times. Clear loop side
1804 // effects before computing them to erase the contents from previous Analyze
1805 // passes.
1806 for (int i = 0; i < loop_side_effects_.length(); ++i) {
1807 loop_side_effects_[i].RemoveAll();
1808 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001809 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1810 // Compute side effects for the block.
1811 HBasicBlock* block = graph_->blocks()->at(i);
1812 HInstruction* instr = block->first();
1813 int id = block->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001814 GVNFlagSet side_effects;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001815 while (instr != NULL) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001816 side_effects.Add(instr->ChangesFlags());
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00001817 if (instr->IsSoftDeoptimize()) {
1818 block_side_effects_[id].RemoveAll();
1819 side_effects.RemoveAll();
1820 break;
1821 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001822 instr = instr->next();
1823 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001824 block_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001825
1826 // Loop headers are part of their loop.
1827 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001828 loop_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001829 }
1830
1831 // Propagate loop side effects upwards.
1832 if (block->HasParentLoopHeader()) {
1833 int header_id = block->parent_loop_header()->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001834 loop_side_effects_[header_id].Add(block->IsLoopHeader()
1835 ? loop_side_effects_[id]
1836 : side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001837 }
1838 }
1839}
1840
1841
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001842SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) {
1843 char underlying_buffer[kLastFlag * 128];
1844 Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer));
1845#if DEBUG
1846 int offset = 0;
1847 const char* separator = "";
1848 const char* comma = ", ";
1849 buffer[0] = 0;
1850 uint32_t set_depends_on = 0;
1851 uint32_t set_changes = 0;
1852 for (int bit = 0; bit < kLastFlag; ++bit) {
1853 if ((flags.ToIntegral() & (1 << bit)) != 0) {
1854 if (bit % 2 == 0) {
1855 set_changes++;
1856 } else {
1857 set_depends_on++;
1858 }
1859 }
1860 }
1861 bool positive_changes = set_changes < (kLastFlag / 2);
1862 bool positive_depends_on = set_depends_on < (kLastFlag / 2);
1863 if (set_changes > 0) {
1864 if (positive_changes) {
1865 offset += OS::SNPrintF(buffer + offset, "changes [");
1866 } else {
1867 offset += OS::SNPrintF(buffer + offset, "changes all except [");
1868 }
1869 for (int bit = 0; bit < kLastFlag; ++bit) {
1870 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) {
1871 switch (static_cast<GVNFlag>(bit)) {
1872#define DECLARE_FLAG(type) \
1873 case kChanges##type: \
1874 offset += OS::SNPrintF(buffer + offset, separator); \
1875 offset += OS::SNPrintF(buffer + offset, #type); \
1876 separator = comma; \
1877 break;
1878GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
1879GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
1880#undef DECLARE_FLAG
1881 default:
1882 break;
1883 }
1884 }
1885 }
1886 offset += OS::SNPrintF(buffer + offset, "]");
1887 }
1888 if (set_depends_on > 0) {
1889 separator = "";
1890 if (set_changes > 0) {
1891 offset += OS::SNPrintF(buffer + offset, ", ");
1892 }
1893 if (positive_depends_on) {
1894 offset += OS::SNPrintF(buffer + offset, "depends on [");
1895 } else {
1896 offset += OS::SNPrintF(buffer + offset, "depends on all except [");
1897 }
1898 for (int bit = 0; bit < kLastFlag; ++bit) {
1899 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) {
1900 switch (static_cast<GVNFlag>(bit)) {
1901#define DECLARE_FLAG(type) \
1902 case kDependsOn##type: \
1903 offset += OS::SNPrintF(buffer + offset, separator); \
1904 offset += OS::SNPrintF(buffer + offset, #type); \
1905 separator = comma; \
1906 break;
1907GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
1908GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
1909#undef DECLARE_FLAG
1910 default:
1911 break;
1912 }
1913 }
1914 }
1915 offset += OS::SNPrintF(buffer + offset, "]");
1916 }
1917#else
1918 OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral());
1919#endif
1920 size_t string_len = strlen(underlying_buffer) + 1;
1921 ASSERT(string_len <= sizeof(underlying_buffer));
1922 char* result = new char[strlen(underlying_buffer) + 1];
1923 memcpy(result, underlying_buffer, string_len);
1924 return SmartArrayPointer<char>(result);
1925}
1926
1927
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001928void HGlobalValueNumberer::LoopInvariantCodeMotion() {
1929 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1930 HBasicBlock* block = graph_->blocks()->at(i);
1931 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001932 GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001933 TRACE_GVN_2("Try loop invariant motion for block B%d %s\n",
1934 block->block_id(),
1935 *GetGVNFlagsString(side_effects));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001936
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001937 GVNFlagSet accumulated_first_time_depends;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001938 GVNFlagSet accumulated_first_time_changes;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001939 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
1940 for (int j = block->block_id(); j <= last->block_id(); ++j) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001941 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001942 &accumulated_first_time_depends,
1943 &accumulated_first_time_changes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001944 }
1945 }
1946 }
1947}
1948
1949
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001950void HGlobalValueNumberer::ProcessLoopBlock(
1951 HBasicBlock* block,
1952 HBasicBlock* loop_header,
1953 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001954 GVNFlagSet* first_time_depends,
1955 GVNFlagSet* first_time_changes) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001956 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001957 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001958 TRACE_GVN_2("Loop invariant motion for B%d %s\n",
1959 block->block_id(),
1960 *GetGVNFlagsString(depends_flags));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001961 HInstruction* instr = block->first();
1962 while (instr != NULL) {
1963 HInstruction* next = instr->next();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001964 bool hoisted = false;
1965 if (instr->CheckFlag(HValue::kUseGVN)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001966 TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n",
1967 instr->id(),
1968 instr->Mnemonic(),
1969 *GetGVNFlagsString(instr->gvn_flags()),
1970 *GetGVNFlagsString(loop_kills));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001971 bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001972 if (instr->IsTransitionElementsKind()) {
1973 // It's possible to hoist transitions out of a loop as long as the
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001974 // hoisting wouldn't move the transition past an instruction that has a
1975 // DependsOn flag for anything it changes.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001976 GVNFlagSet hoist_depends_blockers =
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001977 HValue::ConvertChangesToDependsFlags(instr->ChangesFlags());
1978
1979 // In addition, the transition must not be hoisted above elements kind
1980 // changes, or if the transition is destructive to the elements buffer,
1981 // changes to array pointer or array contents.
1982 GVNFlagSet hoist_change_blockers;
1983 hoist_change_blockers.Add(kChangesElementsKind);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001984 HTransitionElementsKind* trans = HTransitionElementsKind::cast(instr);
1985 if (trans->original_map()->has_fast_double_elements()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001986 hoist_change_blockers.Add(kChangesElementsPointer);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001987 hoist_change_blockers.Add(kChangesDoubleArrayElements);
1988 }
1989 if (trans->transitioned_map()->has_fast_double_elements()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00001990 hoist_change_blockers.Add(kChangesElementsPointer);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00001991 hoist_change_blockers.Add(kChangesArrayElements);
1992 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001993 if (FLAG_trace_gvn) {
1994 GVNFlagSet hoist_blockers = hoist_depends_blockers;
1995 hoist_blockers.Add(hoist_change_blockers);
1996 GVNFlagSet first_time = *first_time_changes;
1997 first_time.Add(*first_time_depends);
1998 TRACE_GVN_4("Checking dependencies on HTransitionElementsKind "
1999 "%d (%s) hoist blockers: %s; "
2000 "first-time accumulated: %s\n",
2001 instr->id(),
2002 instr->Mnemonic(),
2003 *GetGVNFlagsString(hoist_blockers),
2004 *GetGVNFlagsString(first_time));
2005 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002006 // It's possible to hoist transition from the current loop loop only if
2007 // they dominate all of the successor blocks in the same loop and there
2008 // are not any instructions that have Changes/DependsOn that intervene
2009 // between it and the beginning of the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002010 bool in_nested_loop = block != loop_header &&
2011 ((block->parent_loop_header() != loop_header) ||
2012 block->IsLoopHeader());
2013 can_hoist = !in_nested_loop &&
2014 block->IsLoopSuccessorDominator() &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002015 !first_time_depends->ContainsAnyOf(hoist_depends_blockers) &&
2016 !first_time_changes->ContainsAnyOf(hoist_change_blockers);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002017 }
2018
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002019 if (can_hoist) {
2020 bool inputs_loop_invariant = true;
2021 for (int i = 0; i < instr->OperandCount(); ++i) {
2022 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
2023 inputs_loop_invariant = false;
2024 }
2025 }
2026
2027 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002028 TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id());
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002029 // Move the instruction out of the loop.
2030 instr->Unlink();
2031 instr->InsertBefore(pre_header->end());
2032 if (instr->HasSideEffects()) removed_side_effects_ = true;
2033 hoisted = true;
2034 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002035 }
2036 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002037 if (!hoisted) {
2038 // If an instruction is not hoisted, we have to account for its side
2039 // effects when hoisting later HTransitionElementsKind instructions.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002040 GVNFlagSet previous_depends = *first_time_depends;
2041 GVNFlagSet previous_changes = *first_time_changes;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002042 first_time_depends->Add(instr->DependsOnFlags());
2043 first_time_changes->Add(instr->ChangesFlags());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002044 if (!(previous_depends == *first_time_depends)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002045 TRACE_GVN_1("Updated first-time accumulated %s\n",
2046 *GetGVNFlagsString(*first_time_depends));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002047 }
2048 if (!(previous_changes == *first_time_changes)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002049 TRACE_GVN_1("Updated first-time accumulated %s\n",
2050 *GetGVNFlagsString(*first_time_changes));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002051 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002052 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002053 instr = next;
2054 }
2055}
2056
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00002057
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002058bool HGlobalValueNumberer::AllowCodeMotion() {
2059 return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount;
2060}
2061
2062
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002063bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
2064 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002065 // If we've disabled code motion or we're in a block that unconditionally
2066 // deoptimizes, don't move any instructions.
2067 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002068}
2069
2070
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002071GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002072 HBasicBlock* dominator, HBasicBlock* dominated) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002073 GVNFlagSet side_effects;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002074 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
2075 HBasicBlock* block = dominated->predecessors()->at(i);
2076 if (dominator->block_id() < block->block_id() &&
2077 block->block_id() < dominated->block_id() &&
2078 visited_on_paths_.Add(block->block_id())) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002079 side_effects.Add(block_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002080 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002081 side_effects.Add(loop_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002082 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002083 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
2084 dominator, block));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002085 }
2086 }
2087 return side_effects;
2088}
2089
2090
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002091// Each instance of this class is like a "stack frame" for the recursive
2092// traversal of the dominator tree done during GVN (the stack is handled
2093// as a double linked list).
2094// We reuse frames when possible so the list length is limited by the depth
2095// of the dominator tree but this forces us to initialize each frame calling
2096// an explicit "Initialize" method instead of a using constructor.
2097class GvnBasicBlockState: public ZoneObject {
2098 public:
2099 static GvnBasicBlockState* CreateEntry(Zone* zone,
2100 HBasicBlock* entry_block,
2101 HValueMap* entry_map) {
2102 return new(zone)
2103 GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002104 }
2105
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002106 HBasicBlock* block() { return block_; }
2107 HValueMap* map() { return map_; }
2108 HSideEffectMap* dominators() { return &dominators_; }
2109
2110 GvnBasicBlockState* next_in_dominator_tree_traversal(
2111 Zone* zone,
2112 HBasicBlock** dominator) {
2113 // This assignment needs to happen before calling next_dominated() because
2114 // that call can reuse "this" if we are at the last dominated block.
2115 *dominator = block();
2116 GvnBasicBlockState* result = next_dominated(zone);
2117 if (result == NULL) {
2118 GvnBasicBlockState* dominator_state = pop();
2119 if (dominator_state != NULL) {
2120 // This branch is guaranteed not to return NULL because pop() never
2121 // returns a state where "is_done() == true".
2122 *dominator = dominator_state->block();
2123 result = dominator_state->next_dominated(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002124 } else {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002125 // Unnecessary (we are returning NULL) but done for cleanness.
2126 *dominator = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002127 }
2128 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002129 return result;
2130 }
2131
2132 private:
2133 void Initialize(HBasicBlock* block,
2134 HValueMap* map,
2135 HSideEffectMap* dominators,
2136 bool copy_map,
2137 Zone* zone) {
2138 block_ = block;
2139 map_ = copy_map ? map->Copy(zone) : map;
2140 dominated_index_ = -1;
2141 length_ = block->dominated_blocks()->length();
2142 if (dominators != NULL) {
2143 dominators_ = *dominators;
2144 }
2145 }
2146 bool is_done() { return dominated_index_ >= length_; }
2147
2148 GvnBasicBlockState(GvnBasicBlockState* previous,
2149 HBasicBlock* block,
2150 HValueMap* map,
2151 HSideEffectMap* dominators,
2152 Zone* zone)
2153 : previous_(previous), next_(NULL) {
2154 Initialize(block, map, dominators, true, zone);
2155 }
2156
2157 GvnBasicBlockState* next_dominated(Zone* zone) {
2158 dominated_index_++;
2159 if (dominated_index_ == length_ - 1) {
2160 // No need to copy the map for the last child in the dominator tree.
2161 Initialize(block_->dominated_blocks()->at(dominated_index_),
2162 map(),
2163 dominators(),
2164 false,
2165 zone);
2166 return this;
2167 } else if (dominated_index_ < length_) {
2168 return push(zone,
2169 block_->dominated_blocks()->at(dominated_index_),
2170 dominators());
2171 } else {
2172 return NULL;
2173 }
2174 }
2175
2176 GvnBasicBlockState* push(Zone* zone,
2177 HBasicBlock* block,
2178 HSideEffectMap* dominators) {
2179 if (next_ == NULL) {
2180 next_ =
2181 new(zone) GvnBasicBlockState(this, block, map(), dominators, zone);
2182 } else {
2183 next_->Initialize(block, map(), dominators, true, zone);
2184 }
2185 return next_;
2186 }
2187 GvnBasicBlockState* pop() {
2188 GvnBasicBlockState* result = previous_;
2189 while (result != NULL && result->is_done()) {
2190 TRACE_GVN_2("Backtracking from block B%d to block b%d\n",
2191 block()->block_id(),
2192 previous_->block()->block_id())
2193 result = result->previous_;
2194 }
2195 return result;
2196 }
2197
2198 GvnBasicBlockState* previous_;
2199 GvnBasicBlockState* next_;
2200 HBasicBlock* block_;
2201 HValueMap* map_;
2202 HSideEffectMap dominators_;
2203 int dominated_index_;
2204 int length_;
2205};
2206
2207// This is a recursive traversal of the dominator tree but it has been turned
2208// into a loop to avoid stack overflows.
2209// The logical "stack frames" of the recursion are kept in a list of
2210// GvnBasicBlockState instances.
2211void HGlobalValueNumberer::AnalyzeGraph() {
2212 HBasicBlock* entry_block = graph_->entry_block();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002213 HValueMap* entry_map = new(zone()) HValueMap(zone());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002214 GvnBasicBlockState* current =
2215 GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map);
2216
2217 while (current != NULL) {
2218 HBasicBlock* block = current->block();
2219 HValueMap* map = current->map();
2220 HSideEffectMap* dominators = current->dominators();
2221
2222 TRACE_GVN_2("Analyzing block B%d%s\n",
2223 block->block_id(),
2224 block->IsLoopHeader() ? " (loop header)" : "");
2225
2226 // If this is a loop header kill everything killed by the loop.
2227 if (block->IsLoopHeader()) {
2228 map->Kill(loop_side_effects_[block->block_id()]);
2229 }
2230
2231 // Go through all instructions of the current block.
2232 HInstruction* instr = block->first();
2233 while (instr != NULL) {
2234 HInstruction* next = instr->next();
2235 GVNFlagSet flags = instr->ChangesFlags();
2236 if (!flags.IsEmpty()) {
2237 // Clear all instructions in the map that are affected by side effects.
2238 // Store instruction as the dominating one for tracked side effects.
2239 map->Kill(flags);
2240 dominators->Store(flags, instr);
2241 TRACE_GVN_2("Instruction %d %s\n", instr->id(),
2242 *GetGVNFlagsString(flags));
2243 }
2244 if (instr->CheckFlag(HValue::kUseGVN)) {
2245 ASSERT(!instr->HasObservableSideEffects());
2246 HValue* other = map->Lookup(instr);
2247 if (other != NULL) {
2248 ASSERT(instr->Equals(other) && other->Equals(instr));
2249 TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n",
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002250 instr->id(),
2251 instr->Mnemonic(),
2252 other->id(),
2253 other->Mnemonic());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002254 if (instr->HasSideEffects()) removed_side_effects_ = true;
2255 instr->DeleteAndReplaceWith(other);
2256 } else {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002257 map->Add(instr, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002258 }
2259 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002260 if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) {
2261 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
2262 HValue* other = dominators->at(i);
2263 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
2264 GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i);
2265 if (instr->DependsOnFlags().Contains(depends_on_flag) &&
2266 (other != NULL)) {
2267 TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n",
2268 i,
2269 instr->id(),
2270 instr->Mnemonic(),
2271 other->id(),
2272 other->Mnemonic());
2273 instr->SetSideEffectDominator(changes_flag, other);
2274 }
2275 }
2276 }
2277 instr = next;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002278 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002279
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002280 HBasicBlock* dominator_block;
2281 GvnBasicBlockState* next =
2282 current->next_in_dominator_tree_traversal(zone(), &dominator_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002283
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002284 if (next != NULL) {
2285 HBasicBlock* dominated = next->block();
2286 HValueMap* successor_map = next->map();
2287 HSideEffectMap* successor_dominators = next->dominators();
2288
2289 // Kill everything killed on any path between this block and the
2290 // dominated block. We don't have to traverse these paths if the
2291 // value map and the dominators list is already empty. If the range
2292 // of block ids (block_id, dominated_id) is empty there are no such
2293 // paths.
2294 if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) &&
2295 dominator_block->block_id() + 1 < dominated->block_id()) {
2296 visited_on_paths_.Clear();
2297 GVNFlagSet side_effects_on_all_paths =
2298 CollectSideEffectsOnPathsToDominatedBlock(dominator_block,
2299 dominated);
2300 successor_map->Kill(side_effects_on_all_paths);
2301 successor_dominators->Kill(side_effects_on_all_paths);
2302 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002303 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002304 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002305 }
2306}
2307
2308
2309class HInferRepresentation BASE_EMBEDDED {
2310 public:
2311 explicit HInferRepresentation(HGraph* graph)
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002312 : graph_(graph),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002313 worklist_(8, graph->zone()),
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002314 in_worklist_(graph->GetMaximumValueID(), graph->zone()) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002315
2316 void Analyze();
2317
2318 private:
2319 Representation TryChange(HValue* current);
2320 void AddToWorklist(HValue* current);
2321 void InferBasedOnInputs(HValue* current);
2322 void AddDependantsToWorklist(HValue* current);
2323 void InferBasedOnUses(HValue* current);
2324
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002325 Zone* zone() const { return graph_->zone(); }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002326
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002327 HGraph* graph_;
2328 ZoneList<HValue*> worklist_;
2329 BitVector in_worklist_;
2330};
2331
2332
2333void HInferRepresentation::AddToWorklist(HValue* current) {
2334 if (current->representation().IsSpecialization()) return;
2335 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
2336 if (in_worklist_.Contains(current->id())) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002337 worklist_.Add(current, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002338 in_worklist_.Add(current->id());
2339}
2340
2341
2342// This method tries to specialize the representation type of the value
2343// given as a parameter. The value is asked to infer its representation type
2344// based on its inputs. If the inferred type is more specialized, then this
2345// becomes the new representation type of the node.
2346void HInferRepresentation::InferBasedOnInputs(HValue* current) {
2347 Representation r = current->representation();
2348 if (r.IsSpecialization()) return;
2349 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
2350 Representation inferred = current->InferredRepresentation();
2351 if (inferred.IsSpecialization()) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00002352 if (FLAG_trace_representation) {
2353 PrintF("Changing #%d representation %s -> %s based on inputs\n",
2354 current->id(),
2355 r.Mnemonic(),
2356 inferred.Mnemonic());
2357 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002358 current->ChangeRepresentation(inferred);
2359 AddDependantsToWorklist(current);
2360 }
2361}
2362
2363
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002364void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
2365 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2366 AddToWorklist(it.value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002367 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002368 for (int i = 0; i < value->OperandCount(); ++i) {
2369 AddToWorklist(value->OperandAt(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002370 }
2371}
2372
2373
2374// This method calculates whether specializing the representation of the value
2375// given as the parameter has a benefit in terms of less necessary type
2376// conversions. If there is a benefit, then the representation of the value is
2377// specialized.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002378void HInferRepresentation::InferBasedOnUses(HValue* value) {
2379 Representation r = value->representation();
2380 if (r.IsSpecialization() || value->HasNoUses()) return;
2381 ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
2382 Representation new_rep = TryChange(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002383 if (!new_rep.IsNone()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002384 if (!value->representation().Equals(new_rep)) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00002385 if (FLAG_trace_representation) {
2386 PrintF("Changing #%d representation %s -> %s based on uses\n",
2387 value->id(),
2388 r.Mnemonic(),
2389 new_rep.Mnemonic());
2390 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002391 value->ChangeRepresentation(new_rep);
2392 AddDependantsToWorklist(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002393 }
2394 }
2395}
2396
2397
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002398Representation HInferRepresentation::TryChange(HValue* value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002399 // Array of use counts for each representation.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002400 int use_count[Representation::kNumRepresentations] = { 0 };
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002401
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002402 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2403 HValue* use = it.value();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002404 Representation rep = use->ObservedInputRepresentation(it.index());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002405 if (rep.IsNone()) continue;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002406 if (FLAG_trace_representation) {
2407 PrintF("%d %s is used by %d %s as %s\n",
2408 value->id(),
2409 value->Mnemonic(),
2410 use->id(),
2411 use->Mnemonic(),
2412 rep.Mnemonic());
2413 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002414 if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002415 use_count[rep.kind()] += use->LoopWeight();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002416 }
2417 int tagged_count = use_count[Representation::kTagged];
2418 int double_count = use_count[Representation::kDouble];
2419 int int32_count = use_count[Representation::kInteger32];
2420 int non_tagged_count = double_count + int32_count;
2421
2422 // If a non-loop phi has tagged uses, don't convert it to untagged.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002423 if (value->IsPhi() && !value->block()->IsLoopHeader() && tagged_count > 0) {
2424 return Representation::None();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002425 }
2426
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002427 // Prefer unboxing over boxing, the latter is more expensive.
ricow@chromium.org27bf2882011-11-17 08:34:43 +00002428 if (tagged_count > non_tagged_count) return Representation::None();
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002429
2430 // Prefer Integer32 over Double, if possible.
2431 if (int32_count > 0 && value->IsConvertibleToInteger()) {
2432 return Representation::Integer32();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002433 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002434
2435 if (double_count > 0) return Representation::Double();
2436
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002437 return Representation::None();
2438}
2439
2440
2441void HInferRepresentation::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002442 HPhase phase("H_Infer representations", graph_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002443
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002444 // (1) Initialize bit vectors and count real uses. Each phi gets a
2445 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002446 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002447 int phi_count = phi_list->length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002448 ZoneList<BitVector*> connected_phis(phi_count, graph_->zone());
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 phi_list->at(i)->InitRealUses(i);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002451 BitVector* connected_set = new(zone()) BitVector(phi_count, graph_->zone());
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00002452 connected_set->Add(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002453 connected_phis.Add(connected_set, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002454 }
2455
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002456 // (2) Do a fixed point iteration to find the set of connected phis. A
2457 // phi is connected to another phi if its value is used either directly or
2458 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002459 bool change = true;
2460 while (change) {
2461 change = false;
ricow@chromium.org55ee8072011-09-08 16:33:10 +00002462 // We normally have far more "forward edges" than "backward edges",
2463 // so we terminate faster when we walk backwards.
2464 for (int i = phi_count - 1; i >= 0; --i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002465 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002466 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2467 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002468 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002469 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002470 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
2471 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002472 }
2473 }
2474 }
2475 }
2476
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002477 // (3a) Use the phi reachability information from step 2 to
2478 // push information about values which can't be converted to integer
2479 // without deoptimization through the phi use-def chains, avoiding
2480 // unnecessary deoptimizations later.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002481 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002482 HPhi* phi = phi_list->at(i);
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002483 bool cti = phi->AllOperandsConvertibleToInteger();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002484 if (cti) continue;
2485
2486 for (BitVector::Iterator it(connected_phis.at(i));
2487 !it.Done();
2488 it.Advance()) {
2489 HPhi* phi = phi_list->at(it.Current());
2490 phi->set_is_convertible_to_integer(false);
2491 phi->ResetInteger32Uses();
2492 }
2493 }
2494
2495 // (3b) Use the phi reachability information from step 2 to
2496 // sum up the non-phi use counts of all connected phis.
2497 for (int i = 0; i < phi_count; ++i) {
2498 HPhi* phi = phi_list->at(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002499 for (BitVector::Iterator it(connected_phis.at(i));
2500 !it.Done();
2501 it.Advance()) {
2502 int index = it.Current();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002503 HPhi* it_use = phi_list->at(index);
2504 if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002505 }
2506 }
2507
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002508 // Initialize work list
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002509 for (int i = 0; i < graph_->blocks()->length(); ++i) {
2510 HBasicBlock* block = graph_->blocks()->at(i);
2511 const ZoneList<HPhi*>* phis = block->phis();
2512 for (int j = 0; j < phis->length(); ++j) {
2513 AddToWorklist(phis->at(j));
2514 }
2515
2516 HInstruction* current = block->first();
2517 while (current != NULL) {
2518 AddToWorklist(current);
2519 current = current->next();
2520 }
2521 }
2522
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002523 // Do a fixed point iteration, trying to improve representations
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002524 while (!worklist_.is_empty()) {
2525 HValue* current = worklist_.RemoveLast();
2526 in_worklist_.Remove(current->id());
2527 InferBasedOnInputs(current);
2528 InferBasedOnUses(current);
2529 }
2530}
2531
2532
2533void HGraph::InitializeInferredTypes() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002534 HPhase phase("H_Inferring types", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002535 InitializeInferredTypes(0, this->blocks_.length() - 1);
2536}
2537
2538
2539void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
2540 for (int i = from_inclusive; i <= to_inclusive; ++i) {
2541 HBasicBlock* block = blocks_[i];
2542
2543 const ZoneList<HPhi*>* phis = block->phis();
2544 for (int j = 0; j < phis->length(); j++) {
2545 phis->at(j)->UpdateInferredType();
2546 }
2547
2548 HInstruction* current = block->first();
2549 while (current != NULL) {
2550 current->UpdateInferredType();
2551 current = current->next();
2552 }
2553
2554 if (block->IsLoopHeader()) {
2555 HBasicBlock* last_back_edge =
2556 block->loop_information()->GetLastBackEdge();
2557 InitializeInferredTypes(i + 1, last_back_edge->block_id());
2558 // Skip all blocks already processed by the recursive call.
2559 i = last_back_edge->block_id();
2560 // Update phis of the loop header now after the whole loop body is
2561 // guaranteed to be processed.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002562 ZoneList<HValue*> worklist(block->phis()->length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002563 for (int j = 0; j < block->phis()->length(); ++j) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002564 worklist.Add(block->phis()->at(j), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002565 }
2566 InferTypes(&worklist);
2567 }
2568 }
2569}
2570
2571
2572void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
2573 HValue* current = value;
2574 while (current != NULL) {
2575 if (visited->Contains(current->id())) return;
2576
2577 // For phis, we must propagate the check to all of its inputs.
2578 if (current->IsPhi()) {
2579 visited->Add(current->id());
2580 HPhi* phi = HPhi::cast(current);
2581 for (int i = 0; i < phi->OperandCount(); ++i) {
2582 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
2583 }
2584 break;
2585 }
2586
2587 // For multiplication and division, we must propagate to the left and
2588 // the right side.
2589 if (current->IsMul()) {
2590 HMul* mul = HMul::cast(current);
2591 mul->EnsureAndPropagateNotMinusZero(visited);
2592 PropagateMinusZeroChecks(mul->left(), visited);
2593 PropagateMinusZeroChecks(mul->right(), visited);
2594 } else if (current->IsDiv()) {
2595 HDiv* div = HDiv::cast(current);
2596 div->EnsureAndPropagateNotMinusZero(visited);
2597 PropagateMinusZeroChecks(div->left(), visited);
2598 PropagateMinusZeroChecks(div->right(), visited);
2599 }
2600
2601 current = current->EnsureAndPropagateNotMinusZero(visited);
2602 }
2603}
2604
2605
2606void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002607 HValue* use_value,
2608 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002609 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002610 // Insert the representation change right before its use. For phi-uses we
2611 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002612 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002613 if (use_value->IsPhi()) {
2614 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002615 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002616 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002617 }
2618
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002619 // For constants we try to make the representation change at compile
2620 // time. When a representation change is not possible without loss of
2621 // information we treat constants like normal instructions and insert the
2622 // change instructions for them.
2623 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002624 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002625 bool deoptimize_on_undefined =
2626 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002627 if (value->IsConstant()) {
2628 HConstant* constant = HConstant::cast(value);
2629 // Try to create a new copy of the constant with the new representation.
2630 new_value = is_truncating
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002631 ? constant->CopyToTruncatedInt32(zone())
2632 : constant->CopyToRepresentation(to, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002633 }
2634
2635 if (new_value == NULL) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002636 new_value = new(zone()) HChange(value, to,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002637 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002638 }
2639
2640 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002641 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002642}
2643
2644
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002645void HGraph::InsertRepresentationChangesForValue(HValue* value) {
2646 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002647 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002648 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002649
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002650 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
2651 HValue* use_value = it.value();
2652 int use_index = it.index();
2653 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002654 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002655 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002656 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002657 if (value->HasNoUses()) {
2658 ASSERT(value->IsConstant());
2659 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002660 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00002661
2662 // The only purpose of a HForceRepresentation is to represent the value
2663 // after the (possible) HChange instruction. We make it disappear.
2664 if (value->IsForceRepresentation()) {
2665 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
2666 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002667}
2668
2669
2670void HGraph::InsertRepresentationChanges() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002671 HPhase phase("H_Representation changes", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002672
2673 // Compute truncation flag for phis: Initially assume that all
2674 // int32-phis allow truncation and iteratively remove the ones that
2675 // are used in an operation that does not allow a truncating
2676 // conversion.
2677 // TODO(fschneider): Replace this with a worklist-based iteration.
2678 for (int i = 0; i < phi_list()->length(); i++) {
2679 HPhi* phi = phi_list()->at(i);
2680 if (phi->representation().IsInteger32()) {
2681 phi->SetFlag(HValue::kTruncatingToInt32);
2682 }
2683 }
2684 bool change = true;
2685 while (change) {
2686 change = false;
2687 for (int i = 0; i < phi_list()->length(); i++) {
2688 HPhi* phi = phi_list()->at(i);
2689 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
ulan@chromium.org812308e2012-02-29 15:58:45 +00002690 if (!phi->CheckUsesForFlag(HValue::kTruncatingToInt32)) {
2691 phi->ClearFlag(HValue::kTruncatingToInt32);
2692 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002693 }
2694 }
2695 }
2696
2697 for (int i = 0; i < blocks_.length(); ++i) {
2698 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002699 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
2700 for (int j = 0; j < phis->length(); j++) {
2701 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002702 }
2703
2704 // Process normal instructions.
2705 HInstruction* current = blocks_[i]->first();
2706 while (current != NULL) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002707 InsertRepresentationChangesForValue(current);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002708 current = current->next();
2709 }
2710 }
2711}
2712
2713
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002714void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
2715 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
2716 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
2717 for (int i = 0; i < phi->OperandCount(); ++i) {
2718 HValue* input = phi->OperandAt(i);
2719 if (input->IsPhi()) {
2720 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
2721 }
2722 }
2723}
2724
2725
2726void HGraph::MarkDeoptimizeOnUndefined() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002727 HPhase phase("H_MarkDeoptimizeOnUndefined", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002728 // Compute DeoptimizeOnUndefined flag for phis.
2729 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002730 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002731 // double input representation, has this flag set.
2732 // The flag is used by HChange tagged->double, which must deoptimize
2733 // if one of its uses has this flag set.
2734 for (int i = 0; i < phi_list()->length(); i++) {
2735 HPhi* phi = phi_list()->at(i);
2736 if (phi->representation().IsDouble()) {
2737 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2738 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
2739 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
2740 break;
2741 }
2742 }
2743 }
2744 }
2745}
2746
2747
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00002748void HGraph::ComputeMinusZeroChecks() {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002749 BitVector visited(GetMaximumValueID(), zone());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00002750 for (int i = 0; i < blocks_.length(); ++i) {
2751 for (HInstruction* current = blocks_[i]->first();
2752 current != NULL;
2753 current = current->next()) {
2754 if (current->IsChange()) {
2755 HChange* change = HChange::cast(current);
2756 // Propagate flags for negative zero checks upwards from conversions
2757 // int32-to-tagged and int32-to-double.
2758 Representation from = change->value()->representation();
2759 ASSERT(from.Equals(change->from()));
2760 if (from.IsInteger32()) {
2761 ASSERT(change->to().IsTagged() || change->to().IsDouble());
2762 ASSERT(visited.IsEmpty());
2763 PropagateMinusZeroChecks(change->value(), &visited);
2764 visited.Clear();
2765 }
2766 }
2767 }
2768 }
2769}
2770
2771
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002772// Implementation of utility class to encapsulate the translation state for
2773// a (possibly inlined) function.
2774FunctionState::FunctionState(HGraphBuilder* owner,
2775 CompilationInfo* info,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002776 TypeFeedbackOracle* oracle,
ulan@chromium.org967e2702012-02-28 09:49:15 +00002777 ReturnHandlingFlag return_handling)
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002778 : owner_(owner),
2779 compilation_info_(info),
2780 oracle_(oracle),
2781 call_context_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +00002782 return_handling_(return_handling),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002783 function_return_(NULL),
2784 test_context_(NULL),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002785 entry_(NULL),
2786 arguments_elements_(NULL),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002787 outer_(owner->function_state()) {
2788 if (outer_ != NULL) {
2789 // State for an inline function.
2790 if (owner->ast_context()->IsTest()) {
2791 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
2792 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
2793 if_true->MarkAsInlineReturnTarget();
2794 if_false->MarkAsInlineReturnTarget();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002795 Expression* cond = TestContext::cast(owner->ast_context())->condition();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002796 // The AstContext constructor pushed on the context stack. This newed
2797 // instance is the reason that AstContext can't be BASE_EMBEDDED.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002798 test_context_ = new TestContext(owner, cond, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002799 } else {
2800 function_return_ = owner->graph()->CreateBasicBlock();
2801 function_return()->MarkAsInlineReturnTarget();
2802 }
2803 // Set this after possibly allocating a new TestContext above.
2804 call_context_ = owner->ast_context();
2805 }
2806
2807 // Push on the state stack.
2808 owner->set_function_state(this);
2809}
2810
2811
2812FunctionState::~FunctionState() {
2813 delete test_context_;
2814 owner_->set_function_state(outer_);
2815}
2816
2817
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002818// Implementation of utility classes to represent an expression's context in
2819// the AST.
2820AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002821 : owner_(owner),
2822 kind_(kind),
2823 outer_(owner->ast_context()),
2824 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002825 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002826#ifdef DEBUG
ulan@chromium.org967e2702012-02-28 09:49:15 +00002827 ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002828 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002829#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002830}
2831
2832
2833AstContext::~AstContext() {
2834 owner_->set_ast_context(outer_); // Pop.
2835}
2836
2837
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002838EffectContext::~EffectContext() {
2839 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002840 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002841 (owner()->environment()->length() == original_length_ &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00002842 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002843}
2844
2845
2846ValueContext::~ValueContext() {
2847 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002848 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002849 (owner()->environment()->length() == original_length_ + 1 &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00002850 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002851}
2852
2853
2854void EffectContext::ReturnValue(HValue* value) {
2855 // The value is simply ignored.
2856}
2857
2858
2859void ValueContext::ReturnValue(HValue* value) {
2860 // The value is tracked in the bailout environment, and communicated
2861 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002862 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
2863 owner()->Bailout("bad value context for arguments value");
2864 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002865 owner()->Push(value);
2866}
2867
2868
2869void TestContext::ReturnValue(HValue* value) {
2870 BuildBranch(value);
2871}
2872
2873
2874void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002875 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002876 owner()->AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002877 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002878}
2879
2880
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002881void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002882 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002883 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2884 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2885 instr->SetSuccessorAt(0, empty_true);
2886 instr->SetSuccessorAt(1, empty_false);
2887 owner()->current_block()->Finish(instr);
2888 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
2889 owner()->set_current_block(join);
2890}
2891
2892
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002893void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002894 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002895 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002896 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002897 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002898 owner()->AddInstruction(instr);
2899 owner()->Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002900 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002901}
2902
2903
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002904void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002905 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002906 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
2907 return owner()->Bailout("bad value context for arguments object value");
2908 }
2909 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
2910 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
2911 instr->SetSuccessorAt(0, materialize_true);
2912 instr->SetSuccessorAt(1, materialize_false);
2913 owner()->current_block()->Finish(instr);
2914 owner()->set_current_block(materialize_true);
2915 owner()->Push(owner()->graph()->GetConstantTrue());
2916 owner()->set_current_block(materialize_false);
2917 owner()->Push(owner()->graph()->GetConstantFalse());
2918 HBasicBlock* join =
2919 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
2920 owner()->set_current_block(join);
2921}
2922
2923
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002924void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002925 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002926 HGraphBuilder* builder = owner();
2927 builder->AddInstruction(instr);
2928 // We expect a simulate after every expression with side effects, though
2929 // this one isn't actually needed (and wouldn't work if it were targeted).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002930 if (instr->HasObservableSideEffects()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002931 builder->Push(instr);
2932 builder->AddSimulate(ast_id);
2933 builder->Pop();
2934 }
2935 BuildBranch(instr);
2936}
2937
2938
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002939void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002940 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002941 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2942 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2943 instr->SetSuccessorAt(0, empty_true);
2944 instr->SetSuccessorAt(1, empty_false);
2945 owner()->current_block()->Finish(instr);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002946 empty_true->Goto(if_true(), owner()->function_state());
2947 empty_false->Goto(if_false(), owner()->function_state());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002948 owner()->set_current_block(NULL);
2949}
2950
2951
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002952void TestContext::BuildBranch(HValue* value) {
2953 // We expect the graph to be in edge-split form: there is no edge that
2954 // connects a branch node to a join node. We conservatively ensure that
2955 // property by always adding an empty block on the outgoing edges of this
2956 // branch.
2957 HGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002958 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002959 builder->Bailout("arguments object value in a test context");
2960 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002961 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
2962 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002963 unsigned test_id = condition()->test_id();
2964 ToBooleanStub::Types expected(builder->oracle()->ToBooleanTypes(test_id));
2965 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002966 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002967
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002968 empty_true->Goto(if_true(), owner()->function_state());
2969 empty_false->Goto(if_false(), owner()->function_state());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002970 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002971}
2972
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002973
2974// HGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002975#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002976 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002977 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002978 if (HasStackOverflow()) return; \
2979 } while (false)
2980
2981
danno@chromium.org160a7b02011-04-18 15:51:38 +00002982#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002983 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002984 call; \
2985 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002986 } while (false)
2987
2988
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002989void HGraphBuilder::Bailout(const char* reason) {
2990 if (FLAG_trace_bailout) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002991 SmartArrayPointer<char> name(
2992 info()->shared_info()->DebugName()->ToCString());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002993 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002994 }
2995 SetStackOverflow();
2996}
2997
2998
2999void HGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003000 EffectContext for_effect(this);
3001 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003002}
3003
3004
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003005void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
3006 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003007 Visit(expr);
3008}
3009
3010
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003011void HGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003012 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003013 for_value.set_for_typeof(true);
3014 Visit(expr);
3015}
3016
3017
3018
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003019void HGraphBuilder::VisitForControl(Expression* expr,
3020 HBasicBlock* true_block,
3021 HBasicBlock* false_block) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003022 TestContext for_test(this, expr, true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003023 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003024}
3025
3026
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00003027HValue* HGraphBuilder::VisitArgument(Expression* expr) {
3028 VisitForValue(expr);
3029 if (HasStackOverflow() || current_block() == NULL) return NULL;
3030 HValue* value = Pop();
3031 Push(AddInstruction(new(zone()) HPushArgument(value)));
3032 return value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003033}
3034
3035
3036void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
3037 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003038 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003039 }
3040}
3041
3042
3043void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
3044 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003045 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003046 }
3047}
3048
3049
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003050HGraph* HGraphBuilder::CreateGraph() {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00003051 graph_ = new(zone()) HGraph(info());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003052 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
3053
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003054 {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003055 HPhase phase("H_Block building");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003056 current_block_ = graph()->entry_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003057
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003058 Scope* scope = info()->scope();
3059 if (scope->HasIllegalRedeclaration()) {
3060 Bailout("function with illegal redeclaration");
3061 return NULL;
3062 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003063 if (scope->calls_eval()) {
3064 Bailout("function calls eval");
3065 return NULL;
3066 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003067 SetUpScope(scope);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003068
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003069 // Add an edge to the body entry. This is warty: the graph's start
3070 // environment will be used by the Lithium translation as the initial
3071 // environment on graph entry, but it has now been mutated by the
3072 // Hydrogen translation of the instructions in the start block. This
3073 // environment uses values which have not been defined yet. These
3074 // Hydrogen instructions will then be replayed by the Lithium
3075 // translation, so they cannot have an environment effect. The edge to
3076 // the body's entry block (along with some special logic for the start
3077 // block in HInstruction::InsertAfter) seals the start block from
3078 // getting unwanted instructions inserted.
3079 //
3080 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
3081 // Make the Hydrogen instructions in the initial block into Hydrogen
3082 // values (but not instructions), present in the initial environment and
3083 // not replayed by the Lithium translation.
3084 HEnvironment* initial_env = environment()->CopyWithoutHistory();
3085 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
3086 current_block()->Goto(body_entry);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003087 body_entry->SetJoinId(AstNode::kFunctionEntryId);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003088 set_current_block(body_entry);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003089
3090 // Handle implicit declaration of the function name in named function
3091 // expressions before other declarations.
3092 if (scope->is_function_scope() && scope->function() != NULL) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003093 VisitVariableDeclaration(scope->function());
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003094 }
3095 VisitDeclarations(scope->declarations());
3096 AddSimulate(AstNode::kDeclarationsId);
3097
3098 HValue* context = environment()->LookupContext();
3099 AddInstruction(
3100 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
3101
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003102 VisitStatements(info()->function()->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003103 if (HasStackOverflow()) return NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003104
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003105 if (current_block() != NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003106 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003107 current_block()->FinishExit(instr);
3108 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003109 }
3110 }
3111
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003112 graph()->OrderBlocks();
3113 graph()->AssignDominators();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003114
3115#ifdef DEBUG
3116 // Do a full verify after building the graph and computing dominators.
3117 graph()->Verify(true);
3118#endif
3119
ricow@chromium.org2c99e282011-07-28 09:15:17 +00003120 graph()->PropagateDeoptimizingMark();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003121 if (!graph()->CheckConstPhiUses()) {
3122 Bailout("Unsupported phi use of const variable");
3123 return NULL;
3124 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003125 graph()->EliminateRedundantPhis();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003126 if (!graph()->CheckArgumentsPhiUses()) {
3127 Bailout("Unsupported phi use of arguments");
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00003128 return NULL;
3129 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003130 if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003131 graph()->CollectPhis();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003132
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00003133 if (graph()->has_osr_loop_entry()) {
3134 const ZoneList<HPhi*>* phis = graph()->osr_loop_entry()->phis();
3135 for (int j = 0; j < phis->length(); j++) {
3136 HPhi* phi = phis->at(j);
3137 graph()->osr_values()->at(phi->merged_index())->set_incoming_value(phi);
3138 }
3139 }
3140
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003141 HInferRepresentation rep(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003142 rep.Analyze();
3143
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003144 graph()->MarkDeoptimizeOnUndefined();
3145 graph()->InsertRepresentationChanges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003146
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003147 graph()->InitializeInferredTypes();
3148 graph()->Canonicalize();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003149
3150 // Perform common subexpression elimination and loop-invariant code motion.
3151 if (FLAG_use_gvn) {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003152 HPhase phase("H_Global value numbering", graph());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003153 HGlobalValueNumberer gvn(graph(), info());
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003154 bool removed_side_effects = gvn.Analyze();
3155 // Trigger a second analysis pass to further eliminate duplicate values that
3156 // could only be discovered by removing side-effect-generating instructions
3157 // during the first pass.
3158 if (FLAG_smi_only_arrays && removed_side_effects) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003159 removed_side_effects = gvn.Analyze();
3160 ASSERT(!removed_side_effects);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003161 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003162 }
3163
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003164 if (FLAG_use_range) {
3165 HRangeAnalysis rangeAnalysis(graph());
3166 rangeAnalysis.Analyze();
3167 }
3168 graph()->ComputeMinusZeroChecks();
3169
3170 // Eliminate redundant stack checks on backwards branches.
3171 HStackCheckEliminator sce(graph());
3172 sce.Process();
3173
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003174 graph()->EliminateRedundantBoundsChecks();
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00003175 graph()->DehoistSimpleArrayIndexComputations();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003176
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003177 return graph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003178}
3179
3180
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003181// We try to "factor up" HBoundsCheck instructions towards the root of the
3182// dominator tree.
3183// For now we handle checks where the index is like "exp + int32value".
3184// If in the dominator tree we check "exp + v1" and later (dominated)
3185// "exp + v2", if v2 <= v1 we can safely remove the second check, and if
3186// v2 > v1 we can use v2 in the 1st check and again remove the second.
3187// To do so we keep a dictionary of all checks where the key if the pair
3188// "exp, length".
3189// The class BoundsCheckKey represents this key.
3190class BoundsCheckKey : public ZoneObject {
3191 public:
3192 HValue* IndexBase() const { return index_base_; }
3193 HValue* Length() const { return length_; }
3194
3195 uint32_t Hash() {
3196 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
3197 }
3198
3199 static BoundsCheckKey* Create(Zone* zone,
3200 HBoundsCheck* check,
3201 int32_t* offset) {
3202 HValue* index_base = NULL;
3203 HConstant* constant = NULL;
3204 bool is_sub = false;
3205
3206 if (check->index()->IsAdd()) {
3207 HAdd* index = HAdd::cast(check->index());
3208 if (index->left()->IsConstant()) {
3209 constant = HConstant::cast(index->left());
3210 index_base = index->right();
3211 } else if (index->right()->IsConstant()) {
3212 constant = HConstant::cast(index->right());
3213 index_base = index->left();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003214 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003215 } else if (check->index()->IsSub()) {
3216 HSub* index = HSub::cast(check->index());
3217 is_sub = true;
3218 if (index->left()->IsConstant()) {
3219 constant = HConstant::cast(index->left());
3220 index_base = index->right();
3221 } else if (index->right()->IsConstant()) {
3222 constant = HConstant::cast(index->right());
3223 index_base = index->left();
3224 }
3225 }
3226
3227 if (constant != NULL && constant->HasInteger32Value()) {
3228 *offset = is_sub ? - constant->Integer32Value()
3229 : constant->Integer32Value();
3230 } else {
3231 *offset = 0;
3232 index_base = check->index();
3233 }
3234
3235 return new(zone) BoundsCheckKey(index_base, check->length());
3236 }
3237
3238 private:
3239 BoundsCheckKey(HValue* index_base, HValue* length)
3240 : index_base_(index_base),
3241 length_(length) { }
3242
3243 HValue* index_base_;
3244 HValue* length_;
3245};
3246
3247
3248// Data about each HBoundsCheck that can be eliminated or moved.
3249// It is the "value" in the dictionary indexed by "base-index, length"
3250// (the key is BoundsCheckKey).
3251// We scan the code with a dominator tree traversal.
3252// Traversing the dominator tree we keep a stack (implemented as a singly
3253// linked list) of "data" for each basic block that contains a relevant check
3254// with the same key (the dictionary holds the head of the list).
3255// We also keep all the "data" created for a given basic block in a list, and
3256// use it to "clean up" the dictionary when backtracking in the dominator tree
3257// traversal.
3258// Doing this each dictionary entry always directly points to the check that
3259// is dominating the code being examined now.
3260// We also track the current "offset" of the index expression and use it to
3261// decide if any check is already "covered" (so it can be removed) or not.
3262class BoundsCheckBbData: public ZoneObject {
3263 public:
3264 BoundsCheckKey* Key() const { return key_; }
3265 int32_t LowerOffset() const { return lower_offset_; }
3266 int32_t UpperOffset() const { return upper_offset_; }
3267 HBasicBlock* BasicBlock() const { return basic_block_; }
3268 HBoundsCheck* Check() const { return check_; }
3269 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
3270 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
3271
3272 bool OffsetIsCovered(int32_t offset) const {
3273 return offset >= LowerOffset() && offset <= UpperOffset();
3274 }
3275
3276 // This method removes new_check and modifies the current check so that it
3277 // also "covers" what new_check covered.
3278 // The obvious precondition is that new_check follows Check() in the
3279 // same basic block, and that new_offset is not covered (otherwise we
3280 // could simply remove new_check).
3281 // As a consequence LowerOffset() or UpperOffset() change (the covered
3282 // range grows).
3283 //
3284 // In the general case the check covering the current range should be like
3285 // these two checks:
3286 // 0 <= Key()->IndexBase() + LowerOffset()
3287 // Key()->IndexBase() + UpperOffset() < Key()->Length()
3288 //
3289 // We can transform the second check like this:
3290 // Key()->IndexBase() + LowerOffset() <
3291 // Key()->Length() + (LowerOffset() - UpperOffset())
3292 // so we can handle both checks with a single unsigned comparison.
3293 //
3294 // The bulk of this method changes Check()->index() and Check()->length()
3295 // replacing them with new HAdd instructions to perform the transformation
3296 // described above.
3297 void CoverCheck(HBoundsCheck* new_check,
3298 int32_t new_offset) {
3299 ASSERT(new_check->index()->representation().IsInteger32());
3300
3301 if (new_offset > upper_offset_) {
3302 upper_offset_ = new_offset;
3303 } else if (new_offset < lower_offset_) {
3304 lower_offset_ = new_offset;
3305 } else {
3306 ASSERT(false);
3307 }
3308
3309 BuildOffsetAdd(&added_index_,
3310 &added_index_offset_,
3311 Key()->IndexBase(),
3312 new_check->index()->representation(),
3313 lower_offset_);
3314 Check()->SetOperandAt(0, added_index_);
3315 BuildOffsetAdd(&added_length_,
3316 &added_length_offset_,
3317 Key()->Length(),
3318 new_check->length()->representation(),
3319 lower_offset_ - upper_offset_);
3320 Check()->SetOperandAt(1, added_length_);
3321
3322 new_check->DeleteAndReplaceWith(NULL);
3323 }
3324
3325 void RemoveZeroOperations() {
3326 RemoveZeroAdd(&added_index_, &added_index_offset_);
3327 RemoveZeroAdd(&added_length_, &added_length_offset_);
3328 }
3329
3330 BoundsCheckBbData(BoundsCheckKey* key,
3331 int32_t lower_offset,
3332 int32_t upper_offset,
3333 HBasicBlock* bb,
3334 HBoundsCheck* check,
3335 BoundsCheckBbData* next_in_bb,
3336 BoundsCheckBbData* father_in_dt)
3337 : key_(key),
3338 lower_offset_(lower_offset),
3339 upper_offset_(upper_offset),
3340 basic_block_(bb),
3341 check_(check),
3342 added_index_offset_(NULL),
3343 added_index_(NULL),
3344 added_length_offset_(NULL),
3345 added_length_(NULL),
3346 next_in_bb_(next_in_bb),
3347 father_in_dt_(father_in_dt) { }
3348
3349 private:
3350 BoundsCheckKey* key_;
3351 int32_t lower_offset_;
3352 int32_t upper_offset_;
3353 HBasicBlock* basic_block_;
3354 HBoundsCheck* check_;
3355 HConstant* added_index_offset_;
3356 HAdd* added_index_;
3357 HConstant* added_length_offset_;
3358 HAdd* added_length_;
3359 BoundsCheckBbData* next_in_bb_;
3360 BoundsCheckBbData* father_in_dt_;
3361
3362 void BuildOffsetAdd(HAdd** add,
3363 HConstant** constant,
3364 HValue* original_value,
3365 Representation representation,
3366 int32_t new_offset) {
3367 HConstant* new_constant = new(BasicBlock()->zone())
3368 HConstant(Handle<Object>(Smi::FromInt(new_offset)),
3369 Representation::Integer32());
3370 if (*add == NULL) {
3371 new_constant->InsertBefore(Check());
3372 *add = new(BasicBlock()->zone()) HAdd(NULL,
3373 original_value,
3374 new_constant);
3375 (*add)->AssumeRepresentation(representation);
3376 (*add)->InsertBefore(Check());
3377 } else {
3378 new_constant->InsertBefore(*add);
3379 (*constant)->DeleteAndReplaceWith(new_constant);
3380 }
3381 *constant = new_constant;
3382 }
3383
3384 void RemoveZeroAdd(HAdd** add, HConstant** constant) {
3385 if (*add != NULL && (*constant)->Integer32Value() == 0) {
3386 (*add)->DeleteAndReplaceWith((*add)->left());
3387 (*constant)->DeleteAndReplaceWith(NULL);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003388 }
3389 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003390};
3391
3392
3393static bool BoundsCheckKeyMatch(void* key1, void* key2) {
3394 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
3395 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
3396 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
3397}
3398
3399
3400class BoundsCheckTable : private ZoneHashMap {
3401 public:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003402 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003403 return reinterpret_cast<BoundsCheckBbData**>(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003404 &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003405 }
3406
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003407 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone) {
3408 Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003409 }
3410
3411 void Delete(BoundsCheckKey* key) {
3412 Remove(key, key->Hash());
3413 }
3414
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003415 explicit BoundsCheckTable(Zone* zone)
3416 : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
3417 ZoneAllocationPolicy(zone)) { }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003418};
3419
3420
3421// Eliminates checks in bb and recursively in the dominated blocks.
3422// Also replace the results of check instructions with the original value, if
3423// the result is used. This is safe now, since we don't do code motion after
3424// this point. It enables better register allocation since the value produced
3425// by check instructions is really a copy of the original value.
3426void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
3427 BoundsCheckTable* table) {
3428 BoundsCheckBbData* bb_data_list = NULL;
3429
3430 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
3431 if (!i->IsBoundsCheck()) continue;
3432
3433 HBoundsCheck* check = HBoundsCheck::cast(i);
3434 check->ReplaceAllUsesWith(check->index());
3435
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003436 if (!FLAG_array_bounds_checks_elimination) continue;
3437
3438 int32_t offset;
3439 BoundsCheckKey* key =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003440 BoundsCheckKey::Create(zone(), check, &offset);
3441 BoundsCheckBbData** data_p = table->LookupOrInsert(key, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003442 BoundsCheckBbData* data = *data_p;
3443 if (data == NULL) {
3444 bb_data_list = new(zone()) BoundsCheckBbData(key,
3445 offset,
3446 offset,
3447 bb,
3448 check,
3449 bb_data_list,
3450 NULL);
3451 *data_p = bb_data_list;
3452 } else if (data->OffsetIsCovered(offset)) {
3453 check->DeleteAndReplaceWith(NULL);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003454 } else if (data->BasicBlock() == bb) {
3455 data->CoverCheck(check, offset);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003456 } else {
3457 int32_t new_lower_offset = offset < data->LowerOffset()
3458 ? offset
3459 : data->LowerOffset();
3460 int32_t new_upper_offset = offset > data->UpperOffset()
3461 ? offset
3462 : data->UpperOffset();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003463 bb_data_list = new(zone()) BoundsCheckBbData(key,
3464 new_lower_offset,
3465 new_upper_offset,
3466 bb,
3467 check,
3468 bb_data_list,
3469 data);
3470 table->Insert(key, bb_data_list, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003471 }
3472 }
3473
3474 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
3475 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table);
3476 }
3477
3478 for (BoundsCheckBbData* data = bb_data_list;
3479 data != NULL;
3480 data = data->NextInBasicBlock()) {
3481 data->RemoveZeroOperations();
3482 if (data->FatherInDominatorTree()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003483 table->Insert(data->Key(), data->FatherInDominatorTree(), zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003484 } else {
3485 table->Delete(data->Key());
3486 }
3487 }
3488}
3489
3490
3491void HGraph::EliminateRedundantBoundsChecks() {
3492 HPhase phase("H_Eliminate bounds checks", this);
3493 AssertNoAllocation no_gc;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003494 BoundsCheckTable checks_table(zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003495 EliminateRedundantBoundsChecks(entry_block(), &checks_table);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003496}
3497
3498
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00003499static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
3500 HValue* index = array_operation->GetKey();
3501
3502 HConstant* constant;
3503 HValue* subexpression;
3504 int32_t sign;
3505 if (index->IsAdd()) {
3506 sign = 1;
3507 HAdd* add = HAdd::cast(index);
3508 if (add->left()->IsConstant()) {
3509 subexpression = add->right();
3510 constant = HConstant::cast(add->left());
3511 } else if (add->right()->IsConstant()) {
3512 subexpression = add->left();
3513 constant = HConstant::cast(add->right());
3514 } else {
3515 return;
3516 }
3517 } else if (index->IsSub()) {
3518 sign = -1;
3519 HSub* sub = HSub::cast(index);
3520 if (sub->left()->IsConstant()) {
3521 subexpression = sub->right();
3522 constant = HConstant::cast(sub->left());
3523 } else if (sub->right()->IsConstant()) {
3524 subexpression = sub->left();
3525 constant = HConstant::cast(sub->right());
3526 } return;
3527 } else {
3528 return;
3529 }
3530
3531 if (!constant->HasInteger32Value()) return;
3532 int32_t value = constant->Integer32Value() * sign;
3533 // We limit offset values to 30 bits because we want to avoid the risk of
3534 // overflows when the offset is added to the object header size.
3535 if (value >= 1 << 30 || value < 0) return;
3536 array_operation->SetKey(subexpression);
3537 if (index->HasNoUses()) {
3538 index->DeleteAndReplaceWith(NULL);
3539 }
3540 ASSERT(value >= 0);
3541 array_operation->SetIndexOffset(static_cast<uint32_t>(value));
3542 array_operation->SetDehoisted(true);
3543}
3544
3545
3546void HGraph::DehoistSimpleArrayIndexComputations() {
3547 if (!FLAG_array_index_dehoisting) return;
3548
3549 HPhase phase("H_Dehoist index computations", this);
3550 for (int i = 0; i < blocks()->length(); ++i) {
3551 for (HInstruction* instr = blocks()->at(i)->first();
3552 instr != NULL;
3553 instr = instr->next()) {
3554 ArrayInstructionInterface* array_instruction = NULL;
3555 if (instr->IsLoadKeyedFastElement()) {
3556 HLoadKeyedFastElement* op = HLoadKeyedFastElement::cast(instr);
3557 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3558 } else if (instr->IsLoadKeyedFastDoubleElement()) {
3559 HLoadKeyedFastDoubleElement* op =
3560 HLoadKeyedFastDoubleElement::cast(instr);
3561 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3562 } else if (instr->IsLoadKeyedSpecializedArrayElement()) {
3563 HLoadKeyedSpecializedArrayElement* op =
3564 HLoadKeyedSpecializedArrayElement::cast(instr);
3565 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3566 } else if (instr->IsStoreKeyedFastElement()) {
3567 HStoreKeyedFastElement* op = HStoreKeyedFastElement::cast(instr);
3568 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3569 } else if (instr->IsStoreKeyedFastDoubleElement()) {
3570 HStoreKeyedFastDoubleElement* op =
3571 HStoreKeyedFastDoubleElement::cast(instr);
3572 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3573 } else if (instr->IsStoreKeyedSpecializedArrayElement()) {
3574 HStoreKeyedSpecializedArrayElement* op =
3575 HStoreKeyedSpecializedArrayElement::cast(instr);
3576 array_instruction = static_cast<ArrayInstructionInterface*>(op);
3577 } else {
3578 continue;
3579 }
3580 DehoistArrayIndex(array_instruction);
3581 }
3582 }
3583}
3584
3585
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003586HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003587 ASSERT(current_block() != NULL);
3588 current_block()->AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003589 return instr;
3590}
3591
3592
ager@chromium.org04921a82011-06-27 13:21:41 +00003593void HGraphBuilder::AddSimulate(int ast_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003594 ASSERT(current_block() != NULL);
ager@chromium.org04921a82011-06-27 13:21:41 +00003595 current_block()->AddSimulate(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003596}
3597
3598
3599void HGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003600 ASSERT(current_block() != NULL);
3601 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003602}
3603
3604
3605void HGraphBuilder::PushAndAdd(HInstruction* instr) {
3606 Push(instr);
3607 AddInstruction(instr);
3608}
3609
3610
ulan@chromium.org967e2702012-02-28 09:49:15 +00003611template <class Instruction>
3612HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003613 int count = call->argument_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003614 ZoneList<HValue*> arguments(count, zone());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003615 for (int i = 0; i < count; ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003616 arguments.Add(Pop(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003617 }
3618
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003619 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003620 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003621 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003622 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003623}
3624
3625
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003626void HGraphBuilder::SetUpScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003627 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003628 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003629 AddInstruction(undefined_constant);
3630 graph_->set_undefined_constant(undefined_constant);
3631
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00003632 HArgumentsObject* object = new(zone()) HArgumentsObject;
3633 AddInstruction(object);
3634 graph()->SetArgumentsObject(object);
3635
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003636 // Set the initial values of parameters including "this". "This" has
3637 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003638 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
3639
3640 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003641 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003642 environment()->Bind(i, parameter);
3643 }
3644
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003645 // First special is HContext.
3646 HInstruction* context = AddInstruction(new(zone()) HContext);
3647 environment()->BindContext(context);
3648
3649 // Initialize specials and locals to undefined.
3650 for (int i = environment()->parameter_count() + 1;
3651 i < environment()->length();
3652 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003653 environment()->Bind(i, undefined_constant);
3654 }
3655
3656 // Handle the arguments and arguments shadow variables specially (they do
3657 // not have declarations).
3658 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003659 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003660 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003661 }
yangguo@chromium.org154ff992012-03-13 08:09:54 +00003662
yangguo@chromium.org154ff992012-03-13 08:09:54 +00003663 environment()->Bind(scope->arguments(),
3664 graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003665 }
3666}
3667
3668
3669void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
3670 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003671 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003672 }
3673}
3674
3675
3676HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
3677 HBasicBlock* b = graph()->CreateBasicBlock();
3678 b->SetInitialEnvironment(env);
3679 return b;
3680}
3681
3682
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003683HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003684 HBasicBlock* header = graph()->CreateBasicBlock();
3685 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
3686 header->SetInitialEnvironment(entry_env);
3687 header->AttachLoopInformation();
3688 return header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003689}
3690
3691
3692void HGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003693 ASSERT(!HasStackOverflow());
3694 ASSERT(current_block() != NULL);
3695 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003696 if (stmt->scope() != NULL) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003697 return Bailout("ScopedBlock");
3698 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003699 BreakAndContinueInfo break_info(stmt);
3700 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003701 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003702 }
3703 HBasicBlock* break_block = break_info.break_block();
3704 if (break_block != NULL) {
3705 if (current_block() != NULL) current_block()->Goto(break_block);
3706 break_block->SetJoinId(stmt->ExitId());
3707 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003708 }
3709}
3710
3711
3712void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003713 ASSERT(!HasStackOverflow());
3714 ASSERT(current_block() != NULL);
3715 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003716 VisitForEffect(stmt->expression());
3717}
3718
3719
3720void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003721 ASSERT(!HasStackOverflow());
3722 ASSERT(current_block() != NULL);
3723 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003724}
3725
3726
3727void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003728 ASSERT(!HasStackOverflow());
3729 ASSERT(current_block() != NULL);
3730 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003731 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003732 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003733 Visit(stmt->then_statement());
3734 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003735 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003736 Visit(stmt->else_statement());
3737 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003738 HBasicBlock* cond_true = graph()->CreateBasicBlock();
3739 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003740 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003741
danno@chromium.org160a7b02011-04-18 15:51:38 +00003742 if (cond_true->HasPredecessor()) {
3743 cond_true->SetJoinId(stmt->ThenId());
3744 set_current_block(cond_true);
3745 CHECK_BAILOUT(Visit(stmt->then_statement()));
3746 cond_true = current_block();
3747 } else {
3748 cond_true = NULL;
3749 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003750
danno@chromium.org160a7b02011-04-18 15:51:38 +00003751 if (cond_false->HasPredecessor()) {
3752 cond_false->SetJoinId(stmt->ElseId());
3753 set_current_block(cond_false);
3754 CHECK_BAILOUT(Visit(stmt->else_statement()));
3755 cond_false = current_block();
3756 } else {
3757 cond_false = NULL;
3758 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003759
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003760 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003761 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003762 }
3763}
3764
3765
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003766HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
3767 BreakableStatement* stmt,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003768 BreakType type,
3769 int* drop_extra) {
3770 *drop_extra = 0;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003771 BreakAndContinueScope* current = this;
3772 while (current != NULL && current->info()->target() != stmt) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003773 *drop_extra += current->info()->drop_extra();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003774 current = current->next();
3775 }
3776 ASSERT(current != NULL); // Always found (unless stack is malformed).
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003777
3778 if (type == BREAK) {
3779 *drop_extra += current->info()->drop_extra();
3780 }
3781
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003782 HBasicBlock* block = NULL;
3783 switch (type) {
3784 case BREAK:
3785 block = current->info()->break_block();
3786 if (block == NULL) {
3787 block = current->owner()->graph()->CreateBasicBlock();
3788 current->info()->set_break_block(block);
3789 }
3790 break;
3791
3792 case CONTINUE:
3793 block = current->info()->continue_block();
3794 if (block == NULL) {
3795 block = current->owner()->graph()->CreateBasicBlock();
3796 current->info()->set_continue_block(block);
3797 }
3798 break;
3799 }
3800
3801 return block;
3802}
3803
3804
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003805void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003806 ASSERT(!HasStackOverflow());
3807 ASSERT(current_block() != NULL);
3808 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003809 int drop_extra = 0;
3810 HBasicBlock* continue_block = break_scope()->Get(stmt->target(),
3811 CONTINUE,
3812 &drop_extra);
3813 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003814 current_block()->Goto(continue_block);
3815 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003816}
3817
3818
3819void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003820 ASSERT(!HasStackOverflow());
3821 ASSERT(current_block() != NULL);
3822 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003823 int drop_extra = 0;
3824 HBasicBlock* break_block = break_scope()->Get(stmt->target(),
3825 BREAK,
3826 &drop_extra);
3827 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003828 current_block()->Goto(break_block);
3829 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003830}
3831
3832
3833void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003834 ASSERT(!HasStackOverflow());
3835 ASSERT(current_block() != NULL);
3836 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003837 AstContext* context = call_context();
3838 if (context == NULL) {
3839 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003840 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003841 HValue* result = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003842 current_block()->FinishExit(new(zone()) HReturn(result));
ulan@chromium.org967e2702012-02-28 09:49:15 +00003843 } else if (function_state()->is_construct()) {
3844 // Return from an inlined construct call. In a test context the return
3845 // value will always evaluate to true, in a value context the return value
3846 // needs to be a JSObject.
3847 if (context->IsTest()) {
3848 TestContext* test = TestContext::cast(context);
3849 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003850 current_block()->Goto(test->if_true(), function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003851 } else if (context->IsEffect()) {
3852 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003853 current_block()->Goto(function_return(), function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003854 } else {
3855 ASSERT(context->IsValue());
3856 CHECK_ALIVE(VisitForValue(stmt->expression()));
3857 HValue* return_value = Pop();
3858 HValue* receiver = environment()->Lookup(0);
3859 HHasInstanceTypeAndBranch* typecheck =
3860 new(zone()) HHasInstanceTypeAndBranch(return_value,
3861 FIRST_SPEC_OBJECT_TYPE,
3862 LAST_SPEC_OBJECT_TYPE);
3863 HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
3864 HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
3865 typecheck->SetSuccessorAt(0, if_spec_object);
3866 typecheck->SetSuccessorAt(1, not_spec_object);
3867 current_block()->Finish(typecheck);
3868 if_spec_object->AddLeaveInlined(return_value,
3869 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003870 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003871 not_spec_object->AddLeaveInlined(receiver,
3872 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003873 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00003874 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003875 } else {
3876 // Return from an inlined function, visit the subexpression in the
3877 // expression context of the call.
3878 if (context->IsTest()) {
3879 TestContext* test = TestContext::cast(context);
3880 VisitForControl(stmt->expression(),
3881 test->if_true(),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003882 test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003883 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003884 CHECK_ALIVE(VisitForEffect(stmt->expression()));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003885 current_block()->Goto(function_return(), function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003886 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003887 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003888 CHECK_ALIVE(VisitForValue(stmt->expression()));
ulan@chromium.org967e2702012-02-28 09:49:15 +00003889 HValue* return_value = Pop();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003890 current_block()->AddLeaveInlined(return_value,
3891 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003892 function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003893 }
3894 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00003895 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003896}
3897
3898
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003899void HGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003900 ASSERT(!HasStackOverflow());
3901 ASSERT(current_block() != NULL);
3902 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003903 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003904}
3905
3906
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003907void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003908 ASSERT(!HasStackOverflow());
3909 ASSERT(current_block() != NULL);
3910 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003911 // We only optimize switch statements with smi-literal smi comparisons,
3912 // with a bounded number of clauses.
3913 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003914 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003915 int clause_count = clauses->length();
3916 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003917 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003918 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003919
erikcorry0ad885c2011-11-21 13:51:57 +00003920 HValue* context = environment()->LookupContext();
3921
danno@chromium.org160a7b02011-04-18 15:51:38 +00003922 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003923 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003924 HValue* tag_value = Pop();
3925 HBasicBlock* first_test_block = current_block();
3926
erikcorry0ad885c2011-11-21 13:51:57 +00003927 SwitchType switch_type = UNKNOWN_SWITCH;
3928
3929 // 1. Extract clause type
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003930 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003931 CaseClause* clause = clauses->at(i);
3932 if (clause->is_default()) continue;
erikcorry0ad885c2011-11-21 13:51:57 +00003933
3934 if (switch_type == UNKNOWN_SWITCH) {
3935 if (clause->label()->IsSmiLiteral()) {
3936 switch_type = SMI_SWITCH;
3937 } else if (clause->label()->IsStringLiteral()) {
3938 switch_type = STRING_SWITCH;
3939 } else {
3940 return Bailout("SwitchStatement: non-literal switch label");
3941 }
3942 } else if ((switch_type == STRING_SWITCH &&
3943 !clause->label()->IsStringLiteral()) ||
3944 (switch_type == SMI_SWITCH &&
3945 !clause->label()->IsSmiLiteral())) {
3946 return Bailout("SwitchStatemnt: mixed label types are not supported");
3947 }
3948 }
3949
3950 HUnaryControlInstruction* string_check = NULL;
3951 HBasicBlock* not_string_block = NULL;
3952
3953 // Test switch's tag value if all clauses are string literals
3954 if (switch_type == STRING_SWITCH) {
3955 string_check = new(zone()) HIsStringAndBranch(tag_value);
3956 first_test_block = graph()->CreateBasicBlock();
3957 not_string_block = graph()->CreateBasicBlock();
3958
3959 string_check->SetSuccessorAt(0, first_test_block);
3960 string_check->SetSuccessorAt(1, not_string_block);
3961 current_block()->Finish(string_check);
3962
3963 set_current_block(first_test_block);
3964 }
3965
3966 // 2. Build all the tests, with dangling true branches
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003967 int default_id = AstNode::kNoNumber;
erikcorry0ad885c2011-11-21 13:51:57 +00003968 for (int i = 0; i < clause_count; ++i) {
3969 CaseClause* clause = clauses->at(i);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003970 if (clause->is_default()) {
3971 default_id = clause->EntryId();
3972 continue;
3973 }
erikcorry0ad885c2011-11-21 13:51:57 +00003974 if (switch_type == SMI_SWITCH) {
3975 clause->RecordTypeFeedback(oracle());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003976 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003977
erikcorry0ad885c2011-11-21 13:51:57 +00003978 // Generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003979 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003980 HValue* label_value = Pop();
erikcorry0ad885c2011-11-21 13:51:57 +00003981
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003982 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
erikcorry0ad885c2011-11-21 13:51:57 +00003983 HBasicBlock* body_block = graph()->CreateBasicBlock();
3984
3985 HControlInstruction* compare;
3986
3987 if (switch_type == SMI_SWITCH) {
3988 if (!clause->IsSmiCompare()) {
3989 // Finish with deoptimize and add uses of enviroment values to
3990 // account for invisible uses.
3991 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
3992 set_current_block(NULL);
3993 break;
3994 }
3995
3996 HCompareIDAndBranch* compare_ =
3997 new(zone()) HCompareIDAndBranch(tag_value,
3998 label_value,
3999 Token::EQ_STRICT);
4000 compare_->SetInputRepresentation(Representation::Integer32());
4001 compare = compare_;
4002 } else {
4003 compare = new(zone()) HStringCompareAndBranch(context, tag_value,
4004 label_value,
4005 Token::EQ_STRICT);
4006 }
4007
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004008 compare->SetSuccessorAt(0, body_block);
4009 compare->SetSuccessorAt(1, next_test_block);
4010 current_block()->Finish(compare);
erikcorry0ad885c2011-11-21 13:51:57 +00004011
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004012 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004013 }
4014
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004015 // Save the current block to use for the default or to join with the
4016 // exit. This block is NULL if we deoptimized.
4017 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004018
erikcorry0ad885c2011-11-21 13:51:57 +00004019 if (not_string_block != NULL) {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004020 int join_id = (default_id != AstNode::kNoNumber)
4021 ? default_id
4022 : stmt->ExitId();
4023 last_block = CreateJoin(last_block, not_string_block, join_id);
erikcorry0ad885c2011-11-21 13:51:57 +00004024 }
4025
4026 // 3. Loop over the clauses and the linked list of tests in lockstep,
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004027 // translating the clause bodies.
4028 HBasicBlock* curr_test_block = first_test_block;
4029 HBasicBlock* fall_through_block = NULL;
erikcorry0ad885c2011-11-21 13:51:57 +00004030
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004031 BreakAndContinueInfo break_info(stmt);
4032 { BreakAndContinueScope push(&break_info, this);
4033 for (int i = 0; i < clause_count; ++i) {
4034 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004035
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004036 // Identify the block where normal (non-fall-through) control flow
4037 // goes to.
4038 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00004039 if (clause->is_default()) {
4040 if (last_block != NULL) {
4041 normal_block = last_block;
4042 last_block = NULL; // Cleared to indicate we've handled it.
4043 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004044 } else if (!curr_test_block->end()->IsDeoptimize()) {
4045 normal_block = curr_test_block->end()->FirstSuccessor();
4046 curr_test_block = curr_test_block->end()->SecondSuccessor();
4047 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004048
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004049 // Identify a block to emit the body into.
4050 if (normal_block == NULL) {
4051 if (fall_through_block == NULL) {
4052 // (a) Unreachable.
4053 if (clause->is_default()) {
4054 continue; // Might still be reachable clause bodies.
4055 } else {
4056 break;
4057 }
4058 } else {
4059 // (b) Reachable only as fall through.
4060 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004061 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004062 } else if (fall_through_block == NULL) {
4063 // (c) Reachable only normally.
4064 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004065 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004066 // (d) Reachable both ways.
4067 HBasicBlock* join = CreateJoin(fall_through_block,
4068 normal_block,
4069 clause->EntryId());
4070 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004071 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004072
danno@chromium.org160a7b02011-04-18 15:51:38 +00004073 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004074 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004075 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004076 }
4077
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004078 // Create an up-to-3-way join. Use the break block if it exists since
4079 // it's already a join block.
4080 HBasicBlock* break_block = break_info.break_block();
4081 if (break_block == NULL) {
4082 set_current_block(CreateJoin(fall_through_block,
4083 last_block,
4084 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004085 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004086 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
4087 if (last_block != NULL) last_block->Goto(break_block);
4088 break_block->SetJoinId(stmt->ExitId());
4089 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004090 }
4091}
4092
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004093
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004094bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004095 return statement->OsrEntryId() == info()->osr_ast_id();
4096}
4097
4098
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004099bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
4100 if (!HasOsrEntryAt(statement)) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004101
4102 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
4103 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
4104 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004105 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004106 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004107
4108 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
4109 non_osr_entry->Goto(loop_predecessor);
4110
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004111 set_current_block(osr_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004112 int osr_entry_id = statement->OsrEntryId();
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004113 int first_expression_index = environment()->first_expression_index();
4114 int length = environment()->length();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004115 ZoneList<HUnknownOSRValue*>* osr_values =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004116 new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004117
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004118 for (int i = 0; i < first_expression_index; ++i) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004119 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
4120 AddInstruction(osr_value);
4121 environment()->Bind(i, osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004122 osr_values->Add(osr_value, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004123 }
4124
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004125 if (first_expression_index != length) {
4126 environment()->Drop(length - first_expression_index);
4127 for (int i = first_expression_index; i < length; ++i) {
4128 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
4129 AddInstruction(osr_value);
4130 environment()->Push(osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004131 osr_values->Add(osr_value, zone());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004132 }
4133 }
4134
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004135 graph()->set_osr_values(osr_values);
4136
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004137 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004138 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004139 HContext* context = new(zone()) HContext;
4140 AddInstruction(context);
4141 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004142 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004143 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004144 set_current_block(loop_predecessor);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004145 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004146}
4147
4148
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004149void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
ager@chromium.org04921a82011-06-27 13:21:41 +00004150 HBasicBlock* loop_entry,
4151 BreakAndContinueInfo* break_info) {
4152 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004153 AddSimulate(stmt->StackCheckId());
4154 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00004155 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004156 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00004157 AddInstruction(stack_check);
4158 ASSERT(loop_entry->IsLoopHeader());
4159 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004160 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00004161}
4162
4163
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004164void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004165 ASSERT(!HasStackOverflow());
4166 ASSERT(current_block() != NULL);
4167 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004168 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004169 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004170 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004171 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004172 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004173 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004174
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004175 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004176 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004177 HBasicBlock* body_exit =
4178 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004179 HBasicBlock* loop_successor = NULL;
4180 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004181 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004182 // The block for a true condition, the actual predecessor block of the
4183 // back edge.
4184 body_exit = graph()->CreateBasicBlock();
4185 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004186 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
4187 if (body_exit->HasPredecessor()) {
4188 body_exit->SetJoinId(stmt->BackEdgeId());
4189 } else {
4190 body_exit = NULL;
4191 }
4192 if (loop_successor->HasPredecessor()) {
4193 loop_successor->SetJoinId(stmt->ExitId());
4194 } else {
4195 loop_successor = NULL;
4196 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004197 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004198 HBasicBlock* loop_exit = CreateLoop(stmt,
4199 loop_entry,
4200 body_exit,
4201 loop_successor,
4202 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004203 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004204}
4205
4206
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004207void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004208 ASSERT(!HasStackOverflow());
4209 ASSERT(current_block() != NULL);
4210 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004211 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004212 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004213 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004214 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004215 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004216 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4217
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004218
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004219 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004220 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004221 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004222 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4223 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004224 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4225 if (body_entry->HasPredecessor()) {
4226 body_entry->SetJoinId(stmt->BodyId());
4227 set_current_block(body_entry);
4228 }
4229 if (loop_successor->HasPredecessor()) {
4230 loop_successor->SetJoinId(stmt->ExitId());
4231 } else {
4232 loop_successor = NULL;
4233 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004234 }
4235
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004236 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004237 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004238 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004239 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004240 HBasicBlock* body_exit =
4241 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004242 HBasicBlock* loop_exit = CreateLoop(stmt,
4243 loop_entry,
4244 body_exit,
4245 loop_successor,
4246 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004247 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004248}
4249
4250
4251void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004252 ASSERT(!HasStackOverflow());
4253 ASSERT(current_block() != NULL);
4254 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004255 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004256 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004257 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004258 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004259 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004260 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00004261 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004262 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004263 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004264
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004265 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004266 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004267 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4268 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004269 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4270 if (body_entry->HasPredecessor()) {
4271 body_entry->SetJoinId(stmt->BodyId());
4272 set_current_block(body_entry);
4273 }
4274 if (loop_successor->HasPredecessor()) {
4275 loop_successor->SetJoinId(stmt->ExitId());
4276 } else {
4277 loop_successor = NULL;
4278 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004279 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004280
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004281 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004282 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004283 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004284 }
4285 HBasicBlock* body_exit =
4286 JoinContinue(stmt, current_block(), break_info.continue_block());
4287
4288 if (stmt->next() != NULL && body_exit != NULL) {
4289 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004290 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004291 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004292 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004293
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004294 HBasicBlock* loop_exit = CreateLoop(stmt,
4295 loop_entry,
4296 body_exit,
4297 loop_successor,
4298 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004299 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004300}
4301
4302
4303void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004304 ASSERT(!HasStackOverflow());
4305 ASSERT(current_block() != NULL);
4306 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004307
yangguo@chromium.orga7d3df92012-02-27 11:46:55 +00004308 if (!FLAG_optimize_for_in) {
4309 return Bailout("ForInStatement optimization is disabled");
4310 }
4311
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004312 if (!oracle()->IsForInFastCase(stmt)) {
4313 return Bailout("ForInStatement is not fast case");
4314 }
4315
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004316 if (!stmt->each()->IsVariableProxy() ||
4317 !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
4318 return Bailout("ForInStatement with non-local each variable");
4319 }
4320
4321 Variable* each_var = stmt->each()->AsVariableProxy()->var();
4322
4323 CHECK_ALIVE(VisitForValue(stmt->enumerable()));
4324 HValue* enumerable = Top(); // Leave enumerable at the top.
4325
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004326 HInstruction* map = AddInstruction(new(zone()) HForInPrepareMap(
ulan@chromium.org967e2702012-02-28 09:49:15 +00004327 environment()->LookupContext(), enumerable));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004328 AddSimulate(stmt->PrepareId());
4329
4330 HInstruction* array = AddInstruction(
4331 new(zone()) HForInCacheArray(
4332 enumerable,
4333 map,
4334 DescriptorArray::kEnumCacheBridgeCacheIndex));
4335
4336 HInstruction* array_length = AddInstruction(
4337 new(zone()) HFixedArrayBaseLength(array));
4338
4339 HInstruction* start_index = AddInstruction(new(zone()) HConstant(
4340 Handle<Object>(Smi::FromInt(0)), Representation::Integer32()));
4341
4342 Push(map);
4343 Push(array);
4344 Push(array_length);
4345 Push(start_index);
4346
4347 HInstruction* index_cache = AddInstruction(
4348 new(zone()) HForInCacheArray(
4349 enumerable,
4350 map,
4351 DescriptorArray::kEnumCacheBridgeIndicesCacheIndex));
4352 HForInCacheArray::cast(array)->set_index_cache(
4353 HForInCacheArray::cast(index_cache));
4354
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004355 bool osr_entry = PreProcessOsrEntry(stmt);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004356 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4357 current_block()->Goto(loop_entry);
4358 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004359 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004360
4361 HValue* index = environment()->ExpressionStackAt(0);
4362 HValue* limit = environment()->ExpressionStackAt(1);
4363
4364 // Check that we still have more keys.
4365 HCompareIDAndBranch* compare_index =
4366 new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
4367 compare_index->SetInputRepresentation(Representation::Integer32());
4368
4369 HBasicBlock* loop_body = graph()->CreateBasicBlock();
4370 HBasicBlock* loop_successor = graph()->CreateBasicBlock();
4371
4372 compare_index->SetSuccessorAt(0, loop_body);
4373 compare_index->SetSuccessorAt(1, loop_successor);
4374 current_block()->Finish(compare_index);
4375
4376 set_current_block(loop_successor);
4377 Drop(5);
4378
4379 set_current_block(loop_body);
4380
4381 HValue* key = AddInstruction(
4382 new(zone()) HLoadKeyedFastElement(
4383 environment()->ExpressionStackAt(2), // Enum cache.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004384 environment()->ExpressionStackAt(0))); // Iteration index.
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004385
4386 // Check if the expected map still matches that of the enumerable.
4387 // If not just deoptimize.
4388 AddInstruction(new(zone()) HCheckMapValue(
4389 environment()->ExpressionStackAt(4),
4390 environment()->ExpressionStackAt(3)));
4391
4392 Bind(each_var, key);
4393
4394 BreakAndContinueInfo break_info(stmt, 5);
4395 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
4396
4397 HBasicBlock* body_exit =
4398 JoinContinue(stmt, current_block(), break_info.continue_block());
4399
4400 if (body_exit != NULL) {
4401 set_current_block(body_exit);
4402
4403 HValue* current_index = Pop();
ulan@chromium.org967e2702012-02-28 09:49:15 +00004404 HInstruction* new_index = new(zone()) HAdd(environment()->LookupContext(),
4405 current_index,
4406 graph()->GetConstant1());
4407 new_index->AssumeRepresentation(Representation::Integer32());
4408 PushAndAdd(new_index);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004409 body_exit = current_block();
4410 }
4411
4412 HBasicBlock* loop_exit = CreateLoop(stmt,
4413 loop_entry,
4414 body_exit,
4415 loop_successor,
4416 break_info.break_block());
4417
4418 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004419}
4420
4421
4422void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004423 ASSERT(!HasStackOverflow());
4424 ASSERT(current_block() != NULL);
4425 ASSERT(current_block()->HasPredecessor());
4426 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004427}
4428
4429
4430void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004431 ASSERT(!HasStackOverflow());
4432 ASSERT(current_block() != NULL);
4433 ASSERT(current_block()->HasPredecessor());
4434 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004435}
4436
4437
4438void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004439 ASSERT(!HasStackOverflow());
4440 ASSERT(current_block() != NULL);
4441 ASSERT(current_block()->HasPredecessor());
4442 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004443}
4444
4445
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004446static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
4447 Code* unoptimized_code, FunctionLiteral* expr) {
4448 int start_position = expr->start_position();
4449 RelocIterator it(unoptimized_code);
4450 for (;!it.done(); it.next()) {
4451 RelocInfo* rinfo = it.rinfo();
4452 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
4453 Object* obj = rinfo->target_object();
4454 if (obj->IsSharedFunctionInfo()) {
4455 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
4456 if (shared->start_position() == start_position) {
4457 return Handle<SharedFunctionInfo>(shared);
4458 }
4459 }
4460 }
4461
4462 return Handle<SharedFunctionInfo>();
4463}
4464
4465
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004466void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004467 ASSERT(!HasStackOverflow());
4468 ASSERT(current_block() != NULL);
4469 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004470 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004471 SearchSharedFunctionInfo(info()->shared_info()->code(),
4472 expr);
4473 if (shared_info.is_null()) {
4474 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
4475 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00004476 // We also have a stack overflow if the recursive compilation did.
4477 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004478 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004479 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004480 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004481 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004482}
4483
4484
4485void HGraphBuilder::VisitSharedFunctionInfoLiteral(
4486 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004487 ASSERT(!HasStackOverflow());
4488 ASSERT(current_block() != NULL);
4489 ASSERT(current_block()->HasPredecessor());
4490 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004491}
4492
4493
4494void HGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004495 ASSERT(!HasStackOverflow());
4496 ASSERT(current_block() != NULL);
4497 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004498 HBasicBlock* cond_true = graph()->CreateBasicBlock();
4499 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004500 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004501
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004502 // Visit the true and false subexpressions in the same AST context as the
4503 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004504 if (cond_true->HasPredecessor()) {
4505 cond_true->SetJoinId(expr->ThenId());
4506 set_current_block(cond_true);
4507 CHECK_BAILOUT(Visit(expr->then_expression()));
4508 cond_true = current_block();
4509 } else {
4510 cond_true = NULL;
4511 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004512
danno@chromium.org160a7b02011-04-18 15:51:38 +00004513 if (cond_false->HasPredecessor()) {
4514 cond_false->SetJoinId(expr->ElseId());
4515 set_current_block(cond_false);
4516 CHECK_BAILOUT(Visit(expr->else_expression()));
4517 cond_false = current_block();
4518 } else {
4519 cond_false = NULL;
4520 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004521
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004522 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004523 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004524 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004525 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004526 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004527 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004528 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004529}
4530
4531
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004532HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
4533 Variable* var, LookupResult* lookup, bool is_store) {
4534 if (var->is_this() || !info()->has_global_object()) {
4535 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004536 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004537 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004538 global->Lookup(*var->name(), lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004539 if (!lookup->IsNormal() ||
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004540 (is_store && lookup->IsReadOnly()) ||
4541 lookup->holder() != *global) {
4542 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004543 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004544
4545 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004546}
4547
4548
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004549HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
4550 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004551 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004552 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004553 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004554 HInstruction* context_instruction = new(zone()) HOuterContext(context);
4555 AddInstruction(context_instruction);
4556 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004557 }
4558 return context;
4559}
4560
4561
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004562void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004563 ASSERT(!HasStackOverflow());
4564 ASSERT(current_block() != NULL);
4565 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004566 Variable* variable = expr->var();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004567 switch (variable->location()) {
4568 case Variable::UNALLOCATED: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004569 if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
4570 return Bailout("reference to global harmony declared variable");
4571 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004572 // Handle known global constants like 'undefined' specially to avoid a
4573 // load from a global cell for them.
4574 Handle<Object> constant_value =
4575 isolate()->factory()->GlobalConstantFor(variable->name());
4576 if (!constant_value.is_null()) {
4577 HConstant* instr =
4578 new(zone()) HConstant(constant_value, Representation::Tagged());
4579 return ast_context()->ReturnInstruction(instr, expr->id());
4580 }
4581
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004582 LookupResult lookup(isolate());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004583 GlobalPropertyAccess type =
4584 LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004585
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004586 if (type == kUseCell &&
4587 info()->global_object()->IsAccessCheckNeeded()) {
4588 type = kUseGeneric;
4589 }
4590
4591 if (type == kUseCell) {
4592 Handle<GlobalObject> global(info()->global_object());
4593 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004594 HLoadGlobalCell* instr =
4595 new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004596 return ast_context()->ReturnInstruction(instr, expr->id());
4597 } else {
4598 HValue* context = environment()->LookupContext();
4599 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
4600 AddInstruction(global_object);
4601 HLoadGlobalGeneric* instr =
4602 new(zone()) HLoadGlobalGeneric(context,
4603 global_object,
4604 variable->name(),
4605 ast_context()->is_for_typeof());
4606 instr->set_position(expr->position());
4607 return ast_context()->ReturnInstruction(instr, expr->id());
4608 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004609 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004610
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004611 case Variable::PARAMETER:
4612 case Variable::LOCAL: {
4613 HValue* value = environment()->Lookup(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004614 if (value == graph()->GetConstantHole()) {
4615 ASSERT(variable->mode() == CONST ||
4616 variable->mode() == CONST_HARMONY ||
4617 variable->mode() == LET);
4618 return Bailout("reference to uninitialized variable");
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004619 }
4620 return ast_context()->ReturnValue(value);
4621 }
4622
4623 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004624 HValue* context = BuildContextChainWalk(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004625 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004626 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004627 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004628
4629 case Variable::LOOKUP:
4630 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004631 }
4632}
4633
4634
4635void HGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004636 ASSERT(!HasStackOverflow());
4637 ASSERT(current_block() != NULL);
4638 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004639 HConstant* instr =
4640 new(zone()) HConstant(expr->handle(), Representation::Tagged());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004641 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004642}
4643
4644
4645void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004646 ASSERT(!HasStackOverflow());
4647 ASSERT(current_block() != NULL);
4648 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00004649 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
4650 Handle<FixedArray> literals(closure->literals());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004651 HValue* context = environment()->LookupContext();
4652
4653 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00004654 literals,
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004655 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004656 expr->flags(),
4657 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004658 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004659}
4660
4661
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004662// Determines whether the given array or object literal boilerplate satisfies
4663// all limits to be considered for fast deep-copying and computes the total
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004664// size of all objects that are part of the graph.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004665static bool IsFastLiteral(Handle<JSObject> boilerplate,
4666 int max_depth,
4667 int* max_properties,
4668 int* total_size) {
4669 ASSERT(max_depth >= 0 && *max_properties >= 0);
4670 if (max_depth == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004671
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004672 Handle<FixedArrayBase> elements(boilerplate->elements());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004673 if (elements->length() > 0 &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004674 elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) {
danno@chromium.org88aa0582012-03-23 15:11:57 +00004675 if (boilerplate->HasFastDoubleElements()) {
4676 *total_size += FixedDoubleArray::SizeFor(elements->length());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004677 } else if (boilerplate->HasFastObjectElements()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004678 Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
danno@chromium.org88aa0582012-03-23 15:11:57 +00004679 int length = elements->length();
4680 for (int i = 0; i < length; i++) {
4681 if ((*max_properties)-- == 0) return false;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004682 Handle<Object> value(fast_elements->get(i));
danno@chromium.org88aa0582012-03-23 15:11:57 +00004683 if (value->IsJSObject()) {
4684 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
4685 if (!IsFastLiteral(value_object,
4686 max_depth - 1,
4687 max_properties,
4688 total_size)) {
4689 return false;
4690 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004691 }
4692 }
danno@chromium.org88aa0582012-03-23 15:11:57 +00004693 *total_size += FixedArray::SizeFor(length);
4694 } else {
4695 return false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004696 }
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004697 }
4698
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004699 Handle<FixedArray> properties(boilerplate->properties());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004700 if (properties->length() > 0) {
4701 return false;
4702 } else {
4703 int nof = boilerplate->map()->inobject_properties();
4704 for (int i = 0; i < nof; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004705 if ((*max_properties)-- == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004706 Handle<Object> value(boilerplate->InObjectPropertyAt(i));
4707 if (value->IsJSObject()) {
4708 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004709 if (!IsFastLiteral(value_object,
4710 max_depth - 1,
4711 max_properties,
4712 total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004713 return false;
4714 }
4715 }
4716 }
4717 }
4718
4719 *total_size += boilerplate->map()->instance_size();
4720 return true;
4721}
4722
4723
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004724void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004725 ASSERT(!HasStackOverflow());
4726 ASSERT(current_block() != NULL);
4727 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004728 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004729 HValue* context = environment()->LookupContext();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004730 HInstruction* literal;
4731
4732 // Check whether to use fast or slow deep-copying for boilerplate.
4733 int total_size = 0;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004734 int max_properties = HFastLiteral::kMaxLiteralProperties;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004735 Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()));
4736 if (boilerplate->IsJSObject() &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004737 IsFastLiteral(Handle<JSObject>::cast(boilerplate),
4738 HFastLiteral::kMaxLiteralDepth,
4739 &max_properties,
4740 &total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004741 Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004742 literal = new(zone()) HFastLiteral(context,
4743 boilerplate_object,
4744 total_size,
4745 expr->literal_index(),
4746 expr->depth());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004747 } else {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004748 literal = new(zone()) HObjectLiteral(context,
4749 expr->constant_properties(),
4750 expr->fast_elements(),
4751 expr->literal_index(),
4752 expr->depth(),
4753 expr->has_function());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004754 }
4755
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004756 // The object is expected in the bailout environment during computation
4757 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004758 PushAndAdd(literal);
4759
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004760 expr->CalculateEmitStore(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004761
4762 for (int i = 0; i < expr->properties()->length(); i++) {
4763 ObjectLiteral::Property* property = expr->properties()->at(i);
4764 if (property->IsCompileTimeValue()) continue;
4765
4766 Literal* key = property->key();
4767 Expression* value = property->value();
4768
4769 switch (property->kind()) {
4770 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
4771 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
4772 // Fall through.
4773 case ObjectLiteral::Property::COMPUTED:
4774 if (key->handle()->IsSymbol()) {
4775 if (property->emit_store()) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00004776 property->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004777 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004778 HValue* value = Pop();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004779 HInstruction* store;
4780 CHECK_ALIVE(store = BuildStoreNamed(literal, value, property));
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004781 AddInstruction(store);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00004782 if (store->HasObservableSideEffects()) AddSimulate(key->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004783 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004784 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004785 }
4786 break;
4787 }
4788 // Fall through.
4789 case ObjectLiteral::Property::PROTOTYPE:
4790 case ObjectLiteral::Property::SETTER:
4791 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00004792 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004793 default: UNREACHABLE();
4794 }
4795 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004796
4797 if (expr->has_function()) {
4798 // Return the result of the transformation to fast properties
4799 // instead of the original since this operation changes the map
4800 // of the object. This makes sure that the original object won't
4801 // be used by other optimized code before it is transformed
4802 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004803 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004804 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004805 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004806 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004807 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004808 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004809}
4810
4811
4812void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004813 ASSERT(!HasStackOverflow());
4814 ASSERT(current_block() != NULL);
4815 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004816 ZoneList<Expression*>* subexprs = expr->values();
4817 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004818 HValue* context = environment()->LookupContext();
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004819 HInstruction* literal;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004820
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004821 Handle<FixedArray> literals(environment()->closure()->literals());
4822 Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
4823
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004824 if (raw_boilerplate->IsUndefined()) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004825 raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
4826 isolate(), literals, expr->constant_elements());
4827 if (raw_boilerplate.is_null()) {
4828 return Bailout("array boilerplate creation failed");
4829 }
4830 literals->set(expr->literal_index(), *raw_boilerplate);
4831 if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
4832 isolate()->heap()->fixed_cow_array_map()) {
4833 isolate()->counters()->cow_arrays_created_runtime()->Increment();
4834 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004835 }
4836
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004837 Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
4838 ElementsKind boilerplate_elements_kind =
4839 Handle<JSObject>::cast(boilerplate)->GetElementsKind();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004840
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00004841 // Check whether to use fast or slow deep-copying for boilerplate.
4842 int total_size = 0;
4843 int max_properties = HFastLiteral::kMaxLiteralProperties;
4844 if (IsFastLiteral(boilerplate,
4845 HFastLiteral::kMaxLiteralDepth,
4846 &max_properties,
4847 &total_size)) {
4848 literal = new(zone()) HFastLiteral(context,
4849 boilerplate,
4850 total_size,
4851 expr->literal_index(),
4852 expr->depth());
4853 } else {
4854 literal = new(zone()) HArrayLiteral(context,
4855 boilerplate,
4856 length,
4857 expr->literal_index(),
4858 expr->depth());
4859 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004860
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004861 // The array is expected in the bailout environment during computation
4862 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004863 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004864
4865 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004866
4867 for (int i = 0; i < length; i++) {
4868 Expression* subexpr = subexprs->at(i);
4869 // If the subexpression is a literal or a simple materialized literal it
4870 // is already set in the cloned array.
4871 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
4872
danno@chromium.org160a7b02011-04-18 15:51:38 +00004873 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004874 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004875 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004876
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004877 elements = new(zone()) HLoadElements(literal);
4878 AddInstruction(elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004879
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004880 HValue* key = AddInstruction(
4881 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
4882 Representation::Integer32()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004883
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004884 switch (boilerplate_elements_kind) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004885 case FAST_SMI_ELEMENTS:
4886 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00004887 // Smi-only arrays need a smi check.
4888 AddInstruction(new(zone()) HCheckSmi(value));
4889 // Fall through.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004890 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004891 case FAST_HOLEY_ELEMENTS:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004892 AddInstruction(new(zone()) HStoreKeyedFastElement(
4893 elements,
4894 key,
4895 value,
4896 boilerplate_elements_kind));
4897 break;
4898 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00004899 case FAST_HOLEY_DOUBLE_ELEMENTS:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004900 AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements,
4901 key,
4902 value));
4903 break;
4904 default:
4905 UNREACHABLE();
4906 break;
4907 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004908
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004909 AddSimulate(expr->GetIdForElement(i));
4910 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004911 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004912}
4913
4914
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004915// Sets the lookup result and returns true if the load/store can be inlined.
4916static bool ComputeLoadStoreField(Handle<Map> type,
4917 Handle<String> name,
4918 LookupResult* lookup,
4919 bool is_store) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004920 type->LookupInDescriptors(NULL, *name, lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004921 if (lookup->IsField()) return true;
4922 return is_store && lookup->IsMapTransition() &&
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004923 (type->unused_property_fields() > 0);
4924}
4925
4926
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004927static int ComputeLoadStoreFieldIndex(Handle<Map> type,
4928 Handle<String> name,
4929 LookupResult* lookup) {
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00004930 ASSERT(lookup->IsField() || lookup->type() == MAP_TRANSITION);
4931 if (lookup->IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004932 return lookup->GetLocalFieldIndexFromMap(*type);
4933 } else {
4934 Map* transition = lookup->GetTransitionMapFromMap(*type);
4935 return transition->PropertyIndexFor(*name) - type->inobject_properties();
4936 }
4937}
4938
4939
4940HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
4941 Handle<String> name,
4942 HValue* value,
4943 Handle<Map> type,
4944 LookupResult* lookup,
4945 bool smi_and_map_check) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004946 ASSERT(lookup->IsFound());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004947 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004948 AddInstruction(new(zone()) HCheckNonSmi(object));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004949 AddInstruction(HCheckMaps::NewWithTransitions(object, type, zone()));
4950 }
4951
4952 // If the property does not exist yet, we have to check that it wasn't made
4953 // readonly or turned into a setter by some meanwhile modifications on the
4954 // prototype chain.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00004955 if (!lookup->IsProperty() && type->prototype()->IsJSReceiver()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004956 Object* proto = type->prototype();
4957 // First check that the prototype chain isn't affected already.
4958 LookupResult proto_result(isolate());
4959 proto->Lookup(*name, &proto_result);
4960 if (proto_result.IsProperty()) {
4961 // If the inherited property could induce readonly-ness, bail out.
4962 if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
4963 Bailout("improper object on prototype chain for store");
4964 return NULL;
4965 }
4966 // We only need to check up to the preexisting property.
4967 proto = proto_result.holder();
4968 } else {
4969 // Otherwise, find the top prototype.
4970 while (proto->GetPrototype()->IsJSObject()) proto = proto->GetPrototype();
4971 ASSERT(proto->GetPrototype()->IsNull());
4972 }
4973 ASSERT(proto->IsJSObject());
4974 AddInstruction(new(zone()) HCheckPrototypeMaps(
4975 Handle<JSObject>(JSObject::cast(type->prototype())),
4976 Handle<JSObject>(JSObject::cast(proto))));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004977 }
4978
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00004979 int index = ComputeLoadStoreFieldIndex(type, name, lookup);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004980 bool is_in_object = index < 0;
4981 int offset = index * kPointerSize;
4982 if (index < 0) {
4983 // Negative property indices are in-object properties, indexed
4984 // from the end of the fixed part of the object.
4985 offset += type->instance_size();
4986 } else {
4987 offset += FixedArray::kHeaderSize;
4988 }
4989 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004990 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004991 if (lookup->type() == MAP_TRANSITION) {
4992 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
4993 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00004994 // TODO(fschneider): Record the new map type of the object in the IR to
4995 // enable elimination of redundant checks after the transition store.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00004996 instr->SetGVNFlag(kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004997 }
4998 return instr;
4999}
5000
5001
5002HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
5003 Handle<String> name,
5004 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005005 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005006 return new(zone()) HStoreNamedGeneric(
5007 context,
5008 object,
5009 name,
5010 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005011 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005012}
5013
5014
5015HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
5016 HValue* value,
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005017 ObjectLiteral::Property* prop) {
5018 Literal* key = prop->key()->AsLiteral();
5019 Handle<String> name = Handle<String>::cast(key->handle());
5020 ASSERT(!name.is_null());
5021
5022 LookupResult lookup(isolate());
5023 Handle<Map> type = prop->GetReceiverType();
5024 bool is_monomorphic = prop->IsMonomorphic() &&
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005025 ComputeLoadStoreField(type, name, &lookup, true);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005026
5027 return is_monomorphic
5028 ? BuildStoreNamedField(object, name, value, type, &lookup,
5029 true) // Needs smi and map check.
5030 : BuildStoreNamedGeneric(object, name, value);
5031}
5032
5033
5034HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
5035 HValue* value,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005036 Expression* expr) {
5037 Property* prop = (expr->AsProperty() != NULL)
5038 ? expr->AsProperty()
5039 : expr->AsAssignment()->target()->AsProperty();
5040 Literal* key = prop->key()->AsLiteral();
5041 Handle<String> name = Handle<String>::cast(key->handle());
5042 ASSERT(!name.is_null());
5043
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005044 LookupResult lookup(isolate());
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005045 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005046 bool is_monomorphic = expr->IsMonomorphic() &&
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005047 ComputeLoadStoreField(types->first(), name, &lookup, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005048
5049 return is_monomorphic
5050 ? BuildStoreNamedField(object, name, value, types->first(), &lookup,
5051 true) // Needs smi and map check.
5052 : BuildStoreNamedGeneric(object, name, value);
5053}
5054
5055
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005056void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
5057 HValue* object,
5058 SmallMapList* types,
5059 Handle<String> name) {
5060 int count = 0;
5061 int previous_field_offset = 0;
5062 bool previous_field_is_in_object = false;
5063 bool is_monomorphic_field = true;
5064 Handle<Map> map;
5065 LookupResult lookup(isolate());
5066 for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
5067 map = types->at(i);
5068 if (ComputeLoadStoreField(map, name, &lookup, false)) {
5069 int index = ComputeLoadStoreFieldIndex(map, name, &lookup);
5070 bool is_in_object = index < 0;
5071 int offset = index * kPointerSize;
5072 if (index < 0) {
5073 // Negative property indices are in-object properties, indexed
5074 // from the end of the fixed part of the object.
5075 offset += map->instance_size();
5076 } else {
5077 offset += FixedArray::kHeaderSize;
5078 }
5079 if (count == 0) {
5080 previous_field_offset = offset;
5081 previous_field_is_in_object = is_in_object;
5082 } else if (is_monomorphic_field) {
5083 is_monomorphic_field = (offset == previous_field_offset) &&
5084 (is_in_object == previous_field_is_in_object);
5085 }
5086 ++count;
5087 }
5088 }
5089
5090 // Use monomorphic load if property lookup results in the same field index
5091 // for all maps. Requires special map check on the set of all handled maps.
5092 HInstruction* instr;
5093 if (count == types->length() && is_monomorphic_field) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005094 AddInstruction(new(zone()) HCheckMaps(object, types, zone()));
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005095 instr = BuildLoadNamedField(object, expr, map, &lookup, false);
5096 } else {
5097 HValue* context = environment()->LookupContext();
5098 instr = new(zone()) HLoadNamedFieldPolymorphic(context,
5099 object,
5100 types,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005101 name,
5102 zone());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005103 }
5104
5105 instr->set_position(expr->position());
5106 return ast_context()->ReturnInstruction(instr, expr->id());
5107}
5108
5109
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005110void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
5111 HValue* object,
5112 HValue* value,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005113 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005114 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005115 // TODO(ager): We should recognize when the prototype chains for different
5116 // maps are identical. In that case we can avoid repeatedly generating the
5117 // same prototype map checks.
5118 int count = 0;
5119 HBasicBlock* join = NULL;
5120 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005121 Handle<Map> map = types->at(i);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005122 LookupResult lookup(isolate());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005123 if (ComputeLoadStoreField(map, name, &lookup, true)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005124 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005125 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005126 join = graph()->CreateBasicBlock();
5127 }
5128 ++count;
5129 HBasicBlock* if_true = graph()->CreateBasicBlock();
5130 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005131 HCompareMap* compare =
5132 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005133 current_block()->Finish(compare);
5134
5135 set_current_block(if_true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005136 HInstruction* instr;
5137 CHECK_ALIVE(instr =
5138 BuildStoreNamedField(object, name, value, map, &lookup, false));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005139 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005140 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005141 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005142 if (!ast_context()->IsEffect()) Push(value);
5143 current_block()->Goto(join);
5144
5145 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005146 }
5147 }
5148
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005149 // Finish up. Unconditionally deoptimize if we've handled all the maps we
5150 // know about and do not want to handle ones we've never seen. Otherwise
5151 // use a generic IC.
5152 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005153 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005154 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005155 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005156 instr->set_position(expr->position());
5157 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005158
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005159 if (join != NULL) {
5160 if (!ast_context()->IsEffect()) Push(value);
5161 current_block()->Goto(join);
5162 } else {
5163 // The HSimulate for the store should not see the stored value in
5164 // effect contexts (it is not materialized at expr->id() in the
5165 // unoptimized code).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005166 if (instr->HasObservableSideEffects()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005167 if (ast_context()->IsEffect()) {
5168 AddSimulate(expr->id());
5169 } else {
5170 Push(value);
5171 AddSimulate(expr->id());
5172 Drop(1);
5173 }
5174 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005175 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00005176 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00005177 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005178
5179 ASSERT(join != NULL);
5180 join->SetJoinId(expr->id());
5181 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005182 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005183}
5184
5185
5186void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
5187 Property* prop = expr->target()->AsProperty();
5188 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005189 expr->RecordTypeFeedback(oracle(), zone());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005190 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005191
5192 HValue* value = NULL;
5193 HInstruction* instr = NULL;
5194
5195 if (prop->key()->IsPropertyName()) {
5196 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005197 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005198 value = Pop();
5199 HValue* object = Pop();
5200
5201 Literal* key = prop->key()->AsLiteral();
5202 Handle<String> name = Handle<String>::cast(key->handle());
5203 ASSERT(!name.is_null());
5204
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005205 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005206 if (expr->IsMonomorphic()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005207 CHECK_ALIVE(instr = BuildStoreNamed(object, value, expr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005208
5209 } else if (types != NULL && types->length() > 1) {
5210 HandlePolymorphicStoreNamedField(expr, object, value, types, name);
5211 return;
5212
5213 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005214 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005215 }
5216
5217 } else {
5218 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005219 CHECK_ALIVE(VisitForValue(prop->key()));
5220 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005221 value = Pop();
5222 HValue* key = Pop();
5223 HValue* object = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00005224 bool has_side_effects = false;
5225 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
5226 expr->position(),
5227 true, // is_store
5228 &has_side_effects);
5229 Push(value);
5230 ASSERT(has_side_effects); // Stores always have side effects.
5231 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005232 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005233 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005234 Push(value);
5235 instr->set_position(expr->position());
5236 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005237 if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005238 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005239}
5240
5241
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005242// Because not every expression has a position and there is not common
5243// superclass of Assignment and CountOperation, we cannot just pass the
5244// owning expression instead of position and ast_id separately.
5245void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005246 HValue* value,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005247 int position,
5248 int ast_id) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005249 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005250 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
5251 if (type == kUseCell) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005252 Handle<GlobalObject> global(info()->global_object());
5253 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005254 HInstruction* instr =
5255 new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005256 instr->set_position(position);
5257 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005258 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005259 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005260 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005261 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
5262 AddInstruction(global_object);
5263 HStoreGlobalGeneric* instr =
5264 new(zone()) HStoreGlobalGeneric(context,
5265 global_object,
5266 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005267 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005268 function_strict_mode_flag());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005269 instr->set_position(position);
5270 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005271 ASSERT(instr->HasObservableSideEffects());
5272 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005273 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005274}
5275
5276
5277void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
5278 Expression* target = expr->target();
5279 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005280 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005281 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005282
5283 // We have a second position recorded in the FullCodeGenerator to have
5284 // type feedback for the binary operation.
5285 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005286
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005287 if (proxy != NULL) {
5288 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005289 if (var->mode() == LET) {
5290 return Bailout("unsupported let compound assignment");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005291 }
5292
danno@chromium.org160a7b02011-04-18 15:51:38 +00005293 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005294
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005295 switch (var->location()) {
5296 case Variable::UNALLOCATED:
5297 HandleGlobalVariableAssignment(var,
5298 Top(),
5299 expr->position(),
5300 expr->AssignmentId());
5301 break;
5302
5303 case Variable::PARAMETER:
5304 case Variable::LOCAL:
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005305 if (var->mode() == CONST) {
5306 return Bailout("unsupported const compound assignment");
5307 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005308 Bind(var, Top());
5309 break;
5310
5311 case Variable::CONTEXT: {
5312 // Bail out if we try to mutate a parameter value in a function
5313 // using the arguments object. We do not (yet) correctly handle the
5314 // arguments property of the function.
5315 if (info()->scope()->arguments() != NULL) {
5316 // Parameters will be allocated to context slots. We have no
5317 // direct way to detect that the variable is a parameter so we do
5318 // a linear search of the parameter variables.
5319 int count = info()->scope()->num_parameters();
5320 for (int i = 0; i < count; ++i) {
5321 if (var == info()->scope()->parameter(i)) {
5322 Bailout(
5323 "assignment to parameter, function uses arguments object");
5324 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005325 }
5326 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005327
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005328 HStoreContextSlot::Mode mode;
5329
5330 switch (var->mode()) {
5331 case LET:
5332 mode = HStoreContextSlot::kCheckDeoptimize;
5333 break;
5334 case CONST:
5335 return ast_context()->ReturnValue(Pop());
5336 case CONST_HARMONY:
5337 // This case is checked statically so no need to
5338 // perform checks here
5339 UNREACHABLE();
5340 default:
5341 mode = HStoreContextSlot::kNoCheck;
5342 }
5343
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005344 HValue* context = BuildContextChainWalk(var);
5345 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005346 new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005347 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005348 if (instr->HasObservableSideEffects()) {
5349 AddSimulate(expr->AssignmentId());
5350 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005351 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005352 }
5353
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005354 case Variable::LOOKUP:
5355 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005356 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005357 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005358
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005359 } else if (prop != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005360 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005361
5362 if (prop->key()->IsPropertyName()) {
5363 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005364 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005365 HValue* obj = Top();
5366
5367 HInstruction* load = NULL;
5368 if (prop->IsMonomorphic()) {
5369 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
5370 Handle<Map> map = prop->GetReceiverTypes()->first();
5371 load = BuildLoadNamed(obj, prop, map, name);
5372 } else {
5373 load = BuildLoadNamedGeneric(obj, prop);
5374 }
5375 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005376 if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005377
danno@chromium.org160a7b02011-04-18 15:51:38 +00005378 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005379 HValue* right = Pop();
5380 HValue* left = Pop();
5381
5382 HInstruction* instr = BuildBinaryOperation(operation, left, right);
5383 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005384 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005385
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005386 HInstruction* store;
5387 CHECK_ALIVE(store = BuildStoreNamed(obj, instr, prop));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005388 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005389 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005390 Drop(2);
5391 Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005392 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005393 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005394
5395 } else {
5396 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005397 CHECK_ALIVE(VisitForValue(prop->obj()));
5398 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005399 HValue* obj = environment()->ExpressionStackAt(1);
5400 HValue* key = environment()->ExpressionStackAt(0);
5401
whesse@chromium.org7b260152011-06-20 15:33:18 +00005402 bool has_side_effects = false;
5403 HValue* load = HandleKeyedElementAccess(
5404 obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
5405 false, // is_store
5406 &has_side_effects);
5407 Push(load);
5408 if (has_side_effects) AddSimulate(expr->CompoundLoadId());
5409
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005410
danno@chromium.org160a7b02011-04-18 15:51:38 +00005411 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005412 HValue* right = Pop();
5413 HValue* left = Pop();
5414
5415 HInstruction* instr = BuildBinaryOperation(operation, left, right);
5416 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005417 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005418
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005419 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005420 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
5421 RelocInfo::kNoPosition,
5422 true, // is_store
5423 &has_side_effects);
5424
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005425 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005426 Drop(3);
5427 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005428 ASSERT(has_side_effects); // Stores always have side effects.
5429 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005430 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005431 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005432
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005433 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005434 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005435 }
5436}
5437
5438
5439void HGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005440 ASSERT(!HasStackOverflow());
5441 ASSERT(current_block() != NULL);
5442 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005443 VariableProxy* proxy = expr->target()->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005444 Property* prop = expr->target()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005445 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005446
5447 if (expr->is_compound()) {
5448 HandleCompoundAssignment(expr);
5449 return;
5450 }
5451
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005452 if (prop != NULL) {
5453 HandlePropertyAssignment(expr);
5454 } else if (proxy != NULL) {
5455 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005456
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00005457 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005458 if (expr->op() != Token::INIT_CONST) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005459 CHECK_ALIVE(VisitForValue(expr->value()));
5460 return ast_context()->ReturnValue(Pop());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005461 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005462
5463 if (var->IsStackAllocated()) {
5464 // We insert a use of the old value to detect unsupported uses of const
5465 // variables (e.g. initialization inside a loop).
5466 HValue* old_value = environment()->Lookup(var);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005467 AddInstruction(new(zone()) HUseConst(old_value));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005468 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005469 } else if (var->mode() == CONST_HARMONY) {
5470 if (expr->op() != Token::INIT_CONST_HARMONY) {
5471 return Bailout("non-initializer assignment to const");
5472 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005473 }
5474
danno@chromium.org160a7b02011-04-18 15:51:38 +00005475 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005476
5477 // Handle the assignment.
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005478 switch (var->location()) {
5479 case Variable::UNALLOCATED:
5480 CHECK_ALIVE(VisitForValue(expr->value()));
5481 HandleGlobalVariableAssignment(var,
5482 Top(),
5483 expr->position(),
5484 expr->AssignmentId());
5485 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005486
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005487 case Variable::PARAMETER:
5488 case Variable::LOCAL: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005489 // Perform an initialization check for let declared variables
5490 // or parameters.
5491 if (var->mode() == LET && expr->op() == Token::ASSIGN) {
5492 HValue* env_value = environment()->Lookup(var);
5493 if (env_value == graph()->GetConstantHole()) {
5494 return Bailout("assignment to let variable before initialization");
5495 }
5496 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005497 // We do not allow the arguments object to occur in a context where it
5498 // may escape, but assignments to stack-allocated locals are
5499 // permitted.
5500 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
5501 HValue* value = Pop();
5502 Bind(var, value);
5503 return ast_context()->ReturnValue(value);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005504 }
5505
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005506 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005507 // Bail out if we try to mutate a parameter value in a function using
5508 // the arguments object. We do not (yet) correctly handle the
5509 // arguments property of the function.
5510 if (info()->scope()->arguments() != NULL) {
5511 // Parameters will rewrite to context slots. We have no direct way
5512 // to detect that the variable is a parameter.
5513 int count = info()->scope()->num_parameters();
5514 for (int i = 0; i < count; ++i) {
5515 if (var == info()->scope()->parameter(i)) {
5516 return Bailout("assignment to parameter in arguments object");
5517 }
5518 }
5519 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005520
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005521 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005522 HStoreContextSlot::Mode mode;
5523 if (expr->op() == Token::ASSIGN) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005524 switch (var->mode()) {
5525 case LET:
5526 mode = HStoreContextSlot::kCheckDeoptimize;
5527 break;
5528 case CONST:
5529 return ast_context()->ReturnValue(Pop());
5530 case CONST_HARMONY:
5531 // This case is checked statically so no need to
5532 // perform checks here
5533 UNREACHABLE();
5534 default:
5535 mode = HStoreContextSlot::kNoCheck;
5536 }
5537 } else if (expr->op() == Token::INIT_VAR ||
5538 expr->op() == Token::INIT_LET ||
5539 expr->op() == Token::INIT_CONST_HARMONY) {
5540 mode = HStoreContextSlot::kNoCheck;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005541 } else {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005542 ASSERT(expr->op() == Token::INIT_CONST);
5543
5544 mode = HStoreContextSlot::kCheckIgnoreAssignment;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005545 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005546
5547 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005548 HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
5549 context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005550 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005551 if (instr->HasObservableSideEffects()) {
5552 AddSimulate(expr->AssignmentId());
5553 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005554 return ast_context()->ReturnValue(Pop());
5555 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005556
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005557 case Variable::LOOKUP:
5558 return Bailout("assignment to LOOKUP variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005559 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005560 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005561 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005562 }
5563}
5564
5565
5566void HGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005567 ASSERT(!HasStackOverflow());
5568 ASSERT(current_block() != NULL);
5569 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005570 // We don't optimize functions with invalid left-hand sides in
5571 // assignments, count operations, or for-in. Consequently throw can
5572 // currently only occur in an effect context.
5573 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005574 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005575
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005576 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005577 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005578 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005579 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00005580 AddInstruction(instr);
5581 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005582 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005583 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005584}
5585
5586
whesse@chromium.org023421e2010-12-21 12:19:12 +00005587HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
5588 Property* expr,
5589 Handle<Map> type,
5590 LookupResult* lookup,
5591 bool smi_and_map_check) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005592 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005593 AddInstruction(new(zone()) HCheckNonSmi(object));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005594 AddInstruction(HCheckMaps::NewWithTransitions(object, type, zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005595 }
5596
5597 int index = lookup->GetLocalFieldIndexFromMap(*type);
5598 if (index < 0) {
5599 // Negative property indices are in-object properties, indexed
5600 // from the end of the fixed part of the object.
5601 int offset = (index * kPointerSize) + type->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005602 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005603 } else {
5604 // Non-negative property indices are in the properties array.
5605 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005606 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005607 }
5608}
5609
5610
5611HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
5612 Property* expr) {
ulan@chromium.org6ff65142012-03-21 09:52:17 +00005613 if (expr->IsUninitialized() && !FLAG_always_opt) {
jkummerow@chromium.org531dfe82012-03-20 13:01:16 +00005614 AddInstruction(new(zone()) HSoftDeoptimize);
5615 current_block()->MarkAsDeoptimizing();
5616 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005617 ASSERT(expr->key()->IsPropertyName());
5618 Handle<Object> name = expr->key()->AsLiteral()->handle();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005619 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005620 return new(zone()) HLoadNamedGeneric(context, obj, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005621}
5622
5623
5624HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
5625 Property* expr,
5626 Handle<Map> map,
5627 Handle<String> name) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005628 LookupResult lookup(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005629 map->LookupInDescriptors(NULL, *name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005630 if (lookup.IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005631 return BuildLoadNamedField(obj,
5632 expr,
5633 map,
5634 &lookup,
5635 true);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005636 } else if (lookup.IsConstantFunction()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005637 AddInstruction(new(zone()) HCheckNonSmi(obj));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005638 AddInstruction(HCheckMaps::NewWithTransitions(obj, map, zone()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005639 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005640 return new(zone()) HConstant(function, Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005641 } else {
5642 return BuildLoadNamedGeneric(obj, expr);
5643 }
5644}
5645
5646
5647HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
5648 HValue* key) {
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()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005651}
5652
5653
whesse@chromium.org7b260152011-06-20 15:33:18 +00005654HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
5655 HValue* external_elements,
5656 HValue* checked_key,
5657 HValue* val,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005658 ElementsKind elements_kind,
whesse@chromium.org7b260152011-06-20 15:33:18 +00005659 bool is_store) {
5660 if (is_store) {
5661 ASSERT(val != NULL);
5662 switch (elements_kind) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005663 case EXTERNAL_PIXEL_ELEMENTS: {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005664 val = AddInstruction(new(zone()) HClampToUint8(val));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005665 break;
5666 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005667 case EXTERNAL_BYTE_ELEMENTS:
5668 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
5669 case EXTERNAL_SHORT_ELEMENTS:
5670 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
5671 case EXTERNAL_INT_ELEMENTS:
5672 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005673 if (!val->representation().IsInteger32()) {
5674 val = AddInstruction(new(zone()) HChange(
5675 val,
5676 Representation::Integer32(),
5677 true, // Truncate to int32.
5678 false)); // Don't deoptimize undefined (irrelevant here).
5679 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005680 break;
5681 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005682 case EXTERNAL_FLOAT_ELEMENTS:
5683 case EXTERNAL_DOUBLE_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00005684 break;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005685 case FAST_SMI_ELEMENTS:
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005686 case FAST_ELEMENTS:
5687 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005688 case FAST_HOLEY_SMI_ELEMENTS:
5689 case FAST_HOLEY_ELEMENTS:
5690 case FAST_HOLEY_DOUBLE_ELEMENTS:
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005691 case DICTIONARY_ELEMENTS:
5692 case NON_STRICT_ARGUMENTS_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00005693 UNREACHABLE();
5694 break;
5695 }
5696 return new(zone()) HStoreKeyedSpecializedArrayElement(
5697 external_elements, checked_key, val, elements_kind);
5698 } else {
fschneider@chromium.org35814e52012-03-01 15:43:35 +00005699 ASSERT(val == NULL);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005700 return new(zone()) HLoadKeyedSpecializedArrayElement(
5701 external_elements, checked_key, elements_kind);
5702 }
5703}
5704
5705
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005706HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
5707 HValue* checked_key,
5708 HValue* val,
5709 ElementsKind elements_kind,
5710 bool is_store) {
5711 if (is_store) {
5712 ASSERT(val != NULL);
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005713 switch (elements_kind) {
5714 case FAST_DOUBLE_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005715 case FAST_HOLEY_DOUBLE_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005716 return new(zone()) HStoreKeyedFastDoubleElement(
5717 elements, checked_key, val);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005718 case FAST_SMI_ELEMENTS:
5719 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005720 // Smi-only arrays need a smi check.
5721 AddInstruction(new(zone()) HCheckSmi(val));
5722 // Fall through.
5723 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005724 case FAST_HOLEY_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005725 return new(zone()) HStoreKeyedFastElement(
5726 elements, checked_key, val, elements_kind);
5727 default:
5728 UNREACHABLE();
5729 return NULL;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005730 }
5731 }
5732 // It's an element load (!is_store).
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005733 HoleCheckMode mode = IsFastPackedElementsKind(elements_kind) ?
5734 OMIT_HOLE_CHECK :
5735 PERFORM_HOLE_CHECK;
5736 if (IsFastDoubleElementsKind(elements_kind)) {
5737 return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key, mode);
5738 } else { // Smi or Object elements.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005739 return new(zone()) HLoadKeyedFastElement(elements, checked_key,
5740 elements_kind);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005741 }
5742}
5743
5744
whesse@chromium.org7b260152011-06-20 15:33:18 +00005745HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
5746 HValue* key,
5747 HValue* val,
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005748 HValue* dependency,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005749 Handle<Map> map,
whesse@chromium.org7b260152011-06-20 15:33:18 +00005750 bool is_store) {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005751 HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
5752 zone(), dependency);
5753 AddInstruction(mapcheck);
5754 if (dependency) {
5755 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
5756 }
5757 return BuildUncheckedMonomorphicElementAccess(object, key, val,
5758 mapcheck, map, is_store);
5759}
5760
5761
5762HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
5763 HValue* object,
5764 HValue* key,
5765 HValue* val,
5766 HCheckMaps* mapcheck,
5767 Handle<Map> map,
5768 bool is_store) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005769 // No GVNFlag is necessary for ElementsKind if there is an explicit dependency
5770 // on a HElementsTransition instruction. The flag can also be removed if the
5771 // map to check has FAST_HOLEY_ELEMENTS, since there can be no further
5772 // ElementsKind transitions. Finally, the dependency can be removed for stores
5773 // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
5774 // generated store code.
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005775 if ((map->elements_kind() == FAST_HOLEY_ELEMENTS) ||
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005776 (map->elements_kind() == FAST_ELEMENTS && is_store)) {
5777 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
5778 }
5779 bool fast_smi_only_elements = map->has_fast_smi_elements();
5780 bool fast_elements = map->has_fast_object_elements();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005781 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005782 if (is_store && (fast_elements || fast_smi_only_elements)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005783 HCheckMaps* check_cow_map = new(zone()) HCheckMaps(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005784 elements, isolate()->factory()->fixed_array_map(), zone());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005785 check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
5786 AddInstruction(check_cow_map);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005787 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005788 HInstruction* length = NULL;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005789 HInstruction* checked_key = NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005790 if (map->has_external_array_elements()) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005791 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005792 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
5793 HLoadExternalArrayPointer* external_elements =
5794 new(zone()) HLoadExternalArrayPointer(elements);
5795 AddInstruction(external_elements);
5796 return BuildExternalArrayElementAccess(external_elements, checked_key,
5797 val, map->elements_kind(), is_store);
5798 }
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005799 ASSERT(fast_smi_only_elements ||
5800 fast_elements ||
5801 map->has_fast_double_elements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005802 if (map->instance_type() == JS_ARRAY_TYPE) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005803 length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck,
5804 HType::Smi()));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005805 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005806 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00005807 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005808 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005809 return BuildFastElementAccess(elements, checked_key, val,
5810 map->elements_kind(), is_store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005811}
5812
5813
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005814HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
5815 HValue* object,
5816 HValue* key,
5817 HValue* val,
5818 SmallMapList* maps) {
5819 // For polymorphic loads of similar elements kinds (i.e. all tagged or all
5820 // double), always use the "worst case" code without a transition. This is
5821 // much faster than transitioning the elements to the worst case, trading a
5822 // HTransitionElements for a HCheckMaps, and avoiding mutation of the array.
5823 bool has_double_maps = false;
5824 bool has_smi_or_object_maps = false;
5825 bool has_js_array_access = false;
5826 bool has_non_js_array_access = false;
5827 Handle<Map> most_general_consolidated_map;
5828 for (int i = 0; i < maps->length(); ++i) {
5829 Handle<Map> map = maps->at(i);
5830 // Don't allow mixing of JSArrays with JSObjects.
5831 if (map->instance_type() == JS_ARRAY_TYPE) {
5832 if (has_non_js_array_access) return NULL;
5833 has_js_array_access = true;
5834 } else if (has_js_array_access) {
5835 return NULL;
5836 } else {
5837 has_non_js_array_access = true;
5838 }
5839 // Don't allow mixed, incompatible elements kinds.
5840 if (map->has_fast_double_elements()) {
5841 if (has_smi_or_object_maps) return NULL;
5842 has_double_maps = true;
5843 } else if (map->has_fast_smi_or_object_elements()) {
5844 if (has_double_maps) return NULL;
5845 has_smi_or_object_maps = true;
5846 } else {
5847 return NULL;
5848 }
5849 // Remember the most general elements kind, the code for its load will
5850 // properly handle all of the more specific cases.
5851 if ((i == 0) || IsMoreGeneralElementsKindTransition(
5852 most_general_consolidated_map->elements_kind(),
5853 map->elements_kind())) {
5854 most_general_consolidated_map = map;
5855 }
5856 }
5857 if (!has_double_maps && !has_smi_or_object_maps) return NULL;
5858
5859 HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
5860 AddInstruction(check_maps);
5861 HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
5862 object, key, val, check_maps, most_general_consolidated_map, false);
5863 return instr;
5864}
5865
5866
whesse@chromium.org7b260152011-06-20 15:33:18 +00005867HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
5868 HValue* key,
5869 HValue* val,
5870 Expression* prop,
5871 int ast_id,
5872 int position,
5873 bool is_store,
5874 bool* has_side_effects) {
5875 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005876 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005877 SmallMapList* maps = prop->GetReceiverTypes();
whesse@chromium.org7b260152011-06-20 15:33:18 +00005878 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005879
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00005880 if (!is_store) {
5881 HInstruction* consolidated_load =
5882 TryBuildConsolidatedElementLoad(object, key, val, maps);
5883 if (consolidated_load != NULL) {
5884 AddInstruction(consolidated_load);
5885 *has_side_effects |= consolidated_load->HasObservableSideEffects();
5886 consolidated_load->set_position(position);
5887 return consolidated_load;
5888 }
5889 }
5890
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005891 static const int kNumElementTypes = kElementsKindCount;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005892 bool type_todo[kNumElementTypes];
5893 for (int i = 0; i < kNumElementTypes; ++i) {
5894 type_todo[i] = false;
5895 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005896
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005897 // Elements_kind transition support.
5898 MapHandleList transition_target(maps->length());
5899 // Collect possible transition targets.
5900 MapHandleList possible_transitioned_maps(maps->length());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005901 for (int i = 0; i < maps->length(); ++i) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005902 Handle<Map> map = maps->at(i);
5903 ElementsKind elements_kind = map->elements_kind();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005904 if (IsFastElementsKind(elements_kind) &&
5905 elements_kind != GetInitialFastElementsKind()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005906 possible_transitioned_maps.Add(map);
5907 }
5908 }
5909 // Get transition target for each map (NULL == no transition).
5910 for (int i = 0; i < maps->length(); ++i) {
5911 Handle<Map> map = maps->at(i);
5912 Handle<Map> transitioned_map =
5913 map->FindTransitionedMap(&possible_transitioned_maps);
5914 transition_target.Add(transitioned_map);
5915 }
5916
5917 int num_untransitionable_maps = 0;
5918 Handle<Map> untransitionable_map;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005919 HTransitionElementsKind* transition = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005920 for (int i = 0; i < maps->length(); ++i) {
5921 Handle<Map> map = maps->at(i);
5922 ASSERT(map->IsMap());
5923 if (!transition_target.at(i).is_null()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005924 ASSERT(Map::IsValidElementsTransition(
5925 map->elements_kind(),
5926 transition_target.at(i)->elements_kind()));
5927 transition = new(zone()) HTransitionElementsKind(
5928 object, map, transition_target.at(i));
5929 AddInstruction(transition);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005930 } else {
5931 type_todo[map->elements_kind()] = true;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005932 if (IsExternalArrayElementsKind(map->elements_kind())) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005933 todo_external_array = true;
5934 }
5935 num_untransitionable_maps++;
5936 untransitionable_map = map;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005937 }
5938 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005939
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005940 // If only one map is left after transitioning, handle this case
5941 // monomorphically.
5942 if (num_untransitionable_maps == 1) {
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005943 HInstruction* instr = NULL;
5944 if (untransitionable_map->has_slow_elements_kind()) {
5945 instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
5946 : BuildLoadKeyedGeneric(object, key));
5947 } else {
5948 instr = AddInstruction(BuildMonomorphicElementAccess(
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005949 object, key, val, transition, untransitionable_map, is_store));
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005950 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005951 *has_side_effects |= instr->HasObservableSideEffects();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005952 instr->set_position(position);
5953 return is_store ? NULL : instr;
5954 }
5955
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005956 AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005957 HBasicBlock* join = graph()->CreateBasicBlock();
5958
5959 HInstruction* elements_kind_instr =
5960 AddInstruction(new(zone()) HElementsKind(object));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005961 HCompareConstantEqAndBranch* elements_kind_branch = NULL;
5962 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005963 HLoadExternalArrayPointer* external_elements = NULL;
5964 HInstruction* checked_key = NULL;
5965
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005966 // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds
5967 // are handled before external arrays.
5968 STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
5969 STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005970 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
5971 STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005972
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005973 for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005974 elements_kind <= LAST_ELEMENTS_KIND;
5975 elements_kind = ElementsKind(elements_kind + 1)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005976 // After having handled FAST_* and DICTIONARY_ELEMENTS, we need to add some
5977 // code that's executed for all external array cases.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005978 STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
5979 LAST_ELEMENTS_KIND);
5980 if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005981 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00005982 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005983 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00005984 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
5985 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
5986 AddInstruction(external_elements);
5987 }
5988 if (type_todo[elements_kind]) {
5989 HBasicBlock* if_true = graph()->CreateBasicBlock();
5990 HBasicBlock* if_false = graph()->CreateBasicBlock();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00005991 elements_kind_branch = new(zone()) HCompareConstantEqAndBranch(
5992 elements_kind_instr, elements_kind, Token::EQ_STRICT);
5993 elements_kind_branch->SetSuccessorAt(0, if_true);
5994 elements_kind_branch->SetSuccessorAt(1, if_false);
5995 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005996
5997 set_current_block(if_true);
5998 HInstruction* access;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005999 if (IsFastElementsKind(elements_kind)) {
6000 if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006001 AddInstruction(new(zone()) HCheckMaps(
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006002 elements, isolate()->factory()->fixed_array_map(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006003 zone(), elements_kind_branch));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006004 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006005 // TODO(jkummerow): The need for these two blocks could be avoided
6006 // in one of two ways:
6007 // (1) Introduce ElementsKinds for JSArrays that are distinct from
6008 // those for fast objects.
6009 // (2) Put the common instructions into a third "join" block. This
6010 // requires additional AST IDs that we can deopt to from inside
6011 // that join block. They must be added to the Property class (when
6012 // it's a keyed property) and registered in the full codegen.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006013 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
6014 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006015 HHasInstanceTypeAndBranch* typecheck =
6016 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
6017 typecheck->SetSuccessorAt(0, if_jsarray);
6018 typecheck->SetSuccessorAt(1, if_fastobject);
6019 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006020
6021 set_current_block(if_jsarray);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006022 HInstruction* length;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006023 length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck,
6024 HType::Smi()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006025 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006026 access = AddInstruction(BuildFastElementAccess(
6027 elements, checked_key, val, elements_kind, is_store));
6028 if (!is_store) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00006029 Push(access);
6030 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006031
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006032 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006033 if (position != -1) {
6034 access->set_position(position);
6035 }
6036 if_jsarray->Goto(join);
6037
6038 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006039 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006040 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006041 access = AddInstruction(BuildFastElementAccess(
6042 elements, checked_key, val, elements_kind, is_store));
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006043 } else if (elements_kind == DICTIONARY_ELEMENTS) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006044 if (is_store) {
6045 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
6046 } else {
6047 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
6048 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006049 } else { // External array elements.
6050 access = AddInstruction(BuildExternalArrayElementAccess(
6051 external_elements, checked_key, val, elements_kind, is_store));
6052 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006053 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006054 access->set_position(position);
6055 if (!is_store) {
6056 Push(access);
6057 }
6058 current_block()->Goto(join);
6059 set_current_block(if_false);
6060 }
6061 }
6062
6063 // Deopt if none of the cases matched.
6064 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
6065 join->SetJoinId(ast_id);
6066 set_current_block(join);
6067 return is_store ? NULL : Pop();
6068}
6069
6070
6071HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
6072 HValue* key,
6073 HValue* val,
6074 Expression* expr,
6075 int ast_id,
6076 int position,
6077 bool is_store,
6078 bool* has_side_effects) {
6079 ASSERT(!expr->IsPropertyName());
6080 HInstruction* instr = NULL;
6081 if (expr->IsMonomorphic()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006082 Handle<Map> map = expr->GetMonomorphicReceiverType();
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006083 if (map->has_slow_elements_kind()) {
6084 instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
6085 : BuildLoadKeyedGeneric(obj, key);
6086 } else {
6087 AddInstruction(new(zone()) HCheckNonSmi(obj));
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006088 instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006089 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006090 } else if (expr->GetReceiverTypes() != NULL &&
6091 !expr->GetReceiverTypes()->is_empty()) {
6092 return HandlePolymorphicElementAccess(
6093 obj, key, val, expr, ast_id, position, is_store, has_side_effects);
6094 } else {
6095 if (is_store) {
6096 instr = BuildStoreKeyedGeneric(obj, key, val);
6097 } else {
6098 instr = BuildLoadKeyedGeneric(obj, key);
6099 }
6100 }
6101 instr->set_position(position);
6102 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006103 *has_side_effects = instr->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006104 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006105}
6106
6107
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006108HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
6109 HValue* key,
6110 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006111 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006112 return new(zone()) HStoreKeyedGeneric(
6113 context,
6114 object,
6115 key,
6116 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006117 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006118}
6119
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006120
6121void HGraphBuilder::EnsureArgumentsArePushedForAccess() {
6122 // Outermost function already has arguments on the stack.
6123 if (function_state()->outer() == NULL) return;
6124
6125 if (function_state()->arguments_pushed()) return;
6126
6127 // Push arguments when entering inlined function.
6128 HEnterInlined* entry = function_state()->entry();
6129
6130 ZoneList<HValue*>* arguments_values = entry->arguments_values();
6131
6132 HInstruction* insert_after = entry;
6133 for (int i = 0; i < arguments_values->length(); i++) {
6134 HValue* argument = arguments_values->at(i);
6135 HInstruction* push_argument = new(zone()) HPushArgument(argument);
6136 push_argument->InsertAfter(insert_after);
6137 insert_after = push_argument;
6138 }
6139
6140 HArgumentsElements* arguments_elements =
6141 new(zone()) HArgumentsElements(true);
6142 arguments_elements->ClearFlag(HValue::kUseGVN);
6143 arguments_elements->InsertAfter(insert_after);
6144 function_state()->set_arguments_elements(arguments_elements);
6145}
6146
6147
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006148bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
6149 VariableProxy* proxy = expr->obj()->AsVariableProxy();
6150 if (proxy == NULL) return false;
6151 if (!proxy->var()->IsStackAllocated()) return false;
6152 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
6153 return false;
6154 }
6155
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006156 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006157 if (expr->key()->IsPropertyName()) {
6158 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
6159 if (!name->IsEqualTo(CStrVector("length"))) return false;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006160
6161 if (function_state()->outer() == NULL) {
6162 HInstruction* elements = AddInstruction(
6163 new(zone()) HArgumentsElements(false));
6164 result = new(zone()) HArgumentsLength(elements);
6165 } else {
6166 // Number of arguments without receiver.
6167 int argument_count = environment()->
6168 arguments_environment()->parameter_count() - 1;
6169 result = new(zone()) HConstant(
6170 Handle<Object>(Smi::FromInt(argument_count)),
6171 Representation::Integer32());
6172 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006173 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006174 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006175 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006176 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006177 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006178 Drop(1); // Arguments object.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006179 if (function_state()->outer() == NULL) {
6180 HInstruction* elements = AddInstruction(
6181 new(zone()) HArgumentsElements(false));
6182 HInstruction* length = AddInstruction(
6183 new(zone()) HArgumentsLength(elements));
6184 HInstruction* checked_key =
6185 AddInstruction(new(zone()) HBoundsCheck(key, length));
6186 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
6187 } else {
6188 EnsureArgumentsArePushedForAccess();
6189
6190 // Number of arguments without receiver.
6191 HInstruction* elements = function_state()->arguments_elements();
6192 int argument_count = environment()->
6193 arguments_environment()->parameter_count() - 1;
6194 HInstruction* length = AddInstruction(new(zone()) HConstant(
6195 Handle<Object>(Smi::FromInt(argument_count)),
6196 Representation::Integer32()));
6197 HInstruction* checked_key =
6198 AddInstruction(new(zone()) HBoundsCheck(key, length));
6199 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
6200 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006201 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006202 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006203 return true;
6204}
6205
6206
6207void HGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006208 ASSERT(!HasStackOverflow());
6209 ASSERT(current_block() != NULL);
6210 ASSERT(current_block()->HasPredecessor());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006211 expr->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006212
6213 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006214
danno@chromium.org160a7b02011-04-18 15:51:38 +00006215 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006216
6217 HInstruction* instr = NULL;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006218 if (expr->AsProperty()->IsArrayLength()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006219 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006220 AddInstruction(new(zone()) HCheckNonSmi(array));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006221 HInstruction* mapcheck =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006222 AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone()));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006223 instr = new(zone()) HJSArrayLength(array, mapcheck);
ager@chromium.org378b34e2011-01-28 08:04:38 +00006224 } else if (expr->IsStringLength()) {
6225 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006226 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006227 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006228 instr = new(zone()) HStringLength(string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006229 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006230 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006231 HValue* index = Pop();
6232 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006233 HValue* context = environment()->LookupContext();
6234 HStringCharCodeAt* char_code =
6235 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006236 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006237 instr = new(zone()) HStringCharFromCode(context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00006238
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006239 } else if (expr->IsFunctionPrototype()) {
6240 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006241 AddInstruction(new(zone()) HCheckNonSmi(function));
6242 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006243
6244 } else if (expr->key()->IsPropertyName()) {
6245 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006246 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006247
6248 HValue* obj = Pop();
6249 if (expr->IsMonomorphic()) {
6250 instr = BuildLoadNamed(obj, expr, types->first(), name);
6251 } else if (types != NULL && types->length() > 1) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006252 AddInstruction(new(zone()) HCheckNonSmi(obj));
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006253 HandlePolymorphicLoadNamedField(expr, obj, types, name);
6254 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006255 } else {
6256 instr = BuildLoadNamedGeneric(obj, expr);
6257 }
6258
6259 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006260 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006261
6262 HValue* key = Pop();
6263 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006264
6265 bool has_side_effects = false;
6266 HValue* load = HandleKeyedElementAccess(
6267 obj, key, NULL, expr, expr->id(), expr->position(),
6268 false, // is_store
6269 &has_side_effects);
6270 if (has_side_effects) {
6271 if (ast_context()->IsEffect()) {
6272 AddSimulate(expr->id());
6273 } else {
6274 Push(load);
6275 AddSimulate(expr->id());
6276 Drop(1);
6277 }
6278 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006279 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006280 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006281 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006282 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006283}
6284
6285
6286void HGraphBuilder::AddCheckConstantFunction(Call* expr,
6287 HValue* receiver,
6288 Handle<Map> receiver_map,
6289 bool smi_and_map_check) {
6290 // Constant functions have the nice property that the map will change if they
6291 // are overwritten. Therefore it is enough to check the map of the holder and
6292 // its prototypes.
6293 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006294 AddInstruction(new(zone()) HCheckNonSmi(receiver));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006295 AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map,
6296 zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006297 }
6298 if (!expr->holder().is_null()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006299 AddInstruction(new(zone()) HCheckPrototypeMaps(
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006300 Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
6301 expr->holder()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006302 }
6303}
6304
6305
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006306class FunctionSorter {
6307 public:
6308 FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
6309 FunctionSorter(int index, int ticks, int ast_length, int src_length)
6310 : index_(index),
6311 ticks_(ticks),
6312 ast_length_(ast_length),
6313 src_length_(src_length) { }
6314
6315 int index() const { return index_; }
6316 int ticks() const { return ticks_; }
6317 int ast_length() const { return ast_length_; }
6318 int src_length() const { return src_length_; }
6319
6320 private:
6321 int index_;
6322 int ticks_;
6323 int ast_length_;
6324 int src_length_;
6325};
6326
6327
6328static int CompareHotness(void const* a, void const* b) {
6329 FunctionSorter const* function1 = reinterpret_cast<FunctionSorter const*>(a);
6330 FunctionSorter const* function2 = reinterpret_cast<FunctionSorter const*>(b);
6331 int diff = function1->ticks() - function2->ticks();
6332 if (diff != 0) return -diff;
6333 diff = function1->ast_length() - function2->ast_length();
6334 if (diff != 0) return diff;
6335 return function1->src_length() - function2->src_length();
6336}
6337
6338
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006339void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
6340 HValue* receiver,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006341 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006342 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006343 // TODO(ager): We should recognize when the prototype chains for different
6344 // maps are identical. In that case we can avoid repeatedly generating the
6345 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006346 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006347 HBasicBlock* join = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006348 FunctionSorter order[kMaxCallPolymorphism];
6349 int ordered_functions = 0;
6350 for (int i = 0;
6351 i < types->length() && ordered_functions < kMaxCallPolymorphism;
6352 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006353 Handle<Map> map = types->at(i);
6354 if (expr->ComputeTarget(map, name)) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006355 order[ordered_functions++] =
6356 FunctionSorter(i,
6357 expr->target()->shared()->profiler_ticks(),
6358 InliningAstSize(expr->target()),
6359 expr->target()->shared()->SourceSize());
danno@chromium.org2c26cb12012-05-03 09:06:43 +00006360 }
danno@chromium.org2c26cb12012-05-03 09:06:43 +00006361 }
6362
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006363 qsort(reinterpret_cast<void*>(&order[0]),
6364 ordered_functions,
6365 sizeof(order[0]),
6366 &CompareHotness);
6367
6368 for (int fn = 0; fn < ordered_functions; ++fn) {
6369 int i = order[fn].index();
6370 Handle<Map> map = types->at(i);
6371 if (fn == 0) {
6372 // Only needed once.
6373 AddInstruction(new(zone()) HCheckNonSmi(receiver));
6374 join = graph()->CreateBasicBlock();
6375 }
6376 HBasicBlock* if_true = graph()->CreateBasicBlock();
6377 HBasicBlock* if_false = graph()->CreateBasicBlock();
6378 HCompareMap* compare =
6379 new(zone()) HCompareMap(receiver, map, if_true, if_false);
6380 current_block()->Finish(compare);
6381
6382 set_current_block(if_true);
6383 expr->ComputeTarget(map, name);
6384 AddCheckConstantFunction(expr, receiver, map, false);
6385 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
6386 Handle<JSFunction> caller = info()->closure();
6387 SmartArrayPointer<char> caller_name =
6388 caller->shared()->DebugName()->ToCString();
6389 PrintF("Trying to inline the polymorphic call to %s from %s\n",
6390 *name->ToCString(),
6391 *caller_name);
6392 }
6393 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
6394 // Trying to inline will signal that we should bailout from the
6395 // entire compilation by setting stack overflow on the visitor.
6396 if (HasStackOverflow()) return;
6397 } else {
6398 HCallConstantFunction* call =
6399 new(zone()) HCallConstantFunction(expr->target(), argument_count);
6400 call->set_position(expr->position());
6401 PreProcessCall(call);
6402 AddInstruction(call);
6403 if (!ast_context()->IsEffect()) Push(call);
6404 }
6405
6406 if (current_block() != NULL) current_block()->Goto(join);
6407 set_current_block(if_false);
6408 }
6409
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006410 // Finish up. Unconditionally deoptimize if we've handled all the maps we
6411 // know about and do not want to handle ones we've never seen. Otherwise
6412 // use a generic IC.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006413 if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006414 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006415 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006416 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006417 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006418 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006419 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00006420
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006421 if (join != NULL) {
6422 AddInstruction(call);
6423 if (!ast_context()->IsEffect()) Push(call);
6424 current_block()->Goto(join);
6425 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006426 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00006427 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006428 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006429
6430 // We assume that control flow is always live after an expression. So
6431 // even without predecessors to the join block, we set it as the exit
6432 // block and continue by adding instructions there.
6433 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006434 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006435 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006436 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006437 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006438 } else {
6439 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006440 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006441}
6442
6443
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006444void HGraphBuilder::TraceInline(Handle<JSFunction> target,
6445 Handle<JSFunction> caller,
6446 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006447 if (FLAG_trace_inlining) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006448 SmartArrayPointer<char> target_name =
6449 target->shared()->DebugName()->ToCString();
6450 SmartArrayPointer<char> caller_name =
6451 caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006452 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006453 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006454 } else {
6455 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006456 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006457 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006458 }
6459}
6460
6461
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006462static const int kNotInlinable = 1000000000;
6463
6464
6465int HGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
6466 if (!FLAG_use_inlining) return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006467
6468 // Precondition: call is monomorphic and we have found a target with the
6469 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006470 Handle<JSFunction> caller = info()->closure();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006471 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006472
6473 // Do a quick check on source code length to avoid parsing large
6474 // inlining candidates.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006475 if (target_shared->SourceSize() >
6476 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006477 TraceInline(target, caller, "target text too big");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006478 return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006479 }
6480
6481 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006482 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006483 TraceInline(target, caller, "target not inlineable");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006484 return kNotInlinable;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006485 }
yangguo@chromium.org56454712012-02-16 15:33:53 +00006486 if (target_shared->dont_inline() || target_shared->dont_optimize()) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006487 TraceInline(target, caller, "target contains unsupported syntax [early]");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006488 return kNotInlinable;
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006489 }
6490
6491 int nodes_added = target_shared->ast_node_count();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006492 return nodes_added;
6493}
6494
6495
6496bool HGraphBuilder::TryInline(CallKind call_kind,
6497 Handle<JSFunction> target,
6498 ZoneList<Expression*>* arguments,
6499 HValue* receiver,
6500 int ast_id,
6501 int return_id,
6502 ReturnHandlingFlag return_handling) {
6503 int nodes_added = InliningAstSize(target);
6504 if (nodes_added == kNotInlinable) return false;
6505
6506 Handle<JSFunction> caller = info()->closure();
6507
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006508 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006509 TraceInline(target, caller, "target AST is too large [early]");
6510 return false;
6511 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006512
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00006513 Handle<SharedFunctionInfo> target_shared(target->shared());
6514
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006515#if !defined(V8_TARGET_ARCH_IA32)
6516 // Target must be able to use caller's context.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006517 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006518 if (target->context() != outer_info->closure()->context() ||
6519 outer_info->scope()->contains_with() ||
6520 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006521 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006522 return false;
6523 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006524#endif
6525
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006526
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006527 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006528 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006529 int current_level = 1;
6530 while (env->outer() != NULL) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006531 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006532 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006533 return false;
6534 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00006535 if (env->outer()->frame_type() == JS_FUNCTION) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006536 current_level++;
6537 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006538 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006539 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006540
6541 // Don't inline recursive functions.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006542 for (FunctionState* state = function_state();
6543 state != NULL;
6544 state = state->outer()) {
6545 if (state->compilation_info()->closure()->shared() == *target_shared) {
6546 TraceInline(target, caller, "target is recursive");
6547 return false;
6548 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006549 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006550
6551 // We don't want to add more than a certain number of nodes from inlining.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006552 if (inlined_count_ > Min(FLAG_max_inlined_nodes_cumulative,
6553 kUnlimitedMaxInlinedNodesCumulative)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006554 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006555 return false;
6556 }
6557
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006558 // Parse and allocate variables.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00006559 CompilationInfo target_info(target, zone());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006560 if (!ParserApi::Parse(&target_info, kNoParsingFlags) ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006561 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006562 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006563 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006564 SetStackOverflow();
yangguo@chromium.org56454712012-02-16 15:33:53 +00006565 target_shared->DisableOptimization();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006566 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006567 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006568 return false;
6569 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006570
6571 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006572 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006573 return false;
6574 }
6575 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006576
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006577 // The following conditions must be checked again after re-parsing, because
6578 // earlier the information might not have been complete due to lazy parsing.
6579 nodes_added = function->ast_node_count();
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00006580 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00006581 TraceInline(target, caller, "target AST is too large [late]");
6582 return false;
6583 }
6584 AstProperties::Flags* flags(function->flags());
6585 if (flags->Contains(kDontInline) || flags->Contains(kDontOptimize)) {
6586 TraceInline(target, caller, "target contains unsupported syntax [late]");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006587 return false;
6588 }
6589
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006590 // If the function uses the arguments object check that inlining of functions
6591 // with arguments object is enabled and the arguments-variable is
6592 // stack allocated.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006593 if (function->scope()->arguments() != NULL) {
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006594 if (!FLAG_inline_arguments) {
6595 TraceInline(target, caller, "target uses arguments object");
6596 return false;
6597 }
6598
6599 if (!function->scope()->arguments()->IsStackAllocated()) {
6600 TraceInline(target,
6601 caller,
6602 "target uses non-stackallocated arguments object");
6603 return false;
6604 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006605 }
6606
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006607 // All declarations must be inlineable.
6608 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
6609 int decl_count = decls->length();
6610 for (int i = 0; i < decl_count; ++i) {
6611 if (!decls->at(i)->IsInlineable()) {
6612 TraceInline(target, caller, "target has non-trivial declaration");
6613 return false;
6614 }
6615 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006616
6617 // Generate the deoptimization data for the unoptimized version of
6618 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006619 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006620 // Note that we compile here using the same AST that we will use for
6621 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006622 target_info.EnableDeoptimizationSupport();
6623 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006624 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006625 return false;
6626 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006627 if (target_shared->scope_info() == ScopeInfo::Empty()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006628 // The scope info might not have been set if a lazily compiled
6629 // function is inlined before being called for the first time.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006630 Handle<ScopeInfo> target_scope_info =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006631 ScopeInfo::Create(target_info.scope(), zone());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006632 target_shared->set_scope_info(*target_scope_info);
6633 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006634 target_shared->EnableDeoptimizationSupport(*target_info.code());
6635 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
6636 &target_info,
6637 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006638 }
6639
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006640 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006641 // After this point, we've made a decision to inline this function (so
6642 // TryInline should always return true).
6643
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006644 // Save the pending call context and type feedback oracle. Set up new ones
6645 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006646 ASSERT(target_shared->has_deoptimization_support());
6647 TypeFeedbackOracle target_oracle(
6648 Handle<Code>(target_shared->code()),
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00006649 Handle<Context>(target->context()->global_context()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006650 isolate(),
6651 zone());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006652 // The function state is new-allocated because we need to delete it
6653 // in two different places.
ulan@chromium.org967e2702012-02-28 09:49:15 +00006654 FunctionState* target_state = new FunctionState(
6655 this, &target_info, &target_oracle, return_handling);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006656
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006657 HConstant* undefined = graph()->GetConstantUndefined();
6658 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00006659 environment()->CopyForInlining(target,
ulan@chromium.org967e2702012-02-28 09:49:15 +00006660 arguments->length(),
lrn@chromium.org1c092762011-05-09 09:42:16 +00006661 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00006662 undefined,
ulan@chromium.org967e2702012-02-28 09:49:15 +00006663 call_kind,
6664 function_state()->is_construct());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006665#ifdef V8_TARGET_ARCH_IA32
6666 // IA32 only, overwrite the caller's context in the deoptimization
6667 // environment with the correct one.
6668 //
6669 // TODO(kmillikin): implement the same inlining on other platforms so we
6670 // can remove the unsightly ifdefs in this function.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006671 HConstant* context =
6672 new(zone()) HConstant(Handle<Context>(target->context()),
6673 Representation::Tagged());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006674 AddInstruction(context);
6675 inner_env->BindContext(context);
6676#endif
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006677
danno@chromium.orgb2a1c072012-03-23 15:47:56 +00006678 AddSimulate(return_id);
6679 current_block()->UpdateEnvironment(inner_env);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006680
6681 ZoneList<HValue*>* arguments_values = NULL;
6682
6683 // If the function uses arguments copy current arguments values
6684 // to use them for materialization.
6685 if (function->scope()->arguments() != NULL) {
6686 HEnvironment* arguments_env = inner_env->arguments_environment();
6687 int arguments_count = arguments_env->parameter_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006688 arguments_values = new(zone()) ZoneList<HValue*>(arguments_count, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006689 for (int i = 0; i < arguments_count; i++) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006690 arguments_values->Add(arguments_env->Lookup(i), zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006691 }
6692 }
6693
6694 HEnterInlined* enter_inlined =
6695 new(zone()) HEnterInlined(target,
6696 arguments->length(),
6697 function,
6698 call_kind,
6699 function_state()->is_construct(),
6700 function->scope()->arguments(),
6701 arguments_values);
6702 function_state()->set_entry(enter_inlined);
6703 AddInstruction(enter_inlined);
6704
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006705 // If the function uses arguments object create and bind one.
6706 if (function->scope()->arguments() != NULL) {
6707 ASSERT(function->scope()->arguments()->IsStackAllocated());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006708 inner_env->Bind(function->scope()->arguments(),
6709 graph()->GetArgumentsObject());
yangguo@chromium.org154ff992012-03-13 08:09:54 +00006710 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006711
6712
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006713 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006714 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006715 if (HasStackOverflow()) {
6716 // Bail out if the inline function did, as we cannot residualize a call
6717 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006718 TraceInline(target, caller, "inline graph construction failed");
yangguo@chromium.org56454712012-02-16 15:33:53 +00006719 target_shared->DisableOptimization();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006720 inline_bailout_ = true;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006721 delete target_state;
danno@chromium.org160a7b02011-04-18 15:51:38 +00006722 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006723 }
6724
6725 // Update inlined nodes count.
6726 inlined_count_ += nodes_added;
6727
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006728 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006729
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006730 if (current_block() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006731 // Add default return value (i.e. undefined for normals calls or the newly
6732 // allocated receiver for construct calls) if control can fall off the
6733 // body. In a test context, undefined is false and any JSObject is true.
6734 if (call_context()->IsValue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006735 ASSERT(function_return() != NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00006736 HValue* return_value = function_state()->is_construct()
6737 ? receiver
6738 : undefined;
6739 current_block()->AddLeaveInlined(return_value,
6740 function_return(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006741 function_state());
ulan@chromium.org967e2702012-02-28 09:49:15 +00006742 } else if (call_context()->IsEffect()) {
6743 ASSERT(function_return() != NULL);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006744 current_block()->Goto(function_return(), function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006745 } else {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006746 ASSERT(call_context()->IsTest());
ulan@chromium.org967e2702012-02-28 09:49:15 +00006747 ASSERT(inlined_test_context() != NULL);
6748 HBasicBlock* target = function_state()->is_construct()
6749 ? inlined_test_context()->if_true()
6750 : inlined_test_context()->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006751 current_block()->Goto(target, function_state());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006752 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006753 }
6754
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006755 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006756 if (inlined_test_context() != NULL) {
6757 HBasicBlock* if_true = inlined_test_context()->if_true();
6758 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006759
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006760 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006761 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006762 ClearInlinedTestContext();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006763 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006764
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006765 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006766 if (if_true->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006767 if_true->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006768 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006769 if_true->Goto(true_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006770 }
6771 if (if_false->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006772 if_false->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006773 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00006774 if_false->Goto(false_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006775 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006776 set_current_block(NULL);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006777 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006778
danno@chromium.org160a7b02011-04-18 15:51:38 +00006779 } else if (function_return()->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00006780 function_return()->SetJoinId(ast_id);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006781 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006782 } else {
6783 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006784 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006785 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006786 return true;
6787}
6788
6789
ulan@chromium.org967e2702012-02-28 09:49:15 +00006790bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
6791 // The function call we are inlining is a method call if the call
6792 // is a property call.
6793 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
6794 ? CALL_AS_FUNCTION
6795 : CALL_AS_METHOD;
6796
6797 return TryInline(call_kind,
6798 expr->target(),
6799 expr->arguments(),
6800 NULL,
6801 expr->id(),
6802 expr->ReturnId(),
6803 drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
6804}
6805
6806
6807bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) {
6808 return TryInline(CALL_AS_FUNCTION,
6809 expr->target(),
6810 expr->arguments(),
6811 receiver,
6812 expr->id(),
6813 expr->ReturnId(),
6814 CONSTRUCT_CALL_RETURN);
6815}
6816
6817
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006818bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) {
6819 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
6820 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
6821 switch (id) {
6822 case kMathRound:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006823 case kMathAbs:
6824 case kMathSqrt:
6825 case kMathLog:
6826 case kMathSin:
6827 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006828 case kMathTan:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006829 if (expr->arguments()->length() == 1) {
6830 HValue* argument = Pop();
6831 HValue* context = environment()->LookupContext();
6832 Drop(1); // Receiver.
6833 HUnaryMathOperation* op =
6834 new(zone()) HUnaryMathOperation(context, argument, id);
6835 op->set_position(expr->position());
6836 if (drop_extra) Drop(1); // Optionally drop the function.
6837 ast_context()->ReturnInstruction(op, expr->id());
6838 return true;
6839 }
6840 break;
6841 default:
6842 // Not supported for inlining yet.
6843 break;
6844 }
6845 return false;
6846}
6847
6848
6849bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr,
6850 HValue* receiver,
6851 Handle<Map> receiver_map,
6852 CheckType check_type) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006853 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006854 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006855 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006856 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006857 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
6858 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006859 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006860 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006861 if (argument_count == 2 && check_type == STRING_CHECK) {
6862 HValue* index = Pop();
6863 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006864 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006865 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006866 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006867 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
6868 expr->holder()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006869 HStringCharCodeAt* char_code =
6870 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006871 if (id == kStringCharCodeAt) {
6872 ast_context()->ReturnInstruction(char_code, expr->id());
6873 return true;
6874 }
6875 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006876 HStringCharFromCode* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006877 new(zone()) HStringCharFromCode(context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006878 ast_context()->ReturnInstruction(result, expr->id());
6879 return true;
6880 }
6881 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006882 case kMathRound:
6883 case kMathFloor:
6884 case kMathAbs:
6885 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006886 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00006887 case kMathSin:
6888 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006889 case kMathTan:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006890 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
6891 AddCheckConstantFunction(expr, receiver, receiver_map, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006892 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006893 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006894 Drop(1); // Receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006895 HUnaryMathOperation* op =
6896 new(zone()) HUnaryMathOperation(context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006897 op->set_position(expr->position());
6898 ast_context()->ReturnInstruction(op, expr->id());
6899 return true;
6900 }
6901 break;
6902 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006903 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
6904 AddCheckConstantFunction(expr, receiver, receiver_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006905 HValue* right = Pop();
6906 HValue* left = Pop();
6907 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006908 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006909 HInstruction* result = NULL;
6910 // Use sqrt() if exponent is 0.5 or -0.5.
6911 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
6912 double exponent = HConstant::cast(right)->DoubleValue();
6913 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006914 result =
6915 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006916 } else if (exponent == -0.5) {
6917 HConstant* double_one =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006918 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
6919 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006920 AddInstruction(double_one);
6921 HUnaryMathOperation* square_root =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006922 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006923 AddInstruction(square_root);
6924 // MathPowHalf doesn't have side effects so there's no need for
6925 // an environment simulation here.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006926 ASSERT(!square_root->HasObservableSideEffects());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006927 result = new(zone()) HDiv(context, double_one, square_root);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006928 } else if (exponent == 2.0) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006929 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006930 }
6931 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006932 HConstant::cast(right)->HasInteger32Value() &&
6933 HConstant::cast(right)->Integer32Value() == 2) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006934 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006935 }
6936
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006937 if (result == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006938 result = new(zone()) HPower(left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006939 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006940 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006941 return true;
6942 }
6943 break;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006944 case kMathRandom:
6945 if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
6946 AddCheckConstantFunction(expr, receiver, receiver_map, true);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00006947 Drop(1); // Receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006948 HValue* context = environment()->LookupContext();
6949 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
6950 AddInstruction(global_object);
6951 HRandom* result = new(zone()) HRandom(global_object);
6952 ast_context()->ReturnInstruction(result, expr->id());
6953 return true;
6954 }
6955 break;
6956 case kMathMax:
6957 case kMathMin:
6958 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
6959 AddCheckConstantFunction(expr, receiver, receiver_map, true);
6960 HValue* right = Pop();
6961 HValue* left = Pop();
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006962 Pop(); // Pop receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006963
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006964 HValue* left_operand = left;
6965 HValue* right_operand = right;
6966
6967 // If we do not have two integers, we convert to double for comparison.
6968 if (!left->representation().IsInteger32() ||
6969 !right->representation().IsInteger32()) {
6970 if (!left->representation().IsDouble()) {
6971 HChange* left_convert = new(zone()) HChange(
6972 left,
6973 Representation::Double(),
6974 false, // Do not truncate when converting to double.
6975 true); // Deoptimize for undefined.
6976 left_convert->SetFlag(HValue::kBailoutOnMinusZero);
6977 left_operand = AddInstruction(left_convert);
6978 }
6979 if (!right->representation().IsDouble()) {
6980 HChange* right_convert = new(zone()) HChange(
6981 right,
6982 Representation::Double(),
6983 false, // Do not truncate when converting to double.
6984 true); // Deoptimize for undefined.
6985 right_convert->SetFlag(HValue::kBailoutOnMinusZero);
6986 right_operand = AddInstruction(right_convert);
6987 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006988 }
6989
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006990 ASSERT(left_operand->representation().Equals(
6991 right_operand->representation()));
6992 ASSERT(!left_operand->representation().IsTagged());
6993
6994 Token::Value op = (id == kMathMin) ? Token::LT : Token::GT;
6995
6996 HCompareIDAndBranch* compare =
6997 new(zone()) HCompareIDAndBranch(left_operand, right_operand, op);
6998 compare->SetInputRepresentation(left_operand->representation());
6999
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007000 HBasicBlock* return_left = graph()->CreateBasicBlock();
7001 HBasicBlock* return_right = graph()->CreateBasicBlock();
7002
7003 compare->SetSuccessorAt(0, return_left);
7004 compare->SetSuccessorAt(1, return_right);
7005 current_block()->Finish(compare);
7006
7007 set_current_block(return_left);
7008 Push(left);
7009 set_current_block(return_right);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00007010 // The branch above always returns the right operand if either of
7011 // them is NaN, but the spec requires that max/min(NaN, X) = NaN.
7012 // We add another branch that checks if the left operand is NaN or not.
7013 if (left_operand->representation().IsDouble()) {
7014 // If left_operand != left_operand then it is NaN.
7015 HCompareIDAndBranch* compare_nan = new(zone()) HCompareIDAndBranch(
7016 left_operand, left_operand, Token::EQ);
7017 compare_nan->SetInputRepresentation(left_operand->representation());
7018 HBasicBlock* left_is_number = graph()->CreateBasicBlock();
7019 HBasicBlock* left_is_nan = graph()->CreateBasicBlock();
7020 compare_nan->SetSuccessorAt(0, left_is_number);
7021 compare_nan->SetSuccessorAt(1, left_is_nan);
7022 current_block()->Finish(compare_nan);
7023 set_current_block(left_is_nan);
7024 Push(left);
7025 set_current_block(left_is_number);
7026 Push(right);
7027 return_right = CreateJoin(left_is_number, left_is_nan, expr->id());
7028 } else {
7029 Push(right);
7030 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00007031
7032 HBasicBlock* join = CreateJoin(return_left, return_right, expr->id());
7033 set_current_block(join);
7034 ast_context()->ReturnValue(Pop());
7035 return true;
7036 }
7037 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007038 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007039 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007040 break;
7041 }
7042 return false;
7043}
7044
7045
7046bool HGraphBuilder::TryCallApply(Call* expr) {
7047 Expression* callee = expr->expression();
7048 Property* prop = callee->AsProperty();
7049 ASSERT(prop != NULL);
7050
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00007051 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
7052 return false;
7053 }
7054 Handle<Map> function_map = expr->GetReceiverTypes()->first();
7055 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
7056 !expr->target()->shared()->HasBuiltinFunctionId() ||
7057 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
7058 return false;
7059 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007060
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00007061 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007062
7063 ZoneList<Expression*>* args = expr->arguments();
7064 if (args->length() != 2) return false;
7065
7066 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00007067 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007068 HValue* arg_two_value = environment()->Lookup(arg_two->var());
7069 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
7070
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007071 // Found pattern f.apply(receiver, arguments).
7072 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007073 if (HasStackOverflow() || current_block() == NULL) return true;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00007074 HValue* function = Top();
7075 AddCheckConstantFunction(expr, function, function_map, true);
7076 Drop(1);
7077
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007078 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00007079 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007080 HValue* receiver = Pop();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007081
7082 if (function_state()->outer() == NULL) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007083 HInstruction* elements = AddInstruction(
7084 new(zone()) HArgumentsElements(false));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007085 HInstruction* length =
7086 AddInstruction(new(zone()) HArgumentsLength(elements));
7087 HValue* wrapped_receiver =
7088 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
7089 HInstruction* result =
7090 new(zone()) HApplyArguments(function,
7091 wrapped_receiver,
7092 length,
7093 elements);
7094 result->set_position(expr->position());
7095 ast_context()->ReturnInstruction(result, expr->id());
7096 return true;
7097 } else {
7098 // We are inside inlined function and we know exactly what is inside
7099 // arguments object.
7100 HValue* context = environment()->LookupContext();
7101
7102 HValue* wrapped_receiver =
7103 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
7104 PushAndAdd(new(zone()) HPushArgument(wrapped_receiver));
7105
ulan@chromium.org6ff65142012-03-21 09:52:17 +00007106 HEnvironment* arguments_env = environment()->arguments_environment();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007107
ulan@chromium.org6ff65142012-03-21 09:52:17 +00007108 int parameter_count = arguments_env->parameter_count();
7109 for (int i = 1; i < arguments_env->parameter_count(); i++) {
7110 PushAndAdd(new(zone()) HPushArgument(arguments_env->Lookup(i)));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007111 }
7112
7113 HInvokeFunction* call = new(zone()) HInvokeFunction(
7114 context,
7115 function,
7116 parameter_count);
7117 Drop(parameter_count);
7118 call->set_position(expr->position());
7119 ast_context()->ReturnInstruction(call, expr->id());
7120 return true;
7121 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007122}
7123
7124
7125void HGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007126 ASSERT(!HasStackOverflow());
7127 ASSERT(current_block() != NULL);
7128 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007129 Expression* callee = expr->expression();
7130 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007131 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007132
7133 Property* prop = callee->AsProperty();
7134 if (prop != NULL) {
7135 if (!prop->key()->IsPropertyName()) {
7136 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007137 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007138
danno@chromium.org160a7b02011-04-18 15:51:38 +00007139 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007140 // Push receiver and key like the non-optimized code generator expects it.
7141 HValue* key = Pop();
7142 HValue* receiver = Pop();
7143 Push(key);
7144 Push(receiver);
7145
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007146 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007147
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007148 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007149 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007150 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007151 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007152 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007153 }
7154
7155 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00007156 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007157
7158 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007159
danno@chromium.org160a7b02011-04-18 15:51:38 +00007160 CHECK_ALIVE(VisitForValue(prop->obj()));
7161 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007162
7163 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
7164
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007165 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007166
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007167 HValue* receiver =
7168 environment()->ExpressionStackAt(expr->arguments()->length());
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007169 if (expr->IsMonomorphic()) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007170 Handle<Map> receiver_map = (types == NULL || types->is_empty())
7171 ? Handle<Map>::null()
7172 : types->first();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007173 if (TryInlineBuiltinMethodCall(expr,
7174 receiver,
7175 receiver_map,
7176 expr->check_type())) {
7177 if (FLAG_trace_inlining) {
7178 PrintF("Inlining builtin ");
7179 expr->target()->ShortPrint();
7180 PrintF("\n");
7181 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007182 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007183 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007184
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007185 if (CallStubCompiler::HasCustomCallGenerator(expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007186 expr->check_type() != RECEIVER_MAP_CHECK) {
7187 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007188 // because it is likely to generate better code. Also use the IC
7189 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007190 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007191 call = PreProcessCall(
7192 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007193 } else {
7194 AddCheckConstantFunction(expr, receiver, receiver_map, true);
7195
ulan@chromium.org967e2702012-02-28 09:49:15 +00007196 if (TryInlineCall(expr)) return;
danno@chromium.org160a7b02011-04-18 15:51:38 +00007197 call = PreProcessCall(
7198 new(zone()) HCallConstantFunction(expr->target(),
7199 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007200 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007201 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007202 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007203 HandlePolymorphicCallNamed(expr, receiver, types, name);
7204 return;
7205
7206 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007207 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007208 call = PreProcessCall(
7209 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007210 }
7211
7212 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007213 expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007214 VariableProxy* proxy = expr->expression()->AsVariableProxy();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007215 bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007216
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007217 if (proxy != NULL && proxy->var()->is_possibly_eval()) {
7218 return Bailout("possible direct call to eval");
7219 }
7220
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007221 if (global_call) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007222 Variable* var = proxy->var();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007223 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007224 // If there is a global property cell for the name at compile time and
7225 // access check is not enabled we assume that the function will not change
7226 // and generate optimized code for calling the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007227 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007228 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
7229 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007230 !info()->global_object()->IsAccessCheckNeeded()) {
7231 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007232 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007233 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007234 if (known_global_function) {
7235 // Push the global object instead of the global receiver because
7236 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007237 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007238 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007239 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007240 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007241
danno@chromium.org160a7b02011-04-18 15:51:38 +00007242 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007243 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007244 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007245
7246 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007247 HGlobalReceiver* global_receiver =
7248 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007249 // Index of the receiver from the top of the expression stack.
7250 const int receiver_index = argument_count - 1;
7251 AddInstruction(global_receiver);
7252 ASSERT(environment()->ExpressionStackAt(receiver_index)->
7253 IsGlobalObject());
7254 environment()->SetExpressionStackAt(receiver_index, global_receiver);
7255
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007256 if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
7257 if (FLAG_trace_inlining) {
7258 PrintF("Inlining builtin ");
7259 expr->target()->ShortPrint();
7260 PrintF("\n");
7261 }
7262 return;
7263 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00007264 if (TryInlineCall(expr)) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007265
7266 if (expr->target().is_identical_to(info()->closure())) {
7267 graph()->MarkRecursive();
7268 }
7269
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007270 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00007271 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007272 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007273 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007274 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
7275 AddInstruction(receiver);
7276 PushAndAdd(new(zone()) HPushArgument(receiver));
7277 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007278
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007279 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
7280 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007281 }
7282
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007283 } else if (expr->IsMonomorphic()) {
7284 // The function is on the stack in the unoptimized code during
7285 // evaluation of the arguments.
7286 CHECK_ALIVE(VisitForValue(expr->expression()));
7287 HValue* function = Top();
7288 HValue* context = environment()->LookupContext();
7289 HGlobalObject* global = new(zone()) HGlobalObject(context);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007290 AddInstruction(global);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007291 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007292 PushAndAdd(receiver);
7293 CHECK_ALIVE(VisitExpressions(expr->arguments()));
7294 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007295
7296 if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
7297 if (FLAG_trace_inlining) {
7298 PrintF("Inlining builtin ");
7299 expr->target()->ShortPrint();
7300 PrintF("\n");
7301 }
7302 return;
7303 }
7304
ulan@chromium.org967e2702012-02-28 09:49:15 +00007305 if (TryInlineCall(expr, true)) { // Drop function from environment.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007306 return;
7307 } else {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00007308 call = PreProcessCall(
7309 new(zone()) HInvokeFunction(context,
7310 function,
7311 expr->target(),
7312 argument_count));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007313 Drop(1); // The function.
7314 }
7315
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007316 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00007317 CHECK_ALIVE(VisitForValue(expr->expression()));
7318 HValue* function = Top();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007319 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007320 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007321 AddInstruction(global_object);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007322 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007323 AddInstruction(receiver);
7324 PushAndAdd(new(zone()) HPushArgument(receiver));
7325 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007326
danno@chromium.orgc612e022011-11-10 11:38:15 +00007327 call = new(zone()) HCallFunction(context, function, argument_count);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007328 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007329 }
7330 }
7331
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007332 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007333 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007334}
7335
7336
ulan@chromium.org967e2702012-02-28 09:49:15 +00007337// Checks whether allocation using the given constructor can be inlined.
7338static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
7339 return constructor->has_initial_map() &&
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00007340 constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
7341 constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize;
ulan@chromium.org967e2702012-02-28 09:49:15 +00007342}
7343
7344
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007345void HGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007346 ASSERT(!HasStackOverflow());
7347 ASSERT(current_block() != NULL);
7348 ASSERT(current_block()->HasPredecessor());
ulan@chromium.org967e2702012-02-28 09:49:15 +00007349 expr->RecordTypeFeedback(oracle());
7350 int argument_count = expr->arguments()->length() + 1; // Plus constructor.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007351 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007352
ulan@chromium.org967e2702012-02-28 09:49:15 +00007353 if (FLAG_inline_construct &&
7354 expr->IsMonomorphic() &&
7355 IsAllocationInlineable(expr->target())) {
7356 // The constructor function is on the stack in the unoptimized code
7357 // during evaluation of the arguments.
7358 CHECK_ALIVE(VisitForValue(expr->expression()));
7359 HValue* function = Top();
7360 CHECK_ALIVE(VisitExpressions(expr->arguments()));
7361 Handle<JSFunction> constructor = expr->target();
fschneider@chromium.org35814e52012-03-01 15:43:35 +00007362 HValue* check = AddInstruction(
7363 new(zone()) HCheckFunction(function, constructor));
7364
7365 // Force completion of inobject slack tracking before generating
7366 // allocation code to finalize instance size.
7367 if (constructor->shared()->IsInobjectSlackTrackingInProgress()) {
7368 constructor->shared()->CompleteInobjectSlackTracking();
7369 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00007370
7371 // Replace the constructor function with a newly allocated receiver.
7372 HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
7373 // Index of the receiver from the top of the expression stack.
7374 const int receiver_index = argument_count - 1;
7375 AddInstruction(receiver);
7376 ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
7377 environment()->SetExpressionStackAt(receiver_index, receiver);
7378
7379 if (TryInlineConstruct(expr, receiver)) return;
7380
7381 // TODO(mstarzinger): For now we remove the previous HAllocateObject and
7382 // add HPushArgument for the arguments in case inlining failed. What we
7383 // actually should do is emit HInvokeFunction on the constructor instead
7384 // of using HCallNew as a fallback.
7385 receiver->DeleteAndReplaceWith(NULL);
fschneider@chromium.org35814e52012-03-01 15:43:35 +00007386 check->DeleteAndReplaceWith(NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00007387 environment()->SetExpressionStackAt(receiver_index, function);
7388 HInstruction* call = PreProcessCall(
7389 new(zone()) HCallNew(context, function, argument_count));
7390 call->set_position(expr->position());
7391 return ast_context()->ReturnInstruction(call, expr->id());
7392 } else {
7393 // The constructor function is both an operand to the instruction and an
7394 // argument to the construct call.
7395 HValue* constructor = NULL;
7396 CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
7397 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
7398 HInstruction* call =
7399 new(zone()) HCallNew(context, constructor, argument_count);
7400 Drop(argument_count);
7401 call->set_position(expr->position());
7402 return ast_context()->ReturnInstruction(call, expr->id());
7403 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007404}
7405
7406
7407// Support for generating inlined runtime functions.
7408
7409// Lookup table for generators for runtime calls that are generated inline.
7410// Elements of the table are member pointers to functions of HGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007411#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007412 &HGraphBuilder::Generate##Name,
7413
7414const HGraphBuilder::InlineFunctionGenerator
7415 HGraphBuilder::kInlineFunctionGenerators[] = {
7416 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
7417 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
7418};
7419#undef INLINE_FUNCTION_GENERATOR_ADDRESS
7420
7421
7422void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007423 ASSERT(!HasStackOverflow());
7424 ASSERT(current_block() != NULL);
7425 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007426 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007427 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007428 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007429
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007430 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007431 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007432 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007433 ASSERT(expr->name()->length() > 0);
7434 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007435 // Call to an inline function.
7436 int lookup_index = static_cast<int>(function->function_id) -
7437 static_cast<int>(Runtime::kFirstInlineFunction);
7438 ASSERT(lookup_index >= 0);
7439 ASSERT(static_cast<size_t>(lookup_index) <
7440 ARRAY_SIZE(kInlineFunctionGenerators));
7441 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
7442
7443 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007444 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007445 } else {
7446 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007447 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007448
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007449 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007450 Handle<String> name = expr->name();
7451 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007452 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007453 new(zone()) HCallRuntime(context, name, function, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007454 call->set_position(RelocInfo::kNoPosition);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007455 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007456 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007457 }
7458}
7459
7460
7461void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007462 ASSERT(!HasStackOverflow());
7463 ASSERT(current_block() != NULL);
7464 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007465 switch (expr->op()) {
7466 case Token::DELETE: return VisitDelete(expr);
7467 case Token::VOID: return VisitVoid(expr);
7468 case Token::TYPEOF: return VisitTypeof(expr);
7469 case Token::ADD: return VisitAdd(expr);
7470 case Token::SUB: return VisitSub(expr);
7471 case Token::BIT_NOT: return VisitBitNot(expr);
7472 case Token::NOT: return VisitNot(expr);
7473 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007474 }
7475}
7476
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007477void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
7478 Property* prop = expr->expression()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007479 VariableProxy* proxy = expr->expression()->AsVariableProxy();
7480 if (prop != NULL) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00007481 CHECK_ALIVE(VisitForValue(prop->obj()));
7482 CHECK_ALIVE(VisitForValue(prop->key()));
7483 HValue* key = Pop();
7484 HValue* obj = Pop();
7485 HValue* context = environment()->LookupContext();
7486 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
7487 return ast_context()->ReturnInstruction(instr, expr->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007488 } else if (proxy != NULL) {
7489 Variable* var = proxy->var();
7490 if (var->IsUnallocated()) {
7491 Bailout("delete with global variable");
7492 } else if (var->IsStackAllocated() || var->IsContextSlot()) {
7493 // Result of deleting non-global variables is false. 'this' is not
7494 // really a variable, though we implement it as one. The
7495 // subexpression does not have side effects.
7496 HValue* value = var->is_this()
7497 ? graph()->GetConstantTrue()
7498 : graph()->GetConstantFalse();
7499 return ast_context()->ReturnValue(value);
7500 } else {
7501 Bailout("delete with non-global variable");
7502 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007503 } else {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007504 // Result of deleting non-property, non-variable reference is true.
7505 // Evaluate the subexpression for side effects.
7506 CHECK_ALIVE(VisitForEffect(expr->expression()));
7507 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007508 }
7509}
7510
7511
7512void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
7513 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007514 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007515}
7516
7517
7518void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
7519 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
7520 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007521 HValue* context = environment()->LookupContext();
7522 HInstruction* instr = new(zone()) HTypeof(context, value);
7523 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007524}
7525
7526
7527void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
7528 CHECK_ALIVE(VisitForValue(expr->expression()));
7529 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007530 HValue* context = environment()->LookupContext();
7531 HInstruction* instr =
7532 new(zone()) HMul(context, value, graph_->GetConstant1());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007533 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007534}
7535
7536
7537void HGraphBuilder::VisitSub(UnaryOperation* expr) {
7538 CHECK_ALIVE(VisitForValue(expr->expression()));
7539 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007540 HValue* context = environment()->LookupContext();
7541 HInstruction* instr =
7542 new(zone()) HMul(context, value, graph_->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007543 TypeInfo info = oracle()->UnaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007544 if (info.IsUninitialized()) {
7545 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007546 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007547 info = TypeInfo::Unknown();
7548 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007549 Representation rep = ToRepresentation(info);
7550 TraceRepresentation(expr->op(), info, instr, rep);
7551 instr->AssumeRepresentation(rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007552 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007553}
7554
7555
7556void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
7557 CHECK_ALIVE(VisitForValue(expr->expression()));
7558 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007559 TypeInfo info = oracle()->UnaryType(expr);
7560 if (info.IsUninitialized()) {
7561 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007562 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007563 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007564 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007565 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007566}
7567
7568
7569void HGraphBuilder::VisitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007570 if (ast_context()->IsTest()) {
7571 TestContext* context = TestContext::cast(ast_context());
7572 VisitForControl(expr->expression(),
7573 context->if_false(),
7574 context->if_true());
7575 return;
7576 }
7577
7578 if (ast_context()->IsEffect()) {
7579 VisitForEffect(expr->expression());
7580 return;
7581 }
7582
7583 ASSERT(ast_context()->IsValue());
7584 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
7585 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
7586 CHECK_BAILOUT(VisitForControl(expr->expression(),
7587 materialize_false,
7588 materialize_true));
7589
7590 if (materialize_false->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007591 materialize_false->SetJoinId(expr->MaterializeFalseId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007592 set_current_block(materialize_false);
7593 Push(graph()->GetConstantFalse());
7594 } else {
7595 materialize_false = NULL;
7596 }
7597
7598 if (materialize_true->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007599 materialize_true->SetJoinId(expr->MaterializeTrueId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007600 set_current_block(materialize_true);
7601 Push(graph()->GetConstantTrue());
7602 } else {
7603 materialize_true = NULL;
7604 }
7605
7606 HBasicBlock* join =
7607 CreateJoin(materialize_false, materialize_true, expr->id());
7608 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007609 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007610}
7611
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007612
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007613HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00007614 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007615 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007616 TypeInfo info = oracle()->IncrementType(expr);
7617 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00007618 if (rep.IsTagged()) {
7619 rep = Representation::Integer32();
7620 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007621
7622 if (returns_original_input) {
7623 // We need an explicit HValue representing ToNumber(input). The
7624 // actual HChange instruction we need is (sometimes) added in a later
7625 // phase, so it is not available now to be used as an input to HAdd and
7626 // as the return value.
7627 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
7628 AddInstruction(number_input);
7629 Push(number_input);
7630 }
7631
7632 // The addition has no side effects, so we do not need
7633 // to simulate the expression stack after this instruction.
7634 // Any later failures deopt to the load of the input or earlier.
7635 HConstant* delta = (expr->op() == Token::INC)
7636 ? graph_->GetConstant1()
7637 : graph_->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007638 HValue* context = environment()->LookupContext();
7639 HInstruction* instr = new(zone()) HAdd(context, Top(), delta);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007640 TraceRepresentation(expr->op(), info, instr, rep);
7641 instr->AssumeRepresentation(rep);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007642 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007643 return instr;
7644}
7645
7646
7647void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007648 ASSERT(!HasStackOverflow());
7649 ASSERT(current_block() != NULL);
7650 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007651 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007652 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007653 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007654 if (proxy == NULL && prop == NULL) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007655 return Bailout("invalid lhs in count operation");
7656 }
7657
7658 // Match the full code generator stack by simulating an extra stack
7659 // element for postfix operations in a non-effect context. The return
7660 // value is ToNumber(input).
7661 bool returns_original_input =
7662 expr->is_postfix() && !ast_context()->IsEffect();
7663 HValue* input = NULL; // ToNumber(original_input).
7664 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007665
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007666 if (proxy != NULL) {
7667 Variable* var = proxy->var();
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00007668 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007669 return Bailout("unsupported count operation with const");
7670 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007671 // Argument of the count operation is a variable, not a property.
7672 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00007673 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007674
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007675 after = BuildIncrement(returns_original_input, expr);
7676 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007677 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007678
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007679 switch (var->location()) {
7680 case Variable::UNALLOCATED:
7681 HandleGlobalVariableAssignment(var,
7682 after,
7683 expr->position(),
7684 expr->AssignmentId());
7685 break;
7686
7687 case Variable::PARAMETER:
7688 case Variable::LOCAL:
7689 Bind(var, after);
7690 break;
7691
7692 case Variable::CONTEXT: {
7693 // Bail out if we try to mutate a parameter value in a function
7694 // using the arguments object. We do not (yet) correctly handle the
7695 // arguments property of the function.
7696 if (info()->scope()->arguments() != NULL) {
7697 // Parameters will rewrite to context slots. We have no direct
7698 // way to detect that the variable is a parameter so we use a
7699 // linear search of the parameter list.
7700 int count = info()->scope()->num_parameters();
7701 for (int i = 0; i < count; ++i) {
7702 if (var == info()->scope()->parameter(i)) {
7703 return Bailout("assignment to parameter in arguments object");
7704 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007705 }
7706 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007707
7708 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007709 HStoreContextSlot::Mode mode =
7710 (var->mode() == LET || var->mode() == CONST_HARMONY)
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007711 ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007712 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007713 new(zone()) HStoreContextSlot(context, var->index(), mode, after);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007714 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007715 if (instr->HasObservableSideEffects()) {
7716 AddSimulate(expr->AssignmentId());
7717 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007718 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00007719 }
7720
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007721 case Variable::LOOKUP:
7722 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007723 }
7724
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007725 } else {
7726 // Argument of the count operation is a property.
7727 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007728 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007729
7730 if (prop->key()->IsPropertyName()) {
7731 // Named property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007732 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007733
danno@chromium.org160a7b02011-04-18 15:51:38 +00007734 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007735 HValue* obj = Top();
7736
7737 HInstruction* load = NULL;
7738 if (prop->IsMonomorphic()) {
7739 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
7740 Handle<Map> map = prop->GetReceiverTypes()->first();
7741 load = BuildLoadNamed(obj, prop, map, name);
7742 } else {
7743 load = BuildLoadNamedGeneric(obj, prop);
7744 }
7745 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007746 if (load->HasObservableSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007747
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007748 after = BuildIncrement(returns_original_input, expr);
7749 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007750
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007751 HInstruction* store;
7752 CHECK_ALIVE(store = BuildStoreNamed(obj, after, prop));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007753 AddInstruction(store);
7754
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007755 // Overwrite the receiver in the bailout environment with the result
7756 // of the operation, and the placeholder with the original value if
7757 // necessary.
7758 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007759 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007760 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007761
7762 } else {
7763 // Keyed property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007764 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007765
danno@chromium.org160a7b02011-04-18 15:51:38 +00007766 CHECK_ALIVE(VisitForValue(prop->obj()));
7767 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007768 HValue* obj = environment()->ExpressionStackAt(1);
7769 HValue* key = environment()->ExpressionStackAt(0);
7770
whesse@chromium.org7b260152011-06-20 15:33:18 +00007771 bool has_side_effects = false;
7772 HValue* load = HandleKeyedElementAccess(
7773 obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
7774 false, // is_store
7775 &has_side_effects);
7776 Push(load);
7777 if (has_side_effects) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007778
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007779 after = BuildIncrement(returns_original_input, expr);
7780 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007781
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007782 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00007783 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
7784 RelocInfo::kNoPosition,
7785 true, // is_store
7786 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007787
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007788 // Drop the key from the bailout environment. Overwrite the receiver
7789 // with the result of the operation, and the placeholder with the
7790 // original value if necessary.
7791 Drop(1);
7792 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007793 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007794 ASSERT(has_side_effects); // Stores always have side effects.
7795 AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007796 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007797 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007798
7799 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007800 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007801}
7802
7803
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007804HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context,
7805 HValue* string,
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007806 HValue* index) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007807 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007808 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007809 HStringLength* length = new(zone()) HStringLength(string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007810 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007811 HInstruction* checked_index =
7812 AddInstruction(new(zone()) HBoundsCheck(index, length));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007813 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007814}
7815
7816
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007817HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
7818 HValue* left,
7819 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007820 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00007821 TypeInfo info = oracle()->BinaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007822 if (info.IsUninitialized()) {
7823 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007824 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007825 info = TypeInfo::Unknown();
7826 }
7827 HInstruction* instr = NULL;
7828 switch (expr->op()) {
7829 case Token::ADD:
7830 if (info.IsString()) {
7831 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007832 AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007833 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007834 AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007835 instr = new(zone()) HStringAdd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007836 } else {
erikcorry0ad885c2011-11-21 13:51:57 +00007837 instr = HAdd::NewHAdd(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007838 }
7839 break;
7840 case Token::SUB:
erikcorry0ad885c2011-11-21 13:51:57 +00007841 instr = HSub::NewHSub(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007842 break;
7843 case Token::MUL:
erikcorry0ad885c2011-11-21 13:51:57 +00007844 instr = HMul::NewHMul(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007845 break;
7846 case Token::MOD:
erikcorry0ad885c2011-11-21 13:51:57 +00007847 instr = HMod::NewHMod(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007848 break;
7849 case Token::DIV:
erikcorry0ad885c2011-11-21 13:51:57 +00007850 instr = HDiv::NewHDiv(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007851 break;
7852 case Token::BIT_XOR:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007853 case Token::BIT_AND:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007854 case Token::BIT_OR:
erikcorry0ad885c2011-11-21 13:51:57 +00007855 instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007856 break;
7857 case Token::SAR:
erikcorry0ad885c2011-11-21 13:51:57 +00007858 instr = HSar::NewHSar(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007859 break;
7860 case Token::SHR:
erikcorry0ad885c2011-11-21 13:51:57 +00007861 instr = HShr::NewHShr(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007862 break;
7863 case Token::SHL:
erikcorry0ad885c2011-11-21 13:51:57 +00007864 instr = HShl::NewHShl(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007865 break;
7866 default:
7867 UNREACHABLE();
7868 }
7869
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007870 // If we hit an uninitialized binary op stub we will get type info
7871 // for a smi operation. If one of the operands is a constant string
7872 // do not generate code assuming it is a smi operation.
7873 if (info.IsSmi() &&
7874 ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) ||
7875 (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
7876 return instr;
7877 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007878 Representation rep = ToRepresentation(info);
7879 // We only generate either int32 or generic tagged bitwise operations.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007880 if (instr->IsBitwiseBinaryOperation()) {
7881 HBitwiseBinaryOperation::cast(instr)->
7882 InitializeObservedInputRepresentation(rep);
7883 if (rep.IsDouble()) rep = Representation::Integer32();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007884 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007885 TraceRepresentation(expr->op(), info, instr, rep);
7886 instr->AssumeRepresentation(rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007887 return instr;
7888}
7889
7890
7891// Check for the form (%_ClassOf(foo) === 'BarClass').
7892static bool IsClassOfTest(CompareOperation* expr) {
7893 if (expr->op() != Token::EQ_STRICT) return false;
7894 CallRuntime* call = expr->left()->AsCallRuntime();
7895 if (call == NULL) return false;
7896 Literal* literal = expr->right()->AsLiteral();
7897 if (literal == NULL) return false;
7898 if (!literal->handle()->IsString()) return false;
7899 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
7900 ASSERT(call->arguments()->length() == 1);
7901 return true;
7902}
7903
7904
7905void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007906 ASSERT(!HasStackOverflow());
7907 ASSERT(current_block() != NULL);
7908 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007909 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007910 case Token::COMMA:
7911 return VisitComma(expr);
7912 case Token::OR:
7913 case Token::AND:
7914 return VisitLogicalExpression(expr);
7915 default:
7916 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007917 }
7918}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007919
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007920
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007921void HGraphBuilder::VisitComma(BinaryOperation* expr) {
7922 CHECK_ALIVE(VisitForEffect(expr->left()));
7923 // Visit the right subexpression in the same AST context as the entire
7924 // expression.
7925 Visit(expr->right());
7926}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007927
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007928
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007929void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
7930 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007931 if (ast_context()->IsTest()) {
7932 TestContext* context = TestContext::cast(ast_context());
7933 // Translate left subexpression.
7934 HBasicBlock* eval_right = graph()->CreateBasicBlock();
7935 if (is_logical_and) {
7936 CHECK_BAILOUT(VisitForControl(expr->left(),
7937 eval_right,
7938 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007939 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007940 CHECK_BAILOUT(VisitForControl(expr->left(),
7941 context->if_true(),
7942 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007943 }
7944
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007945 // Translate right subexpression by visiting it in the same AST
7946 // context as the entire expression.
7947 if (eval_right->HasPredecessor()) {
7948 eval_right->SetJoinId(expr->RightId());
7949 set_current_block(eval_right);
7950 Visit(expr->right());
7951 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007952
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007953 } else if (ast_context()->IsValue()) {
7954 CHECK_ALIVE(VisitForValue(expr->left()));
7955 ASSERT(current_block() != NULL);
7956
7957 // We need an extra block to maintain edge-split form.
7958 HBasicBlock* empty_block = graph()->CreateBasicBlock();
7959 HBasicBlock* eval_right = graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00007960 unsigned test_id = expr->left()->test_id();
7961 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007962 HBranch* test = is_logical_and
ricow@chromium.org2c99e282011-07-28 09:15:17 +00007963 ? new(zone()) HBranch(Top(), eval_right, empty_block, expected)
7964 : new(zone()) HBranch(Top(), empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007965 current_block()->Finish(test);
7966
7967 set_current_block(eval_right);
7968 Drop(1); // Value of the left subexpression.
7969 CHECK_BAILOUT(VisitForValue(expr->right()));
7970
7971 HBasicBlock* join_block =
7972 CreateJoin(empty_block, current_block(), expr->id());
7973 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007974 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007975
7976 } else {
7977 ASSERT(ast_context()->IsEffect());
7978 // In an effect context, we don't need the value of the left subexpression,
7979 // only its control flow and side effects. We need an extra block to
7980 // maintain edge-split form.
7981 HBasicBlock* empty_block = graph()->CreateBasicBlock();
7982 HBasicBlock* right_block = graph()->CreateBasicBlock();
7983 if (is_logical_and) {
7984 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
7985 } else {
7986 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
7987 }
7988
7989 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
7990 // actually two empty blocks (one here and one inserted by
7991 // TestContext::BuildBranch, and that they both have an HSimulate though the
7992 // second one is not a merge node, and that we really have no good AST ID to
7993 // put on that first HSimulate.
7994
7995 if (empty_block->HasPredecessor()) {
7996 empty_block->SetJoinId(expr->id());
7997 } else {
7998 empty_block = NULL;
7999 }
8000
8001 if (right_block->HasPredecessor()) {
8002 right_block->SetJoinId(expr->RightId());
8003 set_current_block(right_block);
8004 CHECK_BAILOUT(VisitForEffect(expr->right()));
8005 right_block = current_block();
8006 } else {
8007 right_block = NULL;
8008 }
8009
8010 HBasicBlock* join_block =
8011 CreateJoin(empty_block, right_block, expr->id());
8012 set_current_block(join_block);
8013 // We did not materialize any value in the predecessor environments,
8014 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008015 }
8016}
8017
8018
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00008019void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008020 CHECK_ALIVE(VisitForValue(expr->left()));
8021 CHECK_ALIVE(VisitForValue(expr->right()));
8022 HValue* right = Pop();
8023 HValue* left = Pop();
8024 HInstruction* instr = BuildBinaryOperation(expr, left, right);
8025 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008026 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008027}
8028
8029
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008030void HGraphBuilder::TraceRepresentation(Token::Value op,
8031 TypeInfo info,
8032 HValue* value,
8033 Representation rep) {
8034 if (!FLAG_trace_representation) return;
8035 // TODO(svenpanne) Under which circumstances are we actually not flexible?
8036 // At first glance, this looks a bit weird...
8037 bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
8038 PrintF("Operation %s has type info %s, %schange representation assumption "
8039 "for %s (ID %d) from %s to %s\n",
8040 Token::Name(op),
8041 info.ToString(),
8042 flexible ? "" : " DO NOT ",
8043 value->Mnemonic(),
8044 graph_->GetMaximumValueID(),
8045 value->representation().Mnemonic(),
8046 rep.Mnemonic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008047}
8048
8049
8050Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
8051 if (info.IsSmi()) return Representation::Integer32();
8052 if (info.IsInteger32()) return Representation::Integer32();
8053 if (info.IsDouble()) return Representation::Double();
8054 if (info.IsNumber()) return Representation::Double();
8055 return Representation::Tagged();
8056}
8057
8058
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008059void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008060 HTypeof* typeof_expr,
ager@chromium.org04921a82011-06-27 13:21:41 +00008061 Handle<String> check) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008062 // Note: The HTypeof itself is removed during canonicalization, if possible.
8063 HValue* value = typeof_expr->value();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008064 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
8065 instr->set_position(expr->position());
8066 return ast_context()->ReturnControl(instr, expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00008067}
8068
8069
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008070static bool MatchLiteralCompareNil(HValue* left,
8071 Token::Value op,
8072 HValue* right,
8073 Handle<Object> nil,
8074 HValue** expr) {
8075 if (left->IsConstant() &&
8076 HConstant::cast(left)->handle().is_identical_to(nil) &&
8077 Token::IsEqualityOp(op)) {
8078 *expr = right;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008079 return true;
8080 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008081 return false;
ager@chromium.org04921a82011-06-27 13:21:41 +00008082}
8083
8084
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008085static bool MatchLiteralCompareTypeof(HValue* left,
8086 Token::Value op,
8087 HValue* right,
8088 HTypeof** typeof_expr,
8089 Handle<String>* check) {
8090 if (left->IsTypeof() &&
8091 Token::IsEqualityOp(op) &&
8092 right->IsConstant() &&
8093 HConstant::cast(right)->HasStringValue()) {
8094 *typeof_expr = HTypeof::cast(left);
8095 *check = Handle<String>::cast(HConstant::cast(right)->handle());
8096 return true;
8097 }
8098 return false;
8099}
8100
8101
8102static bool IsLiteralCompareTypeof(HValue* left,
8103 Token::Value op,
8104 HValue* right,
8105 HTypeof** typeof_expr,
8106 Handle<String>* check) {
8107 return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) ||
8108 MatchLiteralCompareTypeof(right, op, left, typeof_expr, check);
8109}
8110
8111
8112static bool IsLiteralCompareNil(HValue* left,
8113 Token::Value op,
8114 HValue* right,
8115 Handle<Object> nil,
8116 HValue** expr) {
8117 return MatchLiteralCompareNil(left, op, right, nil, expr) ||
8118 MatchLiteralCompareNil(right, op, left, nil, expr);
8119}
8120
8121
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00008122static bool IsLiteralCompareBool(HValue* left,
8123 Token::Value op,
8124 HValue* right) {
8125 return op == Token::EQ_STRICT &&
8126 ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) ||
8127 (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean()));
8128}
8129
8130
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008131void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008132 ASSERT(!HasStackOverflow());
8133 ASSERT(current_block() != NULL);
8134 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008135 if (IsClassOfTest(expr)) {
8136 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008137 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008138 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008139 HValue* value = Pop();
8140 Literal* literal = expr->right()->AsLiteral();
8141 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008142 HClassOfTestAndBranch* instr =
8143 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008144 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008145 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008146 }
8147
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008148 TypeInfo type_info = oracle()->CompareType(expr);
8149 // Check if this expression was ever executed according to type feedback.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008150 // Note that for the special typeof/null/undefined cases we get unknown here.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008151 if (type_info.IsUninitialized()) {
8152 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008153 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008154 type_info = TypeInfo::Unknown();
8155 }
8156
danno@chromium.org160a7b02011-04-18 15:51:38 +00008157 CHECK_ALIVE(VisitForValue(expr->left()));
8158 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008159
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008160 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008161 HValue* right = Pop();
8162 HValue* left = Pop();
8163 Token::Value op = expr->op();
8164
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008165 HTypeof* typeof_expr = NULL;
8166 Handle<String> check;
8167 if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) {
8168 return HandleLiteralCompareTypeof(expr, typeof_expr, check);
8169 }
8170 HValue* sub_expr = NULL;
8171 Factory* f = graph()->isolate()->factory();
8172 if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) {
8173 return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
8174 }
8175 if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) {
8176 return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
8177 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00008178 if (IsLiteralCompareBool(left, op, right)) {
8179 HCompareObjectEqAndBranch* result =
8180 new(zone()) HCompareObjectEqAndBranch(left, right);
8181 result->set_position(expr->position());
8182 return ast_context()->ReturnControl(result, expr->id());
8183 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008184
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008185 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008186 // Check to see if the rhs of the instanceof is a global function not
8187 // residing in new space. If it is we assume that the function will stay the
8188 // same.
8189 Handle<JSFunction> target = Handle<JSFunction>::null();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008190 VariableProxy* proxy = expr->right()->AsVariableProxy();
8191 bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008192 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008193 info()->has_global_object() &&
8194 !info()->global_object()->IsAccessCheckNeeded()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008195 Handle<String> name = proxy->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008196 Handle<GlobalObject> global(info()->global_object());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008197 LookupResult lookup(isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008198 global->Lookup(*name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00008199 if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008200 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
8201 // If the function is in new space we assume it's more likely to
8202 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008203 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008204 target = candidate;
8205 }
8206 }
8207 }
8208
8209 // If the target is not null we have found a known global function that is
8210 // assumed to stay the same for this instanceof.
8211 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008212 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
8213 result->set_position(expr->position());
8214 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008215 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008216 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008217 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008218 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008219 result->set_position(expr->position());
8220 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00008221 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008222 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008223 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008224 result->set_position(expr->position());
8225 return ast_context()->ReturnInstruction(result, expr->id());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008226 } else if (type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008227 switch (op) {
8228 case Token::EQ:
8229 case Token::EQ_STRICT: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008230 // Can we get away with map check and not instance type check?
8231 Handle<Map> map = oracle()->GetCompareMap(expr);
8232 if (!map.is_null()) {
8233 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008234 AddInstruction(HCheckMaps::NewWithTransitions(left, map, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008235 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008236 AddInstruction(HCheckMaps::NewWithTransitions(right, map, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008237 HCompareObjectEqAndBranch* result =
8238 new(zone()) HCompareObjectEqAndBranch(left, right);
8239 result->set_position(expr->position());
8240 return ast_context()->ReturnControl(result, expr->id());
8241 } else {
8242 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008243 AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008244 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008245 AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008246 HCompareObjectEqAndBranch* result =
8247 new(zone()) HCompareObjectEqAndBranch(left, right);
8248 result->set_position(expr->position());
8249 return ast_context()->ReturnControl(result, expr->id());
8250 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008251 }
8252 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00008253 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008254 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008255 } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
8256 (op == Token::EQ || op == Token::EQ_STRICT)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00008257 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008258 AddInstruction(HCheckInstanceType::NewIsSymbol(left, zone()));
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00008259 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008260 AddInstruction(HCheckInstanceType::NewIsSymbol(right, zone()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008261 HCompareObjectEqAndBranch* result =
8262 new(zone()) HCompareObjectEqAndBranch(left, right);
8263 result->set_position(expr->position());
8264 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008265 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008266 Representation r = ToRepresentation(type_info);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008267 if (r.IsTagged()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008268 HCompareGeneric* result =
8269 new(zone()) HCompareGeneric(context, left, right, op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008270 result->set_position(expr->position());
8271 return ast_context()->ReturnInstruction(result, expr->id());
8272 } else {
8273 HCompareIDAndBranch* result =
8274 new(zone()) HCompareIDAndBranch(left, right, op);
8275 result->set_position(expr->position());
8276 result->SetInputRepresentation(r);
8277 return ast_context()->ReturnControl(result, expr->id());
8278 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008279 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008280}
8281
8282
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008283void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008284 HValue* value,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008285 NilValue nil) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008286 ASSERT(!HasStackOverflow());
8287 ASSERT(current_block() != NULL);
8288 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008289 EqualityKind kind =
8290 expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
8291 HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
8292 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008293 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008294}
8295
8296
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00008297HInstruction* HGraphBuilder::BuildThisFunction() {
8298 // If we share optimized code between different closures, the
8299 // this-function is not a constant, except inside an inlined body.
8300 if (function_state()->outer() != NULL) {
8301 return new(zone()) HConstant(
8302 function_state()->compilation_info()->closure(),
8303 Representation::Tagged());
8304 } else {
8305 return new(zone()) HThisFunction;
8306 }
8307}
8308
8309
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008310void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008311 ASSERT(!HasStackOverflow());
8312 ASSERT(current_block() != NULL);
8313 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00008314 HInstruction* instr = BuildThisFunction();
8315 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008316}
8317
8318
yangguo@chromium.org56454712012-02-16 15:33:53 +00008319void HGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008320 ASSERT(globals_.is_empty());
8321 AstVisitor::VisitDeclarations(declarations);
8322 if (!globals_.is_empty()) {
yangguo@chromium.org56454712012-02-16 15:33:53 +00008323 Handle<FixedArray> array =
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008324 isolate()->factory()->NewFixedArray(globals_.length(), TENURED);
8325 for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i));
yangguo@chromium.org56454712012-02-16 15:33:53 +00008326 int flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
8327 DeclareGlobalsNativeFlag::encode(info()->is_native()) |
8328 DeclareGlobalsLanguageMode::encode(info()->language_mode());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008329 HInstruction* result = new(zone()) HDeclareGlobals(
8330 environment()->LookupContext(), array, flags);
yangguo@chromium.org56454712012-02-16 15:33:53 +00008331 AddInstruction(result);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008332 globals_.Clear();
yangguo@chromium.org56454712012-02-16 15:33:53 +00008333 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008334}
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008335
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008336
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008337void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* declaration) {
8338 VariableProxy* proxy = declaration->proxy();
8339 VariableMode mode = declaration->mode();
8340 Variable* variable = proxy->var();
8341 bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
8342 switch (variable->location()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008343 case Variable::UNALLOCATED:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008344 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008345 globals_.Add(variable->binding_needs_init()
8346 ? isolate()->factory()->the_hole_value()
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008347 : isolate()->factory()->undefined_value(), zone());
yangguo@chromium.org56454712012-02-16 15:33:53 +00008348 return;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008349 case Variable::PARAMETER:
8350 case Variable::LOCAL:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008351 if (hole_init) {
8352 HValue* value = graph()->GetConstantHole();
8353 environment()->Bind(variable, value);
8354 }
8355 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008356 case Variable::CONTEXT:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008357 if (hole_init) {
8358 HValue* value = graph()->GetConstantHole();
8359 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008360 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008361 context, variable->index(), HStoreContextSlot::kNoCheck, value);
8362 AddInstruction(store);
8363 if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008364 }
8365 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008366 case Variable::LOOKUP:
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008367 return Bailout("unsupported lookup slot in declaration");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008368 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008369}
8370
8371
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008372void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) {
8373 VariableProxy* proxy = declaration->proxy();
8374 Variable* variable = proxy->var();
8375 switch (variable->location()) {
8376 case Variable::UNALLOCATED: {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008377 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008378 Handle<SharedFunctionInfo> function =
8379 Compiler::BuildFunctionInfo(declaration->fun(), info()->script());
8380 // Check for stack-overflow exception.
8381 if (function.is_null()) return SetStackOverflow();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008382 globals_.Add(function, zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008383 return;
8384 }
8385 case Variable::PARAMETER:
8386 case Variable::LOCAL: {
8387 CHECK_ALIVE(VisitForValue(declaration->fun()));
8388 HValue* value = Pop();
8389 environment()->Bind(variable, value);
8390 break;
8391 }
8392 case Variable::CONTEXT: {
8393 CHECK_ALIVE(VisitForValue(declaration->fun()));
8394 HValue* value = Pop();
8395 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008396 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008397 context, variable->index(), HStoreContextSlot::kNoCheck, value);
8398 AddInstruction(store);
8399 if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
8400 break;
8401 }
8402 case Variable::LOOKUP:
8403 return Bailout("unsupported lookup slot in declaration");
8404 }
8405}
8406
8407
8408void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008409 UNREACHABLE();
8410}
8411
8412
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008413void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008414 UNREACHABLE();
8415}
8416
8417
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008418void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00008419 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008420}
8421
8422
8423void HGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008424 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008425}
8426
8427
8428void HGraphBuilder::VisitModuleVariable(ModuleVariable* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008429 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008430}
8431
8432
8433void HGraphBuilder::VisitModulePath(ModulePath* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008434 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008435}
8436
8437
8438void HGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00008439 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008440}
8441
8442
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008443// Generators for inline runtime functions.
8444// Support for types.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008445void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
8446 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008447 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008448 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008449 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
8450 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008451}
8452
8453
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008454void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
8455 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008456 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008457 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008458 HHasInstanceTypeAndBranch* result =
8459 new(zone()) HHasInstanceTypeAndBranch(value,
8460 FIRST_SPEC_OBJECT_TYPE,
8461 LAST_SPEC_OBJECT_TYPE);
8462 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008463}
8464
8465
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008466void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
8467 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008468 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008469 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008470 HHasInstanceTypeAndBranch* result =
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008471 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008472 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008473}
8474
8475
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008476void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
8477 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008478 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008479 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008480 HHasCachedArrayIndexAndBranch* result =
8481 new(zone()) HHasCachedArrayIndexAndBranch(value);
8482 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008483}
8484
8485
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008486void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
8487 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008488 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008489 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008490 HHasInstanceTypeAndBranch* result =
8491 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
8492 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008493}
8494
8495
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008496void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
8497 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008498 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008499 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008500 HHasInstanceTypeAndBranch* result =
8501 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
8502 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008503}
8504
8505
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008506void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
8507 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008508 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008509 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008510 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
8511 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008512}
8513
8514
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008515void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008516 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008517}
8518
8519
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008520void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008521 ASSERT(call->arguments()->length() == 1);
8522 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8523 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008524 HIsUndetectableAndBranch* result =
8525 new(zone()) HIsUndetectableAndBranch(value);
8526 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008527}
8528
8529
8530void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008531 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008532 return Bailout(
8533 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008534}
8535
8536
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008537// Support for construct call checks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008538void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
8539 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008540 if (function_state()->outer() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00008541 // We are generating graph for inlined function.
8542 HValue* value = function_state()->is_construct()
8543 ? graph()->GetConstantTrue()
8544 : graph()->GetConstantFalse();
8545 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008546 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008547 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
8548 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008549 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008550}
8551
8552
8553// Support for arguments.length and arguments[?].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008554void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008555 // Our implementation of arguments (based on this stack frame or an
8556 // adapter below it) does not work for inlined functions. This runtime
8557 // function is blacklisted by AstNode::IsInlineable.
8558 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008559 ASSERT(call->arguments()->length() == 0);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008560 HInstruction* elements = AddInstruction(
8561 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008562 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008563 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008564}
8565
8566
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008567void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008568 // Our implementation of arguments (based on this stack frame or an
8569 // adapter below it) does not work for inlined functions. This runtime
8570 // function is blacklisted by AstNode::IsInlineable.
8571 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008572 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008573 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008574 HValue* index = Pop();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008575 HInstruction* elements = AddInstruction(
8576 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008577 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
8578 HAccessArgumentsAt* result =
8579 new(zone()) HAccessArgumentsAt(elements, length, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008580 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008581}
8582
8583
8584// Support for accessing the class and value fields of an object.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008585void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008586 // The special form detected by IsClassOfTest is detected before we get here
8587 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00008588 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008589}
8590
8591
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008592void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
8593 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008594 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008595 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008596 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008597 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008598}
8599
8600
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00008601void HGraphBuilder::GenerateDateField(CallRuntime* call) {
8602 ASSERT(call->arguments()->length() == 2);
8603 ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
8604 Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
8605 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8606 HValue* date = Pop();
8607 HDateField* result = new(zone()) HDateField(date, index);
8608 return ast_context()->ReturnInstruction(result, call->id());
8609}
8610
8611
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008612void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org2c456792011-11-11 12:00:53 +00008613 ASSERT(call->arguments()->length() == 2);
8614 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8615 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
8616 HValue* value = Pop();
8617 HValue* object = Pop();
8618 // Check if object is a not a smi.
8619 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object);
8620 HBasicBlock* if_smi = graph()->CreateBasicBlock();
8621 HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
8622 HBasicBlock* join = graph()->CreateBasicBlock();
8623 smicheck->SetSuccessorAt(0, if_smi);
8624 smicheck->SetSuccessorAt(1, if_heap_object);
8625 current_block()->Finish(smicheck);
8626 if_smi->Goto(join);
8627
8628 // Check if object is a JSValue.
8629 set_current_block(if_heap_object);
8630 HHasInstanceTypeAndBranch* typecheck =
8631 new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE);
8632 HBasicBlock* if_js_value = graph()->CreateBasicBlock();
8633 HBasicBlock* not_js_value = graph()->CreateBasicBlock();
8634 typecheck->SetSuccessorAt(0, if_js_value);
8635 typecheck->SetSuccessorAt(1, not_js_value);
8636 current_block()->Finish(typecheck);
8637 not_js_value->Goto(join);
8638
8639 // Create in-object property store to kValueOffset.
8640 set_current_block(if_js_value);
8641 Handle<String> name = isolate()->factory()->undefined_symbol();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008642 AddInstruction(new(zone()) HStoreNamedField(object,
8643 name,
8644 value,
8645 true, // in-object store.
8646 JSValue::kValueOffset));
danno@chromium.org2c456792011-11-11 12:00:53 +00008647 if_js_value->Goto(join);
8648 join->SetJoinId(call->id());
8649 set_current_block(join);
8650 return ast_context()->ReturnValue(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008651}
8652
8653
8654// Fast support for charCodeAt(n).
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008655void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
8656 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008657 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8658 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008659 HValue* index = Pop();
8660 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008661 HValue* context = environment()->LookupContext();
8662 HStringCharCodeAt* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008663 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008664}
8665
8666
8667// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008668void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008669 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008670 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008671 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008672 HValue* context = environment()->LookupContext();
8673 HStringCharFromCode* result =
8674 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008675 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008676}
8677
8678
8679// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008680void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008681 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008682 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8683 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008684 HValue* index = Pop();
8685 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008686 HValue* context = environment()->LookupContext();
8687 HStringCharCodeAt* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008688 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008689 HStringCharFromCode* result =
8690 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008691 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008692}
8693
8694
8695// Fast support for object equality testing.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008696void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
8697 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008698 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8699 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008700 HValue* right = Pop();
8701 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008702 HCompareObjectEqAndBranch* result =
8703 new(zone()) HCompareObjectEqAndBranch(left, right);
8704 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008705}
8706
8707
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008708void HGraphBuilder::GenerateLog(CallRuntime* call) {
8709 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008710 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008711}
8712
8713
8714// Fast support for Math.random().
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008715void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008716 HValue* context = environment()->LookupContext();
8717 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
8718 AddInstruction(global_object);
8719 HRandom* result = new(zone()) HRandom(global_object);
8720 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008721}
8722
8723
8724// Fast support for StringAdd.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008725void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
8726 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008727 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008728 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008729 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008730 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008731 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008732}
8733
8734
8735// Fast support for SubString.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008736void HGraphBuilder::GenerateSubString(CallRuntime* call) {
8737 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008738 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008739 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008740 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008741 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008742 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008743}
8744
8745
8746// Fast support for StringCompare.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008747void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
8748 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008749 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008750 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008751 HCallStub* result =
8752 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008753 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008754 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008755}
8756
8757
8758// Support for direct calls from JavaScript to native RegExp code.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008759void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
8760 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008761 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008762 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008763 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008764 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008765 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008766}
8767
8768
8769// Construct a RegExp exec result with two in-object properties.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008770void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
8771 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008772 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008773 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008774 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008775 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008776 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008777 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008778}
8779
8780
8781// Support for fast native caches.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008782void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008783 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008784}
8785
8786
8787// Fast support for number to string.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008788void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
8789 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008790 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008791 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008792 HCallStub* result =
8793 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008794 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008795 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008796}
8797
8798
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008799// Fast call for custom callbacks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008800void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008801 // 1 ~ The function to call is not itself an argument to the call.
8802 int arg_count = call->arguments()->length() - 1;
8803 ASSERT(arg_count >= 1); // There's always at least a receiver.
8804
8805 for (int i = 0; i < arg_count; ++i) {
8806 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
8807 }
8808 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
danno@chromium.orgc612e022011-11-10 11:38:15 +00008809
danno@chromium.org160a7b02011-04-18 15:51:38 +00008810 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008811 HValue* context = environment()->LookupContext();
danno@chromium.orgc612e022011-11-10 11:38:15 +00008812
8813 // Branch for function proxies, or other non-functions.
8814 HHasInstanceTypeAndBranch* typecheck =
8815 new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
8816 HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
8817 HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
8818 HBasicBlock* join = graph()->CreateBasicBlock();
8819 typecheck->SetSuccessorAt(0, if_jsfunction);
8820 typecheck->SetSuccessorAt(1, if_nonfunction);
8821 current_block()->Finish(typecheck);
8822
8823 set_current_block(if_jsfunction);
8824 HInstruction* invoke_result = AddInstruction(
8825 new(zone()) HInvokeFunction(context, function, arg_count));
danno@chromium.org160a7b02011-04-18 15:51:38 +00008826 Drop(arg_count);
danno@chromium.orgc612e022011-11-10 11:38:15 +00008827 Push(invoke_result);
8828 if_jsfunction->Goto(join);
8829
8830 set_current_block(if_nonfunction);
8831 HInstruction* call_result = AddInstruction(
8832 new(zone()) HCallFunction(context, function, arg_count));
8833 Drop(arg_count);
8834 Push(call_result);
8835 if_nonfunction->Goto(join);
8836
8837 set_current_block(join);
8838 join->SetJoinId(call->id());
8839 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008840}
8841
8842
8843// Fast call to math functions.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008844void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
8845 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008846 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
8847 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008848 HValue* right = Pop();
8849 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008850 HPower* result = new(zone()) HPower(left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008851 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008852}
8853
8854
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008855void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
8856 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008857 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008858 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008859 HCallStub* result =
8860 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008861 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008862 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008863 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008864}
8865
8866
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008867void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
8868 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008869 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008870 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008871 HCallStub* result =
8872 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008873 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008874 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008875 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008876}
8877
8878
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00008879void HGraphBuilder::GenerateMathTan(CallRuntime* call) {
8880 ASSERT_EQ(1, call->arguments()->length());
8881 CHECK_ALIVE(VisitArgumentList(call->arguments()));
8882 HValue* context = environment()->LookupContext();
8883 HCallStub* result =
8884 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
8885 result->set_transcendental_type(TranscendentalCache::TAN);
8886 Drop(1);
8887 return ast_context()->ReturnInstruction(result, call->id());
8888}
8889
8890
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008891void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
8892 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008893 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008894 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008895 HCallStub* result =
8896 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008897 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008898 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008899 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008900}
8901
8902
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008903void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008904 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008905}
8906
8907
8908// Check whether two RegExps are equivalent
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008909void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008910 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008911}
8912
8913
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008914void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
8915 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008916 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00008917 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008918 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008919 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008920}
8921
8922
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008923void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008924 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008925}
8926
8927
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008928#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +00008929#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008930
8931
8932HEnvironment::HEnvironment(HEnvironment* outer,
8933 Scope* scope,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008934 Handle<JSFunction> closure,
8935 Zone* zone)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008936 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008937 values_(0, zone),
8938 assigned_variables_(4, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00008939 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008940 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008941 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008942 local_count_(0),
8943 outer_(outer),
8944 pop_count_(0),
8945 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008946 ast_id_(AstNode::kNoNumber),
8947 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008948 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
8949}
8950
8951
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008952HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone)
8953 : values_(0, zone),
8954 assigned_variables_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00008955 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008956 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008957 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008958 local_count_(0),
8959 outer_(NULL),
8960 pop_count_(0),
8961 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008962 ast_id_(other->ast_id()),
8963 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008964 Initialize(other);
8965}
8966
8967
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008968HEnvironment::HEnvironment(HEnvironment* outer,
8969 Handle<JSFunction> closure,
ulan@chromium.org967e2702012-02-28 09:49:15 +00008970 FrameType frame_type,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008971 int arguments,
8972 Zone* zone)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008973 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008974 values_(arguments, zone),
8975 assigned_variables_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +00008976 frame_type_(frame_type),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008977 parameter_count_(arguments),
8978 local_count_(0),
8979 outer_(outer),
8980 pop_count_(0),
8981 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008982 ast_id_(AstNode::kNoNumber),
8983 zone_(zone) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008984}
8985
8986
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008987void HEnvironment::Initialize(int parameter_count,
8988 int local_count,
8989 int stack_height) {
8990 parameter_count_ = parameter_count;
8991 local_count_ = local_count;
8992
8993 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008994 int total = parameter_count + specials_count_ + local_count + stack_height;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008995 values_.Initialize(total + 4, zone());
8996 for (int i = 0; i < total; ++i) values_.Add(NULL, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008997}
8998
8999
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009000void HEnvironment::Initialize(const HEnvironment* other) {
9001 closure_ = other->closure();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009002 values_.AddAll(other->values_, zone());
9003 assigned_variables_.AddAll(other->assigned_variables_, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +00009004 frame_type_ = other->frame_type_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009005 parameter_count_ = other->parameter_count_;
9006 local_count_ = other->local_count_;
9007 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
9008 pop_count_ = other->pop_count_;
9009 push_count_ = other->push_count_;
9010 ast_id_ = other->ast_id_;
9011}
9012
9013
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009014void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
9015 ASSERT(!block->IsLoopHeader());
9016 ASSERT(values_.length() == other->values_.length());
9017
9018 int length = values_.length();
9019 for (int i = 0; i < length; ++i) {
9020 HValue* value = values_[i];
9021 if (value != NULL && value->IsPhi() && value->block() == block) {
9022 // There is already a phi for the i'th value.
9023 HPhi* phi = HPhi::cast(value);
9024 // Assert index is correct and that we haven't missed an incoming edge.
9025 ASSERT(phi->merged_index() == i);
9026 ASSERT(phi->OperandCount() == block->predecessors()->length());
9027 phi->AddInput(other->values_[i]);
9028 } else if (values_[i] != other->values_[i]) {
9029 // There is a fresh value on the incoming edge, a phi is needed.
9030 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009031 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009032 HValue* old_value = values_[i];
9033 for (int j = 0; j < block->predecessors()->length(); j++) {
9034 phi->AddInput(old_value);
9035 }
9036 phi->AddInput(other->values_[i]);
9037 this->values_[i] = phi;
9038 block->AddPhi(phi);
9039 }
9040 }
9041}
9042
9043
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009044void HEnvironment::Bind(int index, HValue* value) {
9045 ASSERT(value != NULL);
9046 if (!assigned_variables_.Contains(index)) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009047 assigned_variables_.Add(index, zone());
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009048 }
9049 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009050}
9051
9052
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009053bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009054 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009055}
9056
9057
9058bool HEnvironment::ExpressionStackIsEmpty() const {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00009059 ASSERT(length() >= first_expression_index());
9060 return length() == first_expression_index();
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009061}
9062
9063
9064void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
9065 int count = index_from_top + 1;
9066 int index = values_.length() - count;
9067 ASSERT(HasExpressionAt(index));
9068 // The push count must include at least the element in question or else
9069 // the new value will not be included in this environment's history.
9070 if (push_count_ < count) {
9071 // This is the same effect as popping then re-pushing 'count' elements.
9072 pop_count_ += (count - push_count_);
9073 push_count_ = count;
9074 }
9075 values_[index] = value;
9076}
9077
9078
9079void HEnvironment::Drop(int count) {
9080 for (int i = 0; i < count; ++i) {
9081 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009082 }
9083}
9084
9085
9086HEnvironment* HEnvironment::Copy() const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009087 return new(zone()) HEnvironment(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009088}
9089
9090
9091HEnvironment* HEnvironment::CopyWithoutHistory() const {
9092 HEnvironment* result = Copy();
9093 result->ClearHistory();
9094 return result;
9095}
9096
9097
9098HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
9099 HEnvironment* new_env = Copy();
9100 for (int i = 0; i < values_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009101 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009102 phi->AddInput(values_[i]);
9103 new_env->values_[i] = phi;
9104 loop_header->AddPhi(phi);
9105 }
9106 new_env->ClearHistory();
9107 return new_env;
9108}
9109
9110
ulan@chromium.org967e2702012-02-28 09:49:15 +00009111HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
9112 Handle<JSFunction> target,
9113 FrameType frame_type,
9114 int arguments) const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009115 HEnvironment* new_env =
9116 new(zone()) HEnvironment(outer, target, frame_type,
9117 arguments + 1, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +00009118 for (int i = 0; i <= arguments; ++i) { // Include receiver.
9119 new_env->Push(ExpressionStackAt(arguments - i));
9120 }
9121 new_env->ClearHistory();
9122 return new_env;
9123}
9124
9125
danno@chromium.org40cb8782011-05-25 07:58:50 +00009126HEnvironment* HEnvironment::CopyForInlining(
9127 Handle<JSFunction> target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009128 int arguments,
danno@chromium.org40cb8782011-05-25 07:58:50 +00009129 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00009130 HConstant* undefined,
ulan@chromium.org967e2702012-02-28 09:49:15 +00009131 CallKind call_kind,
9132 bool is_construct) const {
9133 ASSERT(frame_type() == JS_FUNCTION);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009134
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009135 // Outer environment is a copy of this one without the arguments.
9136 int arity = function->scope()->num_parameters();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009137
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009138 HEnvironment* outer = Copy();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009139 outer->Drop(arguments + 1); // Including receiver.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009140 outer->ClearHistory();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009141
ulan@chromium.org967e2702012-02-28 09:49:15 +00009142 if (is_construct) {
9143 // Create artificial constructor stub environment. The receiver should
9144 // actually be the constructor function, but we pass the newly allocated
9145 // object instead, DoComputeConstructStubFrame() relies on that.
9146 outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
9147 }
9148
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009149 if (arity != arguments) {
9150 // Create artificial arguments adaptation environment.
ulan@chromium.org967e2702012-02-28 09:49:15 +00009151 outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009152 }
9153
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009154 HEnvironment* inner =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00009155 new(zone()) HEnvironment(outer, function->scope(), target, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009156 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009157 for (int i = 0; i <= arity; ++i) { // Include receiver.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009158 HValue* push = (i <= arguments) ?
9159 ExpressionStackAt(arguments - i) : undefined;
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009160 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009161 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00009162 // If the function we are inlining is a strict mode function or a
9163 // builtin function, pass undefined as the receiver for function
9164 // calls (instead of the global receiver).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00009165 if ((target->shared()->native() || !function->is_classic_mode()) &&
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00009166 call_kind == CALL_AS_FUNCTION && !is_construct) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00009167 inner->SetValueAt(0, undefined);
9168 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009169 inner->SetValueAt(arity + 1, LookupContext());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009170 for (int i = arity + 2; i < inner->length(); ++i) {
9171 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009172 }
9173
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00009174 inner->set_ast_id(AstNode::kFunctionEntryId);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009175 return inner;
9176}
9177
9178
9179void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00009180 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009181 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009182 if (i == parameter_count()) stream->Add("specials\n");
9183 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
9184 if (i == parameter_count() + specials_count() + local_count()) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009185 stream->Add("expressions\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009186 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009187 HValue* val = values_.at(i);
9188 stream->Add("%d: ", i);
9189 if (val != NULL) {
9190 val->PrintNameTo(stream);
9191 } else {
9192 stream->Add("NULL");
9193 }
9194 stream->Add("\n");
9195 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00009196 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009197}
9198
9199
9200void HEnvironment::PrintToStd() {
9201 HeapStringAllocator string_allocator;
9202 StringStream trace(&string_allocator);
9203 PrintTo(&trace);
9204 PrintF("%s", *trace.ToCString());
9205}
9206
9207
9208void HTracer::TraceCompilation(FunctionLiteral* function) {
9209 Tag tag(this, "compilation");
9210 Handle<String> name = function->debug_name();
9211 PrintStringProperty("name", *name->ToCString());
9212 PrintStringProperty("method", *name->ToCString());
9213 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
9214}
9215
9216
9217void HTracer::TraceLithium(const char* name, LChunk* chunk) {
9218 Trace(name, chunk->graph(), chunk);
9219}
9220
9221
9222void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
9223 Trace(name, graph, NULL);
9224}
9225
9226
9227void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
9228 Tag tag(this, "cfg");
9229 PrintStringProperty("name", name);
9230 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
9231 for (int i = 0; i < blocks->length(); i++) {
9232 HBasicBlock* current = blocks->at(i);
9233 Tag block_tag(this, "block");
9234 PrintBlockProperty("name", current->block_id());
9235 PrintIntProperty("from_bci", -1);
9236 PrintIntProperty("to_bci", -1);
9237
9238 if (!current->predecessors()->is_empty()) {
9239 PrintIndent();
9240 trace_.Add("predecessors");
9241 for (int j = 0; j < current->predecessors()->length(); ++j) {
9242 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
9243 }
9244 trace_.Add("\n");
9245 } else {
9246 PrintEmptyProperty("predecessors");
9247 }
9248
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00009249 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009250 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00009251 } else {
9252 PrintIndent();
9253 trace_.Add("successors");
9254 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
9255 trace_.Add(" \"B%d\"", it.Current()->block_id());
9256 }
9257 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009258 }
9259
9260 PrintEmptyProperty("xhandlers");
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009261 const char* flags = current->IsLoopSuccessorDominator()
9262 ? "dom-loop-succ"
9263 : "";
9264 PrintStringProperty("flags", flags);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009265
9266 if (current->dominator() != NULL) {
9267 PrintBlockProperty("dominator", current->dominator()->block_id());
9268 }
9269
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00009270 PrintIntProperty("loop_depth", current->LoopNestingDepth());
9271
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009272 if (chunk != NULL) {
9273 int first_index = current->first_instruction_index();
9274 int last_index = current->last_instruction_index();
9275 PrintIntProperty(
9276 "first_lir_id",
9277 LifetimePosition::FromInstructionIndex(first_index).Value());
9278 PrintIntProperty(
9279 "last_lir_id",
9280 LifetimePosition::FromInstructionIndex(last_index).Value());
9281 }
9282
9283 {
9284 Tag states_tag(this, "states");
9285 Tag locals_tag(this, "locals");
9286 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009287 PrintIntProperty("size", current->phis()->length());
9288 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009289 for (int j = 0; j < total; ++j) {
9290 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009291 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009292 trace_.Add("%d ", phi->merged_index());
9293 phi->PrintNameTo(&trace_);
9294 trace_.Add(" ");
9295 phi->PrintTo(&trace_);
9296 trace_.Add("\n");
9297 }
9298 }
9299
9300 {
9301 Tag HIR_tag(this, "HIR");
9302 HInstruction* instruction = current->first();
9303 while (instruction != NULL) {
9304 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009305 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009306 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009307 trace_.Add("%d %d ", bci, uses);
9308 instruction->PrintNameTo(&trace_);
9309 trace_.Add(" ");
9310 instruction->PrintTo(&trace_);
9311 trace_.Add(" <|@\n");
9312 instruction = instruction->next();
9313 }
9314 }
9315
9316
9317 if (chunk != NULL) {
9318 Tag LIR_tag(this, "LIR");
9319 int first_index = current->first_instruction_index();
9320 int last_index = current->last_instruction_index();
9321 if (first_index != -1 && last_index != -1) {
9322 const ZoneList<LInstruction*>* instructions = chunk->instructions();
9323 for (int i = first_index; i <= last_index; ++i) {
9324 LInstruction* linstr = instructions->at(i);
9325 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009326 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009327 trace_.Add("%d ",
9328 LifetimePosition::FromInstructionIndex(i).Value());
9329 linstr->PrintTo(&trace_);
9330 trace_.Add(" <|@\n");
9331 }
9332 }
9333 }
9334 }
9335 }
9336}
9337
9338
9339void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
9340 Tag tag(this, "intervals");
9341 PrintStringProperty("name", name);
9342
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009343 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009344 for (int i = 0; i < fixed_d->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009345 TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009346 }
9347
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009348 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009349 for (int i = 0; i < fixed->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009350 TraceLiveRange(fixed->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009351 }
9352
9353 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
9354 for (int i = 0; i < live_ranges->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009355 TraceLiveRange(live_ranges->at(i), "object", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009356 }
9357}
9358
9359
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009360void HTracer::TraceLiveRange(LiveRange* range, const char* type,
9361 Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009362 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009363 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009364 trace_.Add("%d %s", range->id(), type);
9365 if (range->HasRegisterAssigned()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009366 LOperand* op = range->CreateAssignedOperand(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009367 int assigned_reg = op->index();
9368 if (op->IsDoubleRegister()) {
9369 trace_.Add(" \"%s\"",
9370 DoubleRegister::AllocationIndexToString(assigned_reg));
9371 } else {
9372 ASSERT(op->IsRegister());
9373 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
9374 }
9375 } else if (range->IsSpilled()) {
9376 LOperand* op = range->TopLevel()->GetSpillOperand();
9377 if (op->IsDoubleStackSlot()) {
9378 trace_.Add(" \"double_stack:%d\"", op->index());
9379 } else {
9380 ASSERT(op->IsStackSlot());
9381 trace_.Add(" \"stack:%d\"", op->index());
9382 }
9383 }
9384 int parent_index = -1;
9385 if (range->IsChild()) {
9386 parent_index = range->parent()->id();
9387 } else {
9388 parent_index = range->id();
9389 }
9390 LOperand* op = range->FirstHint();
9391 int hint_index = -1;
danno@chromium.orgfa458e42012-02-01 10:48:36 +00009392 if (op != NULL && op->IsUnallocated()) {
9393 hint_index = LUnallocated::cast(op)->virtual_register();
9394 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009395 trace_.Add(" %d %d", parent_index, hint_index);
9396 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009397 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009398 trace_.Add(" [%d, %d[",
9399 cur_interval->start().Value(),
9400 cur_interval->end().Value());
9401 cur_interval = cur_interval->next();
9402 }
9403
9404 UsePosition* current_pos = range->first_pos();
9405 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009406 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009407 trace_.Add(" %d M", current_pos->pos().Value());
9408 }
9409 current_pos = current_pos->next();
9410 }
9411
9412 trace_.Add(" \"\"\n");
9413 }
9414}
9415
9416
9417void HTracer::FlushToFile() {
9418 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
9419 trace_.Reset();
9420}
9421
9422
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009423void HStatistics::Initialize(CompilationInfo* info) {
9424 source_size_ += info->shared_info()->SourceSize();
9425}
9426
9427
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009428void HStatistics::Print() {
9429 PrintF("Timing results:\n");
9430 int64_t sum = 0;
9431 for (int i = 0; i < timing_.length(); ++i) {
9432 sum += timing_[i];
9433 }
9434
9435 for (int i = 0; i < names_.length(); ++i) {
9436 PrintF("%30s", names_[i]);
9437 double ms = static_cast<double>(timing_[i]) / 1000;
9438 double percent = static_cast<double>(timing_[i]) * 100 / sum;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009439 PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
9440
9441 unsigned size = sizes_[i];
9442 double size_percent = static_cast<double>(size) * 100 / total_size_;
9443 PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009444 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009445 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00009446 double normalized_time = source_size_in_kb > 0
9447 ? (static_cast<double>(sum) / 1000) / source_size_in_kb
9448 : 0;
9449 double normalized_bytes = source_size_in_kb > 0
9450 ? total_size_ / source_size_in_kb
9451 : 0;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009452 PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00009453 normalized_time, normalized_bytes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009454 PrintF("---------------------------------------------------------------\n");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009455 PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009456 "Total",
9457 static_cast<double>(total_) / 1000,
9458 static_cast<double>(total_) / full_code_gen_);
9459}
9460
9461
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009462void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009463 if (name == HPhase::kFullCodeGen) {
9464 full_code_gen_ += ticks;
9465 } else if (name == HPhase::kTotal) {
9466 total_ += ticks;
9467 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009468 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009469 for (int i = 0; i < names_.length(); ++i) {
9470 if (names_[i] == name) {
9471 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009472 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009473 return;
9474 }
9475 }
9476 names_.Add(name);
9477 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009478 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009479 }
9480}
9481
9482
9483const char* const HPhase::kFullCodeGen = "Full code generator";
9484const char* const HPhase::kTotal = "Total";
9485
9486
9487void HPhase::Begin(const char* name,
9488 HGraph* graph,
9489 LChunk* chunk,
9490 LAllocator* allocator) {
9491 name_ = name;
9492 graph_ = graph;
9493 chunk_ = chunk;
9494 allocator_ = allocator;
9495 if (allocator != NULL && chunk_ == NULL) {
9496 chunk_ = allocator->chunk();
9497 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009498 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009499 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009500}
9501
9502
9503void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009504 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009505 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009506 unsigned size = Zone::allocation_size_ - start_allocation_size_;
9507 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009508 }
9509
fschneider@chromium.org35814e52012-03-01 15:43:35 +00009510 // Produce trace output if flag is set so that the first letter of the
9511 // phase name matches the command line parameter FLAG_trace_phase.
9512 if (FLAG_trace_hydrogen &&
9513 OS::StrChr(const_cast<char*>(FLAG_trace_phase), name_[0]) != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009514 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
9515 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
9516 if (allocator_ != NULL) {
9517 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
9518 }
9519 }
9520
9521#ifdef DEBUG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009522 if (graph_ != NULL) graph_->Verify(false); // No full verify.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009523 if (allocator_ != NULL) allocator_->Verify();
9524#endif
9525}
9526
9527} } // namespace v8::internal