blob: 5c573feb198c9cddc227186db482388bad94bdb7 [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
28#include "hydrogen.h"
29
danno@chromium.orgca29dd82013-04-26 11:59:48 +000030#include <algorithm>
31
32#include "v8.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000033#include "codegen.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000034#include "full-codegen.h"
35#include "hashmap.h"
36#include "lithium-allocator.h"
37#include "parser.h"
ricow@chromium.org4f693d62011-07-04 14:01:31 +000038#include "scopeinfo.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000039#include "scopes.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000040#include "stub-cache.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000041
42#if V8_TARGET_ARCH_IA32
43#include "ia32/lithium-codegen-ia32.h"
44#elif V8_TARGET_ARCH_X64
45#include "x64/lithium-codegen-x64.h"
46#elif V8_TARGET_ARCH_ARM
47#include "arm/lithium-codegen-arm.h"
lrn@chromium.org7516f052011-03-30 08:52:27 +000048#elif V8_TARGET_ARCH_MIPS
49#include "mips/lithium-codegen-mips.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000050#else
51#error Unsupported target architecture.
52#endif
53
54namespace v8 {
55namespace internal {
56
57HBasicBlock::HBasicBlock(HGraph* graph)
58 : block_id_(graph->GetNextBlockID()),
59 graph_(graph),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000060 phis_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000061 first_(NULL),
62 last_(NULL),
63 end_(NULL),
64 loop_information_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000065 predecessors_(2, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000066 dominator_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000067 dominated_blocks_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000068 last_environment_(NULL),
69 argument_count_(-1),
70 first_instruction_index_(-1),
71 last_instruction_index_(-1),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000072 deleted_phis_(4, graph->zone()),
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000073 parent_loop_header_(NULL),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000074 is_inline_return_target_(false),
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000075 is_deoptimizing_(false),
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +000076 dominates_loop_successors_(false),
77 is_osr_entry_(false) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000078
79
ulan@chromium.org750145a2013-03-07 15:14:13 +000080Isolate* HBasicBlock::isolate() const {
81 return graph_->isolate();
82}
83
84
kasperl@chromium.orga5551262010-12-07 12:49:48 +000085void HBasicBlock::AttachLoopInformation() {
86 ASSERT(!IsLoopHeader());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000087 loop_information_ = new(zone()) HLoopInformation(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000088}
89
90
91void HBasicBlock::DetachLoopInformation() {
92 ASSERT(IsLoopHeader());
93 loop_information_ = NULL;
94}
95
96
97void HBasicBlock::AddPhi(HPhi* phi) {
98 ASSERT(!IsStartBlock());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000099 phis_.Add(phi, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000100 phi->SetBlock(this);
101}
102
103
104void HBasicBlock::RemovePhi(HPhi* phi) {
105 ASSERT(phi->block() == this);
106 ASSERT(phis_.Contains(phi));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000107 ASSERT(phi->HasNoUses() || !phi->is_live());
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000108 phi->Kill();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000109 phis_.RemoveElement(phi);
110 phi->SetBlock(NULL);
111}
112
113
114void HBasicBlock::AddInstruction(HInstruction* instr) {
115 ASSERT(!IsStartBlock() || !IsFinished());
116 ASSERT(!instr->IsLinked());
117 ASSERT(!IsFinished());
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000118
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000119 if (first_ == NULL) {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000120 ASSERT(last_environment() != NULL);
121 ASSERT(!last_environment()->ast_id().IsNone());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000122 HBlockEntry* entry = new(zone()) HBlockEntry();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000123 entry->InitializeAsFirst(this);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000124 first_ = last_ = entry;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000125 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000126 instr->InsertAfter(last_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000127}
128
129
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000130HDeoptimize* HBasicBlock::CreateDeoptimize(
131 HDeoptimize::UseEnvironment has_uses) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000132 ASSERT(HasEnvironment());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000133 if (has_uses == HDeoptimize::kNoUses)
134 return new(zone()) HDeoptimize(0, zone());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000135
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000136 HEnvironment* environment = last_environment();
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000137 HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000138 for (int i = 0; i < environment->length(); i++) {
139 HValue* val = environment->values()->at(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000140 instr->AddEnvironmentValue(val, zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000141 }
142
143 return instr;
144}
145
146
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000147HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
148 RemovableSimulate removable) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000149 ASSERT(HasEnvironment());
150 HEnvironment* environment = last_environment();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000151 ASSERT(ast_id.IsNone() ||
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000152 ast_id == BailoutId::StubEntry() ||
ager@chromium.org04921a82011-06-27 13:21:41 +0000153 environment->closure()->shared()->VerifyBailoutId(ast_id));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000154
155 int push_count = environment->push_count();
156 int pop_count = environment->pop_count();
157
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000158 HSimulate* instr =
159 new(zone()) HSimulate(ast_id, pop_count, zone(), removable);
160 // Order of pushed values: newest (top of stack) first. This allows
161 // HSimulate::MergeInto() to easily append additional pushed values
162 // that are older (from further down the stack).
163 for (int i = 0; i < push_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000164 instr->AddPushedValue(environment->ExpressionStackAt(i));
165 }
jkummerow@chromium.org59297c72013-01-09 16:32:23 +0000166 for (GrowableBitVector::Iterator it(environment->assigned_variables(),
167 zone());
168 !it.Done();
169 it.Advance()) {
170 int index = it.Current();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000171 instr->AddAssignedValue(index, environment->Lookup(index));
172 }
173 environment->ClearHistory();
174 return instr;
175}
176
177
178void HBasicBlock::Finish(HControlInstruction* end) {
179 ASSERT(!IsFinished());
180 AddInstruction(end);
181 end_ = end;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000182 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
183 it.Current()->RegisterPredecessor(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000184 }
185}
186
187
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000188void HBasicBlock::Goto(HBasicBlock* block,
189 FunctionState* state,
190 bool add_simulate) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000191 bool drop_extra = state != NULL &&
192 state->inlining_kind() == DROP_EXTRA_ON_RETURN;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000193
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000194 if (block->IsInlineReturnTarget()) {
verwaest@chromium.org33e09c82012-10-10 17:07:22 +0000195 AddInstruction(new(zone()) HLeaveInlined());
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000196 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000197 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000198
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000199 if (add_simulate) AddSimulate(BailoutId::None());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000200 HGoto* instr = new(zone()) HGoto(block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000201 Finish(instr);
202}
203
204
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000205void HBasicBlock::AddLeaveInlined(HValue* return_value,
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000206 FunctionState* state) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000207 HBasicBlock* target = state->function_return();
208 bool drop_extra = state->inlining_kind() == DROP_EXTRA_ON_RETURN;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000209
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000210 ASSERT(target->IsInlineReturnTarget());
211 ASSERT(return_value != NULL);
verwaest@chromium.org33e09c82012-10-10 17:07:22 +0000212 AddInstruction(new(zone()) HLeaveInlined());
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000213 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000214 last_environment()->Push(return_value);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000215 AddSimulate(BailoutId::None());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000216 HGoto* instr = new(zone()) HGoto(target);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000217 Finish(instr);
218}
219
220
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000221void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000222 ASSERT(!HasEnvironment());
223 ASSERT(first() == NULL);
224 UpdateEnvironment(env);
225}
226
227
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000228void HBasicBlock::SetJoinId(BailoutId ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000229 int length = predecessors_.length();
230 ASSERT(length > 0);
231 for (int i = 0; i < length; i++) {
232 HBasicBlock* predecessor = predecessors_[i];
233 ASSERT(predecessor->end()->IsGoto());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000234 HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000235 ASSERT(i != 0 ||
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000236 (predecessor->last_environment()->closure().is_null() ||
237 predecessor->last_environment()->closure()->shared()
238 ->VerifyBailoutId(ast_id)));
ager@chromium.org04921a82011-06-27 13:21:41 +0000239 simulate->set_ast_id(ast_id);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000240 predecessor->last_environment()->set_ast_id(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000241 }
242}
243
244
245bool HBasicBlock::Dominates(HBasicBlock* other) const {
246 HBasicBlock* current = other->dominator();
247 while (current != NULL) {
248 if (current == this) return true;
249 current = current->dominator();
250 }
251 return false;
252}
253
254
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000255int HBasicBlock::LoopNestingDepth() const {
256 const HBasicBlock* current = this;
257 int result = (current->IsLoopHeader()) ? 1 : 0;
258 while (current->parent_loop_header() != NULL) {
259 current = current->parent_loop_header();
260 result++;
261 }
262 return result;
263}
264
265
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000266void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
267 ASSERT(IsLoopHeader());
268
269 SetJoinId(stmt->EntryId());
270 if (predecessors()->length() == 1) {
271 // This is a degenerated loop.
272 DetachLoopInformation();
273 return;
274 }
275
276 // Only the first entry into the loop is from outside the loop. All other
277 // entries must be back edges.
278 for (int i = 1; i < predecessors()->length(); ++i) {
279 loop_information()->RegisterBackEdge(predecessors()->at(i));
280 }
281}
282
283
284void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
danno@chromium.org160a7b02011-04-18 15:51:38 +0000285 if (HasPredecessor()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000286 // Only loop header blocks can have a predecessor added after
287 // instructions have been added to the block (they have phis for all
288 // values in the environment, these phis may be eliminated later).
289 ASSERT(IsLoopHeader() || first_ == NULL);
290 HEnvironment* incoming_env = pred->last_environment();
291 if (IsLoopHeader()) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000292 ASSERT(phis()->length() == incoming_env->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000293 for (int i = 0; i < phis_.length(); ++i) {
294 phis_[i]->AddInput(incoming_env->values()->at(i));
295 }
296 } else {
297 last_environment()->AddIncomingEdge(this, pred->last_environment());
298 }
299 } else if (!HasEnvironment() && !IsFinished()) {
300 ASSERT(!IsLoopHeader());
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000301 SetInitialEnvironment(pred->last_environment()->Copy());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000302 }
303
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000304 predecessors_.Add(pred, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000305}
306
307
308void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
309 ASSERT(!dominated_blocks_.Contains(block));
310 // Keep the list of dominated blocks sorted such that if there is two
311 // succeeding block in this list, the predecessor is before the successor.
312 int index = 0;
313 while (index < dominated_blocks_.length() &&
314 dominated_blocks_[index]->block_id() < block->block_id()) {
315 ++index;
316 }
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000317 dominated_blocks_.InsertAt(index, block, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000318}
319
320
321void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
322 if (dominator_ == NULL) {
323 dominator_ = other;
324 other->AddDominatedBlock(this);
325 } else if (other->dominator() != NULL) {
326 HBasicBlock* first = dominator_;
327 HBasicBlock* second = other;
328
329 while (first != second) {
330 if (first->block_id() > second->block_id()) {
331 first = first->dominator();
332 } else {
333 second = second->dominator();
334 }
335 ASSERT(first != NULL && second != NULL);
336 }
337
338 if (dominator_ != first) {
339 ASSERT(dominator_->dominated_blocks_.Contains(this));
340 dominator_->dominated_blocks_.RemoveElement(this);
341 dominator_ = first;
342 first->AddDominatedBlock(this);
343 }
344 }
345}
346
347
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000348void HBasicBlock::AssignLoopSuccessorDominators() {
349 // Mark blocks that dominate all subsequent reachable blocks inside their
350 // loop. Exploit the fact that blocks are sorted in reverse post order. When
351 // the loop is visited in increasing block id order, if the number of
352 // non-loop-exiting successor edges at the dominator_candidate block doesn't
353 // exceed the number of previously encountered predecessor edges, there is no
354 // path from the loop header to any block with higher id that doesn't go
355 // through the dominator_candidate block. In this case, the
356 // dominator_candidate block is guaranteed to dominate all blocks reachable
357 // from it with higher ids.
358 HBasicBlock* last = loop_information()->GetLastBackEdge();
359 int outstanding_successors = 1; // one edge from the pre-header
360 // Header always dominates everything.
361 MarkAsLoopSuccessorDominator();
362 for (int j = block_id(); j <= last->block_id(); ++j) {
363 HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
364 for (HPredecessorIterator it(dominator_candidate); !it.Done();
365 it.Advance()) {
366 HBasicBlock* predecessor = it.Current();
367 // Don't count back edges.
368 if (predecessor->block_id() < dominator_candidate->block_id()) {
369 outstanding_successors--;
370 }
371 }
372
373 // If more successors than predecessors have been seen in the loop up to
374 // now, it's not possible to guarantee that the current block dominates
375 // all of the blocks with higher IDs. In this case, assume conservatively
376 // that those paths through loop that don't go through the current block
377 // contain all of the loop's dependencies. Also be careful to record
378 // dominator information about the current loop that's being processed,
379 // and not nested loops, which will be processed when
380 // AssignLoopSuccessorDominators gets called on their header.
381 ASSERT(outstanding_successors >= 0);
382 HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header();
383 if (outstanding_successors == 0 &&
384 (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) {
385 dominator_candidate->MarkAsLoopSuccessorDominator();
386 }
387 HControlInstruction* end = dominator_candidate->end();
388 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
389 HBasicBlock* successor = it.Current();
390 // Only count successors that remain inside the loop and don't loop back
391 // to a loop header.
392 if (successor->block_id() > dominator_candidate->block_id() &&
393 successor->block_id() <= last->block_id()) {
394 // Backwards edges must land on loop headers.
395 ASSERT(successor->block_id() > dominator_candidate->block_id() ||
396 successor->IsLoopHeader());
397 outstanding_successors++;
398 }
399 }
400 }
401}
402
403
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000404int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
405 for (int i = 0; i < predecessors_.length(); ++i) {
406 if (predecessors_[i] == predecessor) return i;
407 }
408 UNREACHABLE();
409 return -1;
410}
411
412
413#ifdef DEBUG
414void HBasicBlock::Verify() {
415 // Check that every block is finished.
416 ASSERT(IsFinished());
417 ASSERT(block_id() >= 0);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000418
419 // Check that the incoming edges are in edge split form.
420 if (predecessors_.length() > 1) {
421 for (int i = 0; i < predecessors_.length(); ++i) {
422 ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
423 }
424 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000425}
426#endif
427
428
429void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000430 this->back_edges_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000431 AddBlock(block);
432}
433
434
435HBasicBlock* HLoopInformation::GetLastBackEdge() const {
436 int max_id = -1;
437 HBasicBlock* result = NULL;
438 for (int i = 0; i < back_edges_.length(); ++i) {
439 HBasicBlock* cur = back_edges_[i];
440 if (cur->block_id() > max_id) {
441 max_id = cur->block_id();
442 result = cur;
443 }
444 }
445 return result;
446}
447
448
449void HLoopInformation::AddBlock(HBasicBlock* block) {
450 if (block == loop_header()) return;
451 if (block->parent_loop_header() == loop_header()) return;
452 if (block->parent_loop_header() != NULL) {
453 AddBlock(block->parent_loop_header());
454 } else {
455 block->set_parent_loop_header(loop_header());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000456 blocks_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000457 for (int i = 0; i < block->predecessors()->length(); ++i) {
458 AddBlock(block->predecessors()->at(i));
459 }
460 }
461}
462
463
464#ifdef DEBUG
465
466// Checks reachability of the blocks in this graph and stores a bit in
467// the BitVector "reachable()" for every block that can be reached
468// from the start block of the graph. If "dont_visit" is non-null, the given
469// block is treated as if it would not be part of the graph. "visited_count()"
470// returns the number of reachable blocks.
471class ReachabilityAnalyzer BASE_EMBEDDED {
472 public:
473 ReachabilityAnalyzer(HBasicBlock* entry_block,
474 int block_count,
475 HBasicBlock* dont_visit)
476 : visited_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000477 stack_(16, entry_block->zone()),
478 reachable_(block_count, entry_block->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000479 dont_visit_(dont_visit) {
480 PushBlock(entry_block);
481 Analyze();
482 }
483
484 int visited_count() const { return visited_count_; }
485 const BitVector* reachable() const { return &reachable_; }
486
487 private:
488 void PushBlock(HBasicBlock* block) {
489 if (block != NULL && block != dont_visit_ &&
490 !reachable_.Contains(block->block_id())) {
491 reachable_.Add(block->block_id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000492 stack_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000493 visited_count_++;
494 }
495 }
496
497 void Analyze() {
498 while (!stack_.is_empty()) {
499 HControlInstruction* end = stack_.RemoveLast()->end();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000500 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
501 PushBlock(it.Current());
502 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000503 }
504 }
505
506 int visited_count_;
507 ZoneList<HBasicBlock*> stack_;
508 BitVector reachable_;
509 HBasicBlock* dont_visit_;
510};
511
512
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000513void HGraph::Verify(bool do_full_verify) const {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000514 Heap::RelocationLock(isolate()->heap());
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000515 ALLOW_HANDLE_DEREF(isolate(), "debug mode verification");
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000516 for (int i = 0; i < blocks_.length(); i++) {
517 HBasicBlock* block = blocks_.at(i);
518
519 block->Verify();
520
521 // Check that every block contains at least one node and that only the last
522 // node is a control instruction.
523 HInstruction* current = block->first();
524 ASSERT(current != NULL && current->IsBlockEntry());
525 while (current != NULL) {
526 ASSERT((current->next() == NULL) == current->IsControlInstruction());
527 ASSERT(current->block() == block);
528 current->Verify();
529 current = current->next();
530 }
531
532 // Check that successors are correctly set.
533 HBasicBlock* first = block->end()->FirstSuccessor();
534 HBasicBlock* second = block->end()->SecondSuccessor();
535 ASSERT(second == NULL || first != NULL);
536
537 // Check that the predecessor array is correct.
538 if (first != NULL) {
539 ASSERT(first->predecessors()->Contains(block));
540 if (second != NULL) {
541 ASSERT(second->predecessors()->Contains(block));
542 }
543 }
544
545 // Check that phis have correct arguments.
546 for (int j = 0; j < block->phis()->length(); j++) {
547 HPhi* phi = block->phis()->at(j);
548 phi->Verify();
549 }
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000550
551 // Check that all join blocks have predecessors that end with an
552 // unconditional goto and agree on their environment node id.
553 if (block->predecessors()->length() >= 2) {
554 BailoutId id =
555 block->predecessors()->first()->last_environment()->ast_id();
556 for (int k = 0; k < block->predecessors()->length(); k++) {
557 HBasicBlock* predecessor = block->predecessors()->at(k);
558 ASSERT(predecessor->end()->IsGoto());
559 ASSERT(predecessor->last_environment()->ast_id() == id);
560 }
561 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000562 }
563
564 // Check special property of first block to have no predecessors.
565 ASSERT(blocks_.at(0)->predecessors()->is_empty());
566
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000567 if (do_full_verify) {
568 // Check that the graph is fully connected.
569 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
570 ASSERT(analyzer.visited_count() == blocks_.length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000571
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000572 // Check that entry block dominator is NULL.
573 ASSERT(entry_block_->dominator() == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000574
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000575 // Check dominators.
576 for (int i = 0; i < blocks_.length(); ++i) {
577 HBasicBlock* block = blocks_.at(i);
578 if (block->dominator() == NULL) {
579 // Only start block may have no dominator assigned to.
580 ASSERT(i == 0);
581 } else {
582 // Assert that block is unreachable if dominator must not be visited.
583 ReachabilityAnalyzer dominator_analyzer(entry_block_,
584 blocks_.length(),
585 block->dominator());
586 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
587 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000588 }
589 }
590}
591
592#endif
593
594
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000595HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer,
596 int32_t value) {
597 if (!pointer->is_set()) {
598 HConstant* constant =
599 new(zone()) HConstant(value, Representation::Integer32());
600 constant->InsertAfter(GetConstantUndefined());
601 pointer->set(constant);
602 }
603 return pointer->get();
604}
605
606
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000607HConstant* HGraph::GetConstantSmi(SetOncePointer<HConstant>* pointer,
608 int32_t value) {
609 if (!pointer->is_set()) {
610 HConstant* constant =
611 new(zone()) HConstant(Handle<Object>(Smi::FromInt(value), isolate()),
612 Representation::Tagged());
613 constant->InsertAfter(GetConstantUndefined());
614 pointer->set(constant);
615 }
616 return pointer->get();
617}
618
619
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000620HConstant* HGraph::GetConstant0() {
621 return GetConstantInt32(&constant_0_, 0);
622}
623
624
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000625HConstant* HGraph::GetConstant1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000626 return GetConstantInt32(&constant_1_, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000627}
628
629
630HConstant* HGraph::GetConstantMinus1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000631 return GetConstantInt32(&constant_minus1_, -1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000632}
633
634
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000635#define DEFINE_GET_CONSTANT(Name, name, htype, boolean_value) \
636HConstant* HGraph::GetConstant##Name() { \
637 if (!constant_##name##_.is_set()) { \
638 HConstant* constant = new(zone()) HConstant( \
639 isolate()->factory()->name##_value(), \
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000640 UniqueValueId(isolate()->heap()->name##_value()), \
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000641 Representation::Tagged(), \
642 htype, \
643 false, \
danno@chromium.orgf005df62013-04-30 16:36:45 +0000644 true, \
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000645 boolean_value); \
646 constant->InsertAfter(GetConstantUndefined()); \
647 constant_##name##_.set(constant); \
648 } \
649 return constant_##name##_.get(); \
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000650}
651
652
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000653DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true)
654DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false)
655DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false)
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000656DEFINE_GET_CONSTANT(Null, null, HType::Tagged(), false)
657
658
659HConstant* HGraph::GetConstantSmi0() {
660 return GetConstantSmi(&constant_smi_0_, 0);
661}
662
663
664HConstant* HGraph::GetConstantSmi1() {
665 return GetConstantSmi(&constant_smi_1_, 1);
666}
667
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000668
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000669#undef DEFINE_GET_CONSTANT
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000670
671
svenpanne@chromium.org876cca82013-03-18 14:43:20 +0000672HConstant* HGraph::GetInvalidContext() {
673 return GetConstantInt32(&constant_invalid_context_, 0xFFFFC0C7);
674}
675
676
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000677HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, int position)
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000678 : builder_(builder),
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000679 position_(position),
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000680 finished_(false),
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000681 did_then_(false),
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000682 did_else_(false),
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000683 did_and_(false),
684 did_or_(false),
685 captured_(false),
686 needs_compare_(true),
687 split_edge_merge_block_(NULL) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000688 HEnvironment* env = builder->environment();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000689 first_true_block_ = builder->CreateBasicBlock(env->Copy());
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000690 last_true_block_ = NULL;
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000691 first_false_block_ = builder->CreateBasicBlock(env->Copy());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000692}
693
694
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000695HGraphBuilder::IfBuilder::IfBuilder(
696 HGraphBuilder* builder,
697 HIfContinuation* continuation)
698 : builder_(builder),
699 position_(RelocInfo::kNoPosition),
700 finished_(false),
701 did_then_(false),
702 did_else_(false),
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000703 did_and_(false),
704 did_or_(false),
705 captured_(false),
706 needs_compare_(false),
707 first_true_block_(NULL),
708 first_false_block_(NULL),
709 split_edge_merge_block_(NULL),
710 merge_block_(NULL) {
711 continuation->Continue(&first_true_block_,
712 &first_false_block_,
713 &position_);
714}
715
716
717HInstruction* HGraphBuilder::IfBuilder::IfCompare(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000718 HValue* left,
719 HValue* right,
720 Token::Value token,
721 Representation input_representation) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000722 HCompareIDAndBranch* compare =
723 new(zone()) HCompareIDAndBranch(left, right, token);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000724 compare->set_observed_input_representation(input_representation,
725 input_representation);
726 compare->ChangeRepresentation(input_representation);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000727 AddCompare(compare);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000728 return compare;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000729}
730
731
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000732HInstruction* HGraphBuilder::IfBuilder::IfCompareMap(HValue* left,
733 Handle<Map> map) {
734 HCompareMap* compare =
735 new(zone()) HCompareMap(left, map,
736 first_true_block_, first_false_block_);
737 AddCompare(compare);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000738 return compare;
739}
740
741
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000742void HGraphBuilder::IfBuilder::AddCompare(HControlInstruction* compare) {
743 if (split_edge_merge_block_ != NULL) {
744 HEnvironment* env = first_false_block_->last_environment();
745 HBasicBlock* split_edge =
746 builder_->CreateBasicBlock(env->Copy());
747 if (did_or_) {
748 compare->SetSuccessorAt(0, split_edge);
749 compare->SetSuccessorAt(1, first_false_block_);
750 } else {
751 compare->SetSuccessorAt(0, first_true_block_);
752 compare->SetSuccessorAt(1, split_edge);
753 }
754 split_edge->GotoNoSimulate(split_edge_merge_block_);
755 } else {
756 compare->SetSuccessorAt(0, first_true_block_);
757 compare->SetSuccessorAt(1, first_false_block_);
758 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000759 builder_->current_block()->Finish(compare);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000760 needs_compare_ = false;
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000761}
762
763
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000764void HGraphBuilder::IfBuilder::Or() {
765 ASSERT(!did_and_);
766 did_or_ = true;
767 HEnvironment* env = first_false_block_->last_environment();
768 if (split_edge_merge_block_ == NULL) {
769 split_edge_merge_block_ =
770 builder_->CreateBasicBlock(env->Copy());
771 first_true_block_->GotoNoSimulate(split_edge_merge_block_);
772 first_true_block_ = split_edge_merge_block_;
773 }
774 builder_->set_current_block(first_false_block_);
775 first_false_block_ = builder_->CreateBasicBlock(env->Copy());
776}
777
778
779void HGraphBuilder::IfBuilder::And() {
780 ASSERT(!did_or_);
781 did_and_ = true;
782 HEnvironment* env = first_false_block_->last_environment();
783 if (split_edge_merge_block_ == NULL) {
784 split_edge_merge_block_ = builder_->CreateBasicBlock(env->Copy());
785 first_false_block_->GotoNoSimulate(split_edge_merge_block_);
786 first_false_block_ = split_edge_merge_block_;
787 }
788 builder_->set_current_block(first_true_block_);
789 first_true_block_ = builder_->CreateBasicBlock(env->Copy());
790}
791
792
793void HGraphBuilder::IfBuilder::CaptureContinuation(
794 HIfContinuation* continuation) {
795 ASSERT(!finished_);
796 ASSERT(!captured_);
797 HBasicBlock* true_block = last_true_block_ == NULL
798 ? first_true_block_
799 : last_true_block_;
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000800 HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
801 ? builder_->current_block()
802 : first_false_block_;
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000803 continuation->Capture(true_block, false_block, position_);
804 captured_ = true;
805 End();
806}
807
808
809void HGraphBuilder::IfBuilder::Then() {
810 ASSERT(!captured_);
811 ASSERT(!finished_);
812 did_then_ = true;
813 if (needs_compare_) {
814 // Handle if's without any expressions, they jump directly to the "else"
815 // branch.
816 builder_->current_block()->GotoNoSimulate(first_false_block_);
817 first_true_block_ = NULL;
818 }
819 builder_->set_current_block(first_true_block_);
820}
821
822
823void HGraphBuilder::IfBuilder::Else() {
824 ASSERT(did_then_);
825 ASSERT(!captured_);
826 ASSERT(!finished_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000827 last_true_block_ = builder_->current_block();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000828 ASSERT(first_true_block_ == NULL || !last_true_block_->IsFinished());
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000829 builder_->set_current_block(first_false_block_);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000830 did_else_ = true;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000831}
832
833
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000834void HGraphBuilder::IfBuilder::Deopt() {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000835 HBasicBlock* block = builder_->current_block();
836 block->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
837 if (did_else_) {
838 first_false_block_ = NULL;
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000839 } else {
840 first_true_block_ = NULL;
841 }
842}
843
844
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000845void HGraphBuilder::IfBuilder::Return(HValue* value) {
846 HBasicBlock* block = builder_->current_block();
847 block->Finish(new(zone()) HReturn(value,
848 builder_->environment()->LookupContext(),
849 builder_->graph()->GetConstantMinus1()));
850 if (did_else_) {
851 first_false_block_ = NULL;
852 } else {
853 first_true_block_ = NULL;
854 }
855}
856
857
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000858void HGraphBuilder::IfBuilder::End() {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000859 if (!captured_) {
860 ASSERT(did_then_);
861 if (!did_else_) {
862 last_true_block_ = builder_->current_block();
863 }
864 if (first_true_block_ == NULL) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000865 // Deopt on true. Nothing to do, just continue the false block.
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000866 } else if (first_false_block_ == NULL) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000867 // Deopt on false. Nothing to do except switching to the true block.
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000868 builder_->set_current_block(last_true_block_);
869 } else {
870 HEnvironment* merge_env = last_true_block_->last_environment()->Copy();
871 merge_block_ = builder_->CreateBasicBlock(merge_env);
872 ASSERT(!finished_);
873 if (!did_else_) Else();
874 ASSERT(!last_true_block_->IsFinished());
875 HBasicBlock* last_false_block = builder_->current_block();
876 ASSERT(!last_false_block->IsFinished());
877 last_true_block_->GotoNoSimulate(merge_block_);
878 last_false_block->GotoNoSimulate(merge_block_);
879 builder_->set_current_block(merge_block_);
880 }
881 }
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000882 finished_ = true;
883}
884
885
886HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder,
887 HValue* context,
danno@chromium.org2d18b362013-03-22 17:19:26 +0000888 LoopBuilder::Direction direction)
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000889 : builder_(builder),
890 context_(context),
891 direction_(direction),
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000892 finished_(false) {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000893 header_block_ = builder->CreateLoopHeaderBlock();
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000894 body_block_ = NULL;
895 exit_block_ = NULL;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000896}
897
898
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000899HValue* HGraphBuilder::LoopBuilder::BeginBody(
900 HValue* initial,
901 HValue* terminating,
902 Token::Value token,
903 Representation input_representation) {
904 HEnvironment* env = builder_->environment();
905 phi_ = new(zone()) HPhi(env->values()->length(), zone());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000906 header_block_->AddPhi(phi_);
907 phi_->AddInput(initial);
908 phi_->ChangeRepresentation(Representation::Integer32());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000909 env->Push(initial);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000910 builder_->current_block()->GotoNoSimulate(header_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000911
912 HEnvironment* body_env = env->Copy();
913 HEnvironment* exit_env = env->Copy();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000914 body_block_ = builder_->CreateBasicBlock(body_env);
915 exit_block_ = builder_->CreateBasicBlock(exit_env);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000916 // Remove the phi from the expression stack
917 body_env->Pop();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000918
919 builder_->set_current_block(header_block_);
920 HCompareIDAndBranch* compare =
921 new(zone()) HCompareIDAndBranch(phi_, terminating, token);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000922 compare->set_observed_input_representation(input_representation,
923 input_representation);
924 compare->ChangeRepresentation(input_representation);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000925 compare->SetSuccessorAt(0, body_block_);
926 compare->SetSuccessorAt(1, exit_block_);
927 builder_->current_block()->Finish(compare);
928
929 builder_->set_current_block(body_block_);
930 if (direction_ == kPreIncrement || direction_ == kPreDecrement) {
931 HValue* one = builder_->graph()->GetConstant1();
932 if (direction_ == kPreIncrement) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000933 increment_ = HAdd::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000934 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000935 increment_ = HSub::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000936 }
937 increment_->ClearFlag(HValue::kCanOverflow);
938 increment_->ChangeRepresentation(Representation::Integer32());
939 builder_->AddInstruction(increment_);
940 return increment_;
941 } else {
942 return phi_;
943 }
944}
945
946
947void HGraphBuilder::LoopBuilder::EndBody() {
948 ASSERT(!finished_);
949
950 if (direction_ == kPostIncrement || direction_ == kPostDecrement) {
951 HValue* one = builder_->graph()->GetConstant1();
952 if (direction_ == kPostIncrement) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000953 increment_ = HAdd::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000954 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000955 increment_ = HSub::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000956 }
957 increment_->ClearFlag(HValue::kCanOverflow);
958 increment_->ChangeRepresentation(Representation::Integer32());
959 builder_->AddInstruction(increment_);
960 }
961
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000962 // Push the new increment value on the expression stack to merge into the phi.
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000963 builder_->environment()->Push(increment_);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +0000964 builder_->current_block()->GotoNoSimulate(header_block_);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000965 header_block_->loop_information()->RegisterBackEdge(body_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000966
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000967 builder_->set_current_block(exit_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000968 // Pop the phi from the expression stack
969 builder_->environment()->Pop();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000970 finished_ = true;
971}
972
973
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000974HGraph* HGraphBuilder::CreateGraph() {
975 graph_ = new(zone()) HGraph(info_);
ulan@chromium.org750145a2013-03-07 15:14:13 +0000976 if (FLAG_hydrogen_stats) isolate()->GetHStatistics()->Initialize(info_);
977 HPhase phase("H_Block building", isolate());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000978 set_current_block(graph()->entry_block());
979 if (!BuildGraph()) return NULL;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000980 graph()->FinalizeUniqueValueIds();
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000981 return graph_;
982}
983
984
985HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
986 ASSERT(current_block() != NULL);
987 current_block()->AddInstruction(instr);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000988 if (no_side_effects_scope_count_ > 0) {
989 instr->SetFlag(HValue::kHasNoObservableSideEffects);
990 }
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000991 return instr;
992}
993
994
995void HGraphBuilder::AddSimulate(BailoutId id,
996 RemovableSimulate removable) {
997 ASSERT(current_block() != NULL);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000998 ASSERT(no_side_effects_scope_count_ == 0);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000999 current_block()->AddSimulate(id, removable);
1000}
1001
1002
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001003HBoundsCheck* HGraphBuilder::AddBoundsCheck(HValue* index,
1004 HValue* length,
1005 BoundsCheckKeyMode key_mode,
1006 Representation r) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001007 if (!index->type().IsSmi()) {
1008 index = new(graph()->zone()) HCheckSmiOrInt32(index);
1009 AddInstruction(HCheckSmiOrInt32::cast(index));
1010 }
1011 if (!length->type().IsSmi()) {
1012 length = new(graph()->zone()) HCheckSmiOrInt32(length);
1013 AddInstruction(HCheckSmiOrInt32::cast(length));
1014 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001015 HBoundsCheck* result = new(graph()->zone()) HBoundsCheck(
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001016 index, length, key_mode, r);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001017 AddInstruction(result);
1018 return result;
1019}
1020
1021
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00001022HReturn* HGraphBuilder::AddReturn(HValue* value) {
1023 HValue* context = environment()->LookupContext();
1024 int num_parameters = graph()->info()->num_parameters();
1025 HValue* params = AddInstruction(new(graph()->zone())
1026 HConstant(num_parameters, Representation::Integer32()));
1027 HReturn* return_instruction = new(graph()->zone())
1028 HReturn(value, context, params);
1029 current_block()->FinishExit(return_instruction);
1030 return return_instruction;
1031}
1032
1033
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001034HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001035 HBasicBlock* b = graph()->CreateBasicBlock();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001036 b->SetInitialEnvironment(env);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001037 return b;
1038}
1039
1040
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001041HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001042 HBasicBlock* header = graph()->CreateBasicBlock();
1043 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001044 header->SetInitialEnvironment(entry_env);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001045 header->AttachLoopInformation();
1046 return header;
1047}
1048
1049
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001050HValue* HGraphBuilder::BuildCheckNonSmi(HValue* obj) {
1051 HCheckNonSmi* check = new(zone()) HCheckNonSmi(obj);
1052 AddInstruction(check);
1053 return check;
1054}
1055
1056
1057HValue* HGraphBuilder::BuildCheckMap(HValue* obj,
1058 Handle<Map> map) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00001059 HCheckMaps* check = HCheckMaps::New(obj, map, zone());
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001060 AddInstruction(check);
1061 return check;
1062}
1063
1064
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001065HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
1066 HValue* external_elements,
1067 HValue* checked_key,
1068 HValue* val,
1069 HValue* dependency,
1070 ElementsKind elements_kind,
1071 bool is_store) {
1072 Zone* zone = this->zone();
1073 if (is_store) {
1074 ASSERT(val != NULL);
1075 switch (elements_kind) {
1076 case EXTERNAL_PIXEL_ELEMENTS: {
1077 val = AddInstruction(new(zone) HClampToUint8(val));
1078 break;
1079 }
1080 case EXTERNAL_BYTE_ELEMENTS:
1081 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
1082 case EXTERNAL_SHORT_ELEMENTS:
1083 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
1084 case EXTERNAL_INT_ELEMENTS:
1085 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
1086 break;
1087 }
1088 case EXTERNAL_FLOAT_ELEMENTS:
1089 case EXTERNAL_DOUBLE_ELEMENTS:
1090 break;
1091 case FAST_SMI_ELEMENTS:
1092 case FAST_ELEMENTS:
1093 case FAST_DOUBLE_ELEMENTS:
1094 case FAST_HOLEY_SMI_ELEMENTS:
1095 case FAST_HOLEY_ELEMENTS:
1096 case FAST_HOLEY_DOUBLE_ELEMENTS:
1097 case DICTIONARY_ELEMENTS:
1098 case NON_STRICT_ARGUMENTS_ELEMENTS:
1099 UNREACHABLE();
1100 break;
1101 }
1102 return new(zone) HStoreKeyed(external_elements, checked_key,
1103 val, elements_kind);
1104 } else {
1105 ASSERT(val == NULL);
1106 HLoadKeyed* load =
1107 new(zone) HLoadKeyed(
1108 external_elements, checked_key, dependency, elements_kind);
1109 if (FLAG_opt_safe_uint32_operations &&
1110 elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
1111 graph()->RecordUint32Instruction(load);
1112 }
1113 return load;
1114 }
1115}
1116
1117
1118HInstruction* HGraphBuilder::BuildFastElementAccess(
1119 HValue* elements,
1120 HValue* checked_key,
1121 HValue* val,
1122 HValue* load_dependency,
1123 ElementsKind elements_kind,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001124 bool is_store,
1125 KeyedAccessStoreMode store_mode) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001126 Zone* zone = this->zone();
1127 if (is_store) {
1128 ASSERT(val != NULL);
1129 switch (elements_kind) {
1130 case FAST_SMI_ELEMENTS:
1131 case FAST_HOLEY_SMI_ELEMENTS:
1132 // Smi-only arrays need a smi check.
1133 AddInstruction(new(zone) HCheckSmi(val));
1134 // Fall through.
1135 case FAST_ELEMENTS:
1136 case FAST_HOLEY_ELEMENTS:
1137 case FAST_DOUBLE_ELEMENTS:
1138 case FAST_HOLEY_DOUBLE_ELEMENTS:
1139 return new(zone) HStoreKeyed(elements, checked_key, val, elements_kind);
1140 default:
1141 UNREACHABLE();
1142 return NULL;
1143 }
1144 }
1145 // It's an element load (!is_store).
1146 return new(zone) HLoadKeyed(elements,
1147 checked_key,
1148 load_dependency,
1149 elements_kind);
1150}
1151
1152
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001153HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object,
1154 HValue* elements,
1155 ElementsKind kind,
1156 HValue* length,
1157 HValue* key,
1158 bool is_js_array) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001159 Zone* zone = this->zone();
danno@chromium.org2d18b362013-03-22 17:19:26 +00001160 IfBuilder length_checker(this);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001161
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001162 length_checker.IfCompare(length, key, Token::EQ);
1163 length_checker.Then();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001164
1165 HValue* current_capacity =
1166 AddInstruction(new(zone) HFixedArrayBaseLength(elements));
1167
danno@chromium.org2d18b362013-03-22 17:19:26 +00001168 IfBuilder capacity_checker(this);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001169
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001170 capacity_checker.IfCompare(length, current_capacity, Token::EQ);
1171 capacity_checker.Then();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001172
1173 HValue* context = environment()->LookupContext();
1174
1175 HValue* new_capacity =
1176 BuildNewElementsCapacity(context, current_capacity);
1177
1178 HValue* new_elements = BuildGrowElementsCapacity(object, elements,
1179 kind, length,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001180 new_capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001181
1182 environment()->Push(new_elements);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001183 capacity_checker.Else();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001184
1185 environment()->Push(elements);
1186 capacity_checker.End();
1187
1188 if (is_js_array) {
1189 HValue* new_length = AddInstruction(
1190 HAdd::New(zone, context, length, graph_->GetConstant1()));
1191 new_length->ChangeRepresentation(Representation::Integer32());
1192 new_length->ClearFlag(HValue::kCanOverflow);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001193
1194 Factory* factory = isolate()->factory();
danno@chromium.orgf005df62013-04-30 16:36:45 +00001195 Representation representation = IsFastElementsKind(kind)
1196 ? Representation::Smi() : Representation::Tagged();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001197 HInstruction* length_store = AddInstruction(new(zone) HStoreNamedField(
1198 object,
1199 factory->length_field_string(),
1200 new_length, true,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001201 representation,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001202 JSArray::kLengthOffset));
1203 length_store->SetGVNFlag(kChangesArrayLengths);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001204 }
1205
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001206 length_checker.Else();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001207
1208 AddBoundsCheck(key, length, ALLOW_SMI_KEY);
1209 environment()->Push(elements);
1210
1211 length_checker.End();
1212
1213 return environment()->Pop();
1214}
1215
1216
1217HValue* HGraphBuilder::BuildCopyElementsOnWrite(HValue* object,
1218 HValue* elements,
1219 ElementsKind kind,
1220 HValue* length) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001221 Zone* zone = this->zone();
1222 Heap* heap = isolate()->heap();
1223
danno@chromium.org2d18b362013-03-22 17:19:26 +00001224 IfBuilder cow_checker(this);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001225
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001226 cow_checker.IfCompareMap(elements,
1227 Handle<Map>(heap->fixed_cow_array_map()));
1228 cow_checker.Then();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001229
1230 HValue* capacity =
1231 AddInstruction(new(zone) HFixedArrayBaseLength(elements));
1232
1233 HValue* new_elements = BuildGrowElementsCapacity(object, elements,
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001234 kind, length, capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001235
1236 environment()->Push(new_elements);
1237
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001238 cow_checker.Else();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001239
1240 environment()->Push(elements);
1241
1242 cow_checker.End();
1243
1244 return environment()->Pop();
1245}
1246
1247
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001248HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
1249 HValue* object,
1250 HValue* key,
1251 HValue* val,
1252 HCheckMaps* mapcheck,
1253 bool is_js_array,
1254 ElementsKind elements_kind,
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00001255 bool is_store,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001256 KeyedAccessStoreMode store_mode,
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00001257 Representation checked_index_representation) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001258 ASSERT(!IsExternalArrayElementsKind(elements_kind) || !is_js_array);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001259 Zone* zone = this->zone();
1260 // No GVNFlag is necessary for ElementsKind if there is an explicit dependency
1261 // on a HElementsTransition instruction. The flag can also be removed if the
1262 // map to check has FAST_HOLEY_ELEMENTS, since there can be no further
1263 // ElementsKind transitions. Finally, the dependency can be removed for stores
1264 // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
1265 // generated store code.
1266 if ((elements_kind == FAST_HOLEY_ELEMENTS) ||
1267 (elements_kind == FAST_ELEMENTS && is_store)) {
1268 if (mapcheck != NULL) {
1269 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
1270 }
1271 }
1272 bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
1273 bool fast_elements = IsFastObjectElementsKind(elements_kind);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001274 HValue* elements = AddLoadElements(object, mapcheck);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001275 if (is_store && (fast_elements || fast_smi_only_elements) &&
1276 store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00001277 HCheckMaps* check_cow_map = HCheckMaps::New(
ulan@chromium.org750145a2013-03-07 15:14:13 +00001278 elements, isolate()->factory()->fixed_array_map(), zone);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001279 check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
1280 AddInstruction(check_cow_map);
1281 }
1282 HInstruction* length = NULL;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001283 if (is_js_array) {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00001284 length = AddInstruction(
1285 HLoadNamedField::NewArrayLength(zone, object, mapcheck, HType::Smi()));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001286 } else {
1287 length = AddInstruction(new(zone) HFixedArrayBaseLength(elements));
1288 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001289 HValue* checked_key = NULL;
1290 if (IsExternalArrayElementsKind(elements_kind)) {
1291 if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001292 NoObservableSideEffectsScope no_effects(this);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001293 HLoadExternalArrayPointer* external_elements =
1294 new(zone) HLoadExternalArrayPointer(elements);
1295 AddInstruction(external_elements);
danno@chromium.org2d18b362013-03-22 17:19:26 +00001296 IfBuilder length_checker(this);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001297 length_checker.IfCompare(key, length, Token::LT);
1298 length_checker.Then();
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00001299 IfBuilder negative_checker(this);
1300 HValue* bounds_check = negative_checker.IfCompare(
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001301 key, graph()->GetConstant0(), Token::GTE);
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00001302 negative_checker.Then();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001303 HInstruction* result = BuildExternalArrayElementAccess(
1304 external_elements, key, val, bounds_check,
1305 elements_kind, is_store);
1306 AddInstruction(result);
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00001307 negative_checker.ElseDeopt();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001308 length_checker.End();
1309 return result;
1310 } else {
1311 ASSERT(store_mode == STANDARD_STORE);
1312 checked_key = AddBoundsCheck(
1313 key, length, ALLOW_SMI_KEY, checked_index_representation);
1314 HLoadExternalArrayPointer* external_elements =
1315 new(zone) HLoadExternalArrayPointer(elements);
1316 AddInstruction(external_elements);
1317 return AddInstruction(BuildExternalArrayElementAccess(
1318 external_elements, checked_key, val, mapcheck,
1319 elements_kind, is_store));
1320 }
1321 }
1322 ASSERT(fast_smi_only_elements ||
1323 fast_elements ||
1324 IsFastDoubleElementsKind(elements_kind));
1325
1326 if (is_store && IsFastSmiElementsKind(elements_kind) &&
1327 !val->type().IsSmi()) {
1328 AddInstruction(new(zone) HCheckSmi(val));
1329 }
1330
1331 if (IsGrowStoreMode(store_mode)) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001332 NoObservableSideEffectsScope no_effects(this);
1333
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001334 elements = BuildCheckForCapacityGrow(object, elements, elements_kind,
1335 length, key, is_js_array);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001336 if (!key->type().IsSmi()) {
1337 checked_key = AddInstruction(new(zone) HCheckSmiOrInt32(key));
1338 } else {
1339 checked_key = key;
1340 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001341 } else {
1342 checked_key = AddBoundsCheck(
1343 key, length, ALLOW_SMI_KEY, checked_index_representation);
1344
1345 if (is_store && (fast_elements || fast_smi_only_elements)) {
1346 if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001347 NoObservableSideEffectsScope no_effects(this);
1348
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001349 elements = BuildCopyElementsOnWrite(object, elements, elements_kind,
1350 length);
1351 } else {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00001352 HCheckMaps* check_cow_map = HCheckMaps::New(
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001353 elements, isolate()->factory()->fixed_array_map(), zone);
1354 check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
1355 AddInstruction(check_cow_map);
1356 }
1357 }
1358 }
1359 return AddInstruction(
1360 BuildFastElementAccess(elements, checked_key, val, mapcheck,
1361 elements_kind, is_store, store_mode));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001362}
1363
1364
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001365HValue* HGraphBuilder::BuildAllocateElements(HValue* context,
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001366 ElementsKind kind,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001367 HValue* capacity) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001368 Zone* zone = this->zone();
1369
1370 int elements_size = IsFastDoubleElementsKind(kind)
1371 ? kDoubleSize : kPointerSize;
1372 HConstant* elements_size_value =
1373 new(zone) HConstant(elements_size, Representation::Integer32());
1374 AddInstruction(elements_size_value);
1375 HValue* mul = AddInstruction(
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001376 HMul::New(zone, context, capacity, elements_size_value));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001377 mul->ChangeRepresentation(Representation::Integer32());
1378 mul->ClearFlag(HValue::kCanOverflow);
1379
1380 HConstant* header_size =
1381 new(zone) HConstant(FixedArray::kHeaderSize, Representation::Integer32());
1382 AddInstruction(header_size);
1383 HValue* total_size = AddInstruction(
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001384 HAdd::New(zone, context, mul, header_size));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001385 total_size->ChangeRepresentation(Representation::Integer32());
1386 total_size->ClearFlag(HValue::kCanOverflow);
1387
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001388 HAllocate::Flags flags = HAllocate::DefaultFlags(kind);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001389 if (FLAG_pretenure_literals) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001390 // TODO(hpayer): When pretenuring can be internalized, flags can become
1391 // private to HAllocate.
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001392 if (IsFastDoubleElementsKind(kind)) {
1393 flags = static_cast<HAllocate::Flags>(
1394 flags | HAllocate::CAN_ALLOCATE_IN_OLD_DATA_SPACE);
1395 } else {
1396 flags = static_cast<HAllocate::Flags>(
1397 flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE);
1398 }
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00001399 }
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001400
1401 HValue* elements =
1402 AddInstruction(new(zone) HAllocate(context, total_size,
1403 HType::JSArray(), flags));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001404 return elements;
1405}
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001406
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001407
1408void HGraphBuilder::BuildInitializeElements(HValue* elements,
1409 ElementsKind kind,
1410 HValue* capacity) {
1411 Zone* zone = this->zone();
ulan@chromium.org750145a2013-03-07 15:14:13 +00001412 Factory* factory = isolate()->factory();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001413 Handle<Map> map = IsFastDoubleElementsKind(kind)
1414 ? factory->fixed_double_array_map()
1415 : factory->fixed_array_map();
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001416 BuildStoreMap(elements, map);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001417
ulan@chromium.org750145a2013-03-07 15:14:13 +00001418 Handle<String> fixed_array_length_field_name = factory->length_field_string();
danno@chromium.orgf005df62013-04-30 16:36:45 +00001419 Representation representation = IsFastElementsKind(kind)
1420 ? Representation::Smi() : Representation::Tagged();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001421 HInstruction* store_length =
1422 new(zone) HStoreNamedField(elements, fixed_array_length_field_name,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001423 capacity, true, representation,
1424 FixedArray::kLengthOffset);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001425 AddInstruction(store_length);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001426}
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001427
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001428
1429HValue* HGraphBuilder::BuildAllocateAndInitializeElements(HValue* context,
1430 ElementsKind kind,
1431 HValue* capacity) {
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00001432 HValue* new_elements = BuildAllocateElements(context, kind, capacity);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001433 BuildInitializeElements(new_elements, kind, capacity);
1434 return new_elements;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001435}
1436
1437
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001438HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array,
1439 HValue* array_map,
1440 AllocationSiteMode mode,
1441 HValue* allocation_site_payload,
1442 HValue* length_field) {
1443
1444 BuildStoreMap(array, array_map);
1445
1446 HConstant* empty_fixed_array =
1447 new(zone()) HConstant(
1448 Handle<FixedArray>(isolate()->heap()->empty_fixed_array()),
1449 Representation::Tagged());
1450 AddInstruction(empty_fixed_array);
1451
1452 AddInstruction(new(zone()) HStoreNamedField(array,
1453 isolate()->factory()->properties_field_symbol(),
1454 empty_fixed_array,
1455 true,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001456 Representation::Tagged(),
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001457 JSArray::kPropertiesOffset));
1458
1459 HInstruction* length_store = AddInstruction(
1460 new(zone()) HStoreNamedField(array,
1461 isolate()->factory()->length_field_string(),
1462 length_field,
1463 true,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001464 Representation::Tagged(),
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001465 JSArray::kLengthOffset));
1466 length_store->SetGVNFlag(kChangesArrayLengths);
1467
1468 if (mode == TRACK_ALLOCATION_SITE) {
1469 BuildCreateAllocationSiteInfo(array,
1470 JSArray::kSize,
1471 allocation_site_payload);
1472 }
1473
1474 int elements_location = JSArray::kSize;
1475 if (mode == TRACK_ALLOCATION_SITE) {
1476 elements_location += AllocationSiteInfo::kSize;
1477 }
1478
1479 HInnerAllocatedObject* elements = new(zone()) HInnerAllocatedObject(
1480 array,
1481 elements_location);
1482 AddInstruction(elements);
1483
1484 HInstruction* elements_store = AddInstruction(
1485 new(zone()) HStoreNamedField(
1486 array,
1487 isolate()->factory()->elements_field_string(),
1488 elements,
1489 true,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001490 Representation::Tagged(),
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001491 JSArray::kElementsOffset));
1492 elements_store->SetGVNFlag(kChangesElementsPointer);
1493
1494 return elements;
1495}
1496
1497
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001498HInstruction* HGraphBuilder::BuildStoreMap(HValue* object,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001499 HValue* map) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001500 Zone* zone = this->zone();
ulan@chromium.org750145a2013-03-07 15:14:13 +00001501 Factory* factory = isolate()->factory();
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001502 Handle<String> map_field_name = factory->map_field_string();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001503 HInstruction* store_map =
1504 new(zone) HStoreNamedField(object, map_field_name, map,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001505 true, Representation::Tagged(),
1506 JSObject::kMapOffset);
1507 store_map->ClearGVNFlag(kChangesInobjectFields);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001508 store_map->SetGVNFlag(kChangesMaps);
1509 AddInstruction(store_map);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001510 return store_map;
1511}
1512
1513
1514HInstruction* HGraphBuilder::BuildStoreMap(HValue* object,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001515 Handle<Map> map) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001516 Zone* zone = this->zone();
1517 HValue* map_constant =
1518 AddInstruction(new(zone) HConstant(map, Representation::Tagged()));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001519 return BuildStoreMap(object, map_constant);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001520}
1521
1522
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001523HLoadNamedField* HGraphBuilder::AddLoadElements(HValue* object,
1524 HValue* typecheck) {
1525 HLoadNamedField* instr = new(zone()) HLoadNamedField(object, true,
1526 Representation::Tagged(), JSObject::kElementsOffset, typecheck);
1527 AddInstruction(instr);
1528 instr->SetGVNFlag(kDependsOnElementsPointer);
1529 instr->ClearGVNFlag(kDependsOnMaps);
1530 instr->ClearGVNFlag(kDependsOnInobjectFields);
1531 return instr;
1532}
1533
1534
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001535HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* context,
1536 HValue* old_capacity) {
1537 Zone* zone = this->zone();
1538 HValue* half_old_capacity =
1539 AddInstruction(HShr::New(zone, context, old_capacity,
1540 graph_->GetConstant1()));
1541 half_old_capacity->ChangeRepresentation(Representation::Integer32());
1542 half_old_capacity->ClearFlag(HValue::kCanOverflow);
1543
1544 HValue* new_capacity = AddInstruction(
1545 HAdd::New(zone, context, half_old_capacity, old_capacity));
1546 new_capacity->ChangeRepresentation(Representation::Integer32());
1547 new_capacity->ClearFlag(HValue::kCanOverflow);
1548
1549 HValue* min_growth =
1550 AddInstruction(new(zone) HConstant(16, Representation::Integer32()));
1551
1552 new_capacity = AddInstruction(
1553 HAdd::New(zone, context, new_capacity, min_growth));
1554 new_capacity->ChangeRepresentation(Representation::Integer32());
1555 new_capacity->ClearFlag(HValue::kCanOverflow);
1556
1557 return new_capacity;
1558}
1559
1560
1561void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) {
1562 Zone* zone = this->zone();
1563 Heap* heap = isolate()->heap();
1564 int element_size = IsFastDoubleElementsKind(kind) ? kDoubleSize
1565 : kPointerSize;
1566 int max_size = heap->MaxNewSpaceAllocationSize() / element_size;
1567 max_size -= JSArray::kSize / element_size;
1568 HConstant* max_size_constant =
1569 new(zone) HConstant(max_size, Representation::Integer32());
1570 AddInstruction(max_size_constant);
1571 // Since we're forcing Integer32 representation for this HBoundsCheck,
1572 // there's no need to Smi-check the index.
1573 AddInstruction(new(zone)
1574 HBoundsCheck(length, max_size_constant,
1575 DONT_ALLOW_SMI_KEY, Representation::Integer32()));
1576}
1577
1578
1579HValue* HGraphBuilder::BuildGrowElementsCapacity(HValue* object,
1580 HValue* elements,
1581 ElementsKind kind,
1582 HValue* length,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001583 HValue* new_capacity) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001584 Zone* zone = this->zone();
1585 HValue* context = environment()->LookupContext();
1586
1587 BuildNewSpaceArrayCheck(new_capacity, kind);
1588
1589 HValue* new_elements =
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001590 BuildAllocateAndInitializeElements(context, kind, new_capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001591
1592 BuildCopyElements(context, elements, kind,
1593 new_elements, kind,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001594 length, new_capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001595
1596 Factory* factory = isolate()->factory();
1597 HInstruction* elements_store = AddInstruction(new(zone) HStoreNamedField(
1598 object,
1599 factory->elements_field_string(),
danno@chromium.orgf005df62013-04-30 16:36:45 +00001600 new_elements, true, Representation::Tagged(),
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001601 JSArray::kElementsOffset));
1602 elements_store->SetGVNFlag(kChangesElementsPointer);
1603
1604 return new_elements;
1605}
1606
1607
1608void HGraphBuilder::BuildFillElementsWithHole(HValue* context,
1609 HValue* elements,
1610 ElementsKind elements_kind,
1611 HValue* from,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001612 HValue* to) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001613 // Fast elements kinds need to be initialized in case statements below cause
1614 // a garbage collection.
1615 Factory* factory = isolate()->factory();
1616
1617 double nan_double = FixedDoubleArray::hole_nan_as_double();
1618 Zone* zone = this->zone();
1619 HValue* hole = IsFastSmiOrObjectElementsKind(elements_kind)
1620 ? AddInstruction(new(zone) HConstant(factory->the_hole_value(),
1621 Representation::Tagged()))
1622 : AddInstruction(new(zone) HConstant(nan_double,
1623 Representation::Double()));
1624
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001625 // Special loop unfolding case
1626 static const int kLoopUnfoldLimit = 4;
1627 bool unfold_loop = false;
1628 int initial_capacity = JSArray::kPreallocatedArrayElements;
1629 if (from->IsConstant() && to->IsConstant() &&
1630 initial_capacity <= kLoopUnfoldLimit) {
1631 HConstant* constant_from = HConstant::cast(from);
1632 HConstant* constant_to = HConstant::cast(to);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001633
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001634 if (constant_from->HasInteger32Value() &&
1635 constant_from->Integer32Value() == 0 &&
1636 constant_to->HasInteger32Value() &&
1637 constant_to->Integer32Value() == initial_capacity) {
1638 unfold_loop = true;
1639 }
1640 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001641
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001642 if (unfold_loop) {
1643 for (int i = 0; i < initial_capacity; i++) {
1644 HInstruction* key = AddInstruction(new(zone)
1645 HConstant(i, Representation::Integer32()));
1646 AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind));
1647 }
1648 } else {
1649 LoopBuilder builder(this, context, LoopBuilder::kPostIncrement);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001650
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001651 HValue* key = builder.BeginBody(from, to, Token::LT);
1652
1653 AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind));
1654
1655 builder.EndBody();
1656 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001657}
1658
1659
1660void HGraphBuilder::BuildCopyElements(HValue* context,
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001661 HValue* from_elements,
1662 ElementsKind from_elements_kind,
1663 HValue* to_elements,
1664 ElementsKind to_elements_kind,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001665 HValue* length,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001666 HValue* capacity) {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001667 bool pre_fill_with_holes =
1668 IsFastDoubleElementsKind(from_elements_kind) &&
1669 IsFastObjectElementsKind(to_elements_kind);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001670
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001671 if (pre_fill_with_holes) {
1672 // If the copy might trigger a GC, make sure that the FixedArray is
1673 // pre-initialized with holes to make sure that it's always in a consistent
1674 // state.
1675 BuildFillElementsWithHole(context, to_elements, to_elements_kind,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001676 graph()->GetConstant0(), capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001677 }
1678
danno@chromium.org2d18b362013-03-22 17:19:26 +00001679 LoopBuilder builder(this, context, LoopBuilder::kPostIncrement);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001680
1681 HValue* key = builder.BeginBody(graph()->GetConstant0(), length, Token::LT);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001682
1683 HValue* element =
1684 AddInstruction(new(zone()) HLoadKeyed(from_elements, key, NULL,
1685 from_elements_kind,
1686 ALLOW_RETURN_HOLE));
1687
1688 AddInstruction(new(zone()) HStoreKeyed(to_elements, key, element,
1689 to_elements_kind));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001690
1691 builder.EndBody();
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001692
1693 if (!pre_fill_with_holes && length != capacity) {
1694 // Fill unused capacity with the hole.
1695 BuildFillElementsWithHole(context, to_elements, to_elements_kind,
danno@chromium.org2d18b362013-03-22 17:19:26 +00001696 key, capacity);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00001697 }
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001698}
1699
1700
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001701HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context,
1702 HValue* boilerplate,
1703 AllocationSiteMode mode,
1704 ElementsKind kind,
1705 int length) {
1706 Zone* zone = this->zone();
1707 Factory* factory = isolate()->factory();
1708
1709 NoObservableSideEffectsScope no_effects(this);
1710
1711 // All sizes here are multiples of kPointerSize.
1712 int size = JSArray::kSize;
1713 if (mode == TRACK_ALLOCATION_SITE) {
1714 size += AllocationSiteInfo::kSize;
1715 }
1716 int elems_offset = size;
1717 if (length > 0) {
1718 size += IsFastDoubleElementsKind(kind)
1719 ? FixedDoubleArray::SizeFor(length)
1720 : FixedArray::SizeFor(length);
1721 }
1722
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001723 HAllocate::Flags allocate_flags = HAllocate::DefaultFlags(kind);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001724 // Allocate both the JS array and the elements array in one big
1725 // allocation. This avoids multiple limit checks.
1726 HValue* size_in_bytes =
1727 AddInstruction(new(zone) HConstant(size, Representation::Integer32()));
1728 HInstruction* object =
1729 AddInstruction(new(zone) HAllocate(context,
1730 size_in_bytes,
1731 HType::JSObject(),
1732 allocate_flags));
1733
1734 // Copy the JS array part.
1735 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
1736 if ((i != JSArray::kElementsOffset) || (length == 0)) {
danno@chromium.orgf005df62013-04-30 16:36:45 +00001737 HInstruction* value = AddInstruction(new(zone) HLoadNamedField(
1738 boilerplate, true, Representation::Tagged(), i));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001739 if (i != JSArray::kMapOffset) {
1740 AddInstruction(new(zone) HStoreNamedField(object,
1741 factory->empty_string(),
danno@chromium.orgf005df62013-04-30 16:36:45 +00001742 value, true,
1743 Representation::Tagged(), i));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001744 } else {
1745 BuildStoreMap(object, value);
1746 }
1747 }
1748 }
1749
1750 // Create an allocation site info if requested.
1751 if (mode == TRACK_ALLOCATION_SITE) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001752 BuildCreateAllocationSiteInfo(object, JSArray::kSize, boilerplate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001753 }
1754
1755 if (length > 0) {
1756 // Get hold of the elements array of the boilerplate and setup the
1757 // elements pointer in the resulting object.
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001758 HValue* boilerplate_elements = AddLoadElements(boilerplate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001759 HValue* object_elements =
1760 AddInstruction(new(zone) HInnerAllocatedObject(object, elems_offset));
1761 AddInstruction(new(zone) HStoreNamedField(object,
1762 factory->elements_field_string(),
danno@chromium.orgf005df62013-04-30 16:36:45 +00001763 object_elements, true,
1764 Representation::Tagged(),
1765 JSObject::kElementsOffset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001766
1767 // Copy the elements array header.
1768 for (int i = 0; i < FixedArrayBase::kHeaderSize; i += kPointerSize) {
1769 HInstruction* value =
danno@chromium.orgf005df62013-04-30 16:36:45 +00001770 AddInstruction(new(zone) HLoadNamedField(
1771 boilerplate_elements, true, Representation::Tagged(), i));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001772 AddInstruction(new(zone) HStoreNamedField(object_elements,
1773 factory->empty_string(),
danno@chromium.orgf005df62013-04-30 16:36:45 +00001774 value, true,
1775 Representation::Tagged(), i));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00001776 }
1777
1778 // Copy the elements array contents.
1779 // TODO(mstarzinger): Teach HGraphBuilder::BuildCopyElements to unfold
1780 // copying loops with constant length up to a given boundary and use this
1781 // helper here instead.
1782 for (int i = 0; i < length; i++) {
1783 HValue* key_constant =
1784 AddInstruction(new(zone) HConstant(i, Representation::Integer32()));
1785 HInstruction* value =
1786 AddInstruction(new(zone) HLoadKeyed(boilerplate_elements,
1787 key_constant,
1788 NULL,
1789 kind));
1790 AddInstruction(new(zone) HStoreKeyed(object_elements,
1791 key_constant,
1792 value,
1793 kind));
1794 }
1795 }
1796
1797 return object;
1798}
1799
1800
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001801void HGraphBuilder::BuildCompareNil(
1802 HValue* value,
1803 EqualityKind kind,
1804 CompareNilICStub::Types types,
1805 Handle<Map> map,
1806 int position,
1807 HIfContinuation* continuation) {
1808 IfBuilder if_nil(this, position);
1809 bool needs_or = false;
1810 if ((types & CompareNilICStub::kCompareAgainstNull) != 0) {
1811 if (needs_or) if_nil.Or();
1812 if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull());
1813 needs_or = true;
1814 }
1815 if ((types & CompareNilICStub::kCompareAgainstUndefined) != 0) {
1816 if (needs_or) if_nil.Or();
1817 if_nil.If<HCompareObjectEqAndBranch>(value,
1818 graph()->GetConstantUndefined());
1819 needs_or = true;
1820 }
1821 // Handle either undetectable or monomorphic, not both.
1822 ASSERT(((types & CompareNilICStub::kCompareAgainstUndetectable) == 0) ||
1823 ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) == 0));
1824 if ((types & CompareNilICStub::kCompareAgainstUndetectable) != 0) {
1825 if (needs_or) if_nil.Or();
1826 if_nil.If<HIsUndetectableAndBranch>(value);
1827 } else {
1828 if_nil.Then();
1829 if_nil.Else();
1830 if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
1831 BuildCheckNonSmi(value);
1832 // For ICs, the map checked below is a sentinel map that gets replaced by
1833 // the monomorphic map when the code is used as a template to generate a
1834 // new IC. For optimized functions, there is no sentinel map, the map
1835 // emitted below is the actual monomorphic map.
1836 BuildCheckMap(value, map);
1837 } else {
1838 if (kind == kNonStrictEquality) {
1839 if_nil.Deopt();
1840 }
1841 }
1842 }
1843
1844 if_nil.CaptureContinuation(continuation);
1845}
1846
1847
1848HValue* HGraphBuilder::BuildCreateAllocationSiteInfo(HValue* previous_object,
1849 int previous_object_size,
1850 HValue* payload) {
1851 HInnerAllocatedObject* alloc_site = new(zone())
1852 HInnerAllocatedObject(previous_object, previous_object_size);
1853 AddInstruction(alloc_site);
1854 Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map());
1855 BuildStoreMap(alloc_site, alloc_site_map);
1856 AddInstruction(new(zone()) HStoreNamedField(alloc_site,
1857 isolate()->factory()->payload_string(),
1858 payload,
1859 true,
danno@chromium.orgf005df62013-04-30 16:36:45 +00001860 Representation::Tagged(),
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001861 AllocationSiteInfo::kPayloadOffset));
1862 return alloc_site;
1863}
1864
1865
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001866HInstruction* HGraphBuilder::BuildGetNativeContext(HValue* context) {
1867 HInstruction* global_object = AddInstruction(new(zone())
1868 HGlobalObject(context));
1869 HInstruction* native_context = AddInstruction(new(zone())
1870 HLoadNamedField(global_object, true, Representation::Tagged(),
1871 GlobalObject::kNativeContextOffset));
1872 return native_context;
1873}
1874
1875
1876HInstruction* HGraphBuilder::BuildGetArrayFunction(HValue* context) {
1877 HInstruction* native_context = BuildGetNativeContext(context);
1878 int offset = Context::kHeaderSize +
1879 kPointerSize * Context::ARRAY_FUNCTION_INDEX;
1880 HInstruction* array_function = AddInstruction(new(zone())
1881 HLoadNamedField(native_context, true, Representation::Tagged(), offset));
1882 return array_function;
1883}
1884
1885
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001886HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
1887 ElementsKind kind,
1888 HValue* allocation_site_payload,
1889 AllocationSiteMode mode) :
1890 builder_(builder),
1891 kind_(kind),
1892 allocation_site_payload_(allocation_site_payload) {
1893 if (mode == DONT_TRACK_ALLOCATION_SITE) {
1894 mode_ = mode;
1895 } else {
1896 mode_ = AllocationSiteInfo::GetMode(kind);
1897 }
1898}
1899
1900
1901HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode(HValue* context) {
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001902 HInstruction* native_context = builder()->BuildGetNativeContext(context);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001903 int offset = Context::kHeaderSize +
1904 kPointerSize * Context::JS_ARRAY_MAPS_INDEX;
1905 HInstruction* map_array = AddInstruction(new(zone())
danno@chromium.orgf005df62013-04-30 16:36:45 +00001906 HLoadNamedField(native_context, true, Representation::Tagged(), offset));
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001907 offset = kind_ * kPointerSize + FixedArrayBase::kHeaderSize;
danno@chromium.orgf005df62013-04-30 16:36:45 +00001908 return AddInstruction(new(zone()) HLoadNamedField(
1909 map_array, true, Representation::Tagged(), offset));
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001910}
1911
1912
1913HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize(
1914 HValue* length_node) {
1915 HValue* context = builder()->environment()->LookupContext();
1916 ASSERT(length_node != NULL);
1917
1918 int base_size = JSArray::kSize;
1919 if (mode_ == TRACK_ALLOCATION_SITE) {
1920 base_size += AllocationSiteInfo::kSize;
1921 }
1922
1923 if (IsFastDoubleElementsKind(kind_)) {
1924 base_size += FixedDoubleArray::kHeaderSize;
1925 } else {
1926 base_size += FixedArray::kHeaderSize;
1927 }
1928
1929 HInstruction* elements_size_value = new(zone())
1930 HConstant(elements_size(), Representation::Integer32());
1931 AddInstruction(elements_size_value);
1932 HInstruction* mul = HMul::New(zone(), context, length_node,
1933 elements_size_value);
1934 mul->ChangeRepresentation(Representation::Integer32());
1935 mul->ClearFlag(HValue::kCanOverflow);
1936 AddInstruction(mul);
1937
1938 HInstruction* base = new(zone()) HConstant(base_size,
1939 Representation::Integer32());
1940 AddInstruction(base);
1941 HInstruction* total_size = HAdd::New(zone(), context, base, mul);
1942 total_size->ChangeRepresentation(Representation::Integer32());
1943 total_size->ClearFlag(HValue::kCanOverflow);
1944 AddInstruction(total_size);
1945 return total_size;
1946}
1947
1948
1949HValue* HGraphBuilder::JSArrayBuilder::EstablishEmptyArrayAllocationSize() {
1950 int base_size = JSArray::kSize;
1951 if (mode_ == TRACK_ALLOCATION_SITE) {
1952 base_size += AllocationSiteInfo::kSize;
1953 }
1954
1955 base_size += IsFastDoubleElementsKind(kind_)
1956 ? FixedDoubleArray::SizeFor(initial_capacity())
1957 : FixedArray::SizeFor(initial_capacity());
1958
1959 HConstant* array_size =
1960 new(zone()) HConstant(base_size, Representation::Integer32());
1961 AddInstruction(array_size);
1962 return array_size;
1963}
1964
1965
1966HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() {
1967 HValue* size_in_bytes = EstablishEmptyArrayAllocationSize();
1968 HConstant* capacity =
1969 new(zone()) HConstant(initial_capacity(), Representation::Integer32());
1970 AddInstruction(capacity);
1971 return AllocateArray(size_in_bytes,
1972 capacity,
1973 builder()->graph()->GetConstant0(),
1974 true);
1975}
1976
1977
1978HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity,
1979 HValue* length_field,
1980 bool fill_with_hole) {
1981 HValue* size_in_bytes = EstablishAllocationSize(capacity);
1982 return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole);
1983}
1984
1985
1986HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
1987 HValue* capacity,
1988 HValue* length_field,
1989 bool fill_with_hole) {
1990 HValue* context = builder()->environment()->LookupContext();
1991
1992 // Allocate (dealing with failure appropriately)
1993 HAllocate::Flags flags = HAllocate::DefaultFlags(kind_);
1994 HAllocate* new_object = new(zone()) HAllocate(context, size_in_bytes,
1995 HType::JSArray(), flags);
1996 AddInstruction(new_object);
1997
1998 // Fill in the fields: map, properties, length
1999 HValue* map = EmitMapCode(context);
2000 elements_location_ = builder()->BuildJSArrayHeader(new_object,
2001 map,
2002 mode_,
2003 allocation_site_payload_,
2004 length_field);
2005
2006 // Initialize the elements
2007 builder()->BuildInitializeElements(elements_location_, kind_, capacity);
2008
2009 if (fill_with_hole) {
2010 builder()->BuildFillElementsWithHole(context, elements_location_, kind_,
2011 graph()->GetConstant0(), capacity);
2012 }
2013
2014 return new_object;
2015}
2016
2017
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002018HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info,
2019 TypeFeedbackOracle* oracle)
2020 : HGraphBuilder(info),
2021 function_state_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +00002022 initial_function_state_(this, info, oracle, NORMAL_RETURN),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002023 ast_context_(NULL),
2024 break_scope_(NULL),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002025 inlined_count_(0),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002026 globals_(10, info->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002027 inline_bailout_(false) {
2028 // This is not initialized in the initializer list because the
2029 // constructor for the initial state relies on function_state_ == NULL
2030 // to know it's the initial state.
2031 function_state_= &initial_function_state_;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002032 InitializeAstVisitor();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002033}
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002034
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002035
2036HBasicBlock* HOptimizedGraphBuilder::CreateJoin(HBasicBlock* first,
2037 HBasicBlock* second,
2038 BailoutId join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002039 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002040 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002041 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002042 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002043 } else {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002044 HBasicBlock* join_block = graph()->CreateBasicBlock();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002045 first->Goto(join_block);
2046 second->Goto(join_block);
2047 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002048 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002049 }
2050}
2051
2052
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002053HBasicBlock* HOptimizedGraphBuilder::JoinContinue(IterationStatement* statement,
2054 HBasicBlock* exit_block,
2055 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002056 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002057 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002058 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002059 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002060 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002061 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002062}
2063
2064
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002065HBasicBlock* HOptimizedGraphBuilder::CreateLoop(IterationStatement* statement,
2066 HBasicBlock* loop_entry,
2067 HBasicBlock* body_exit,
2068 HBasicBlock* loop_successor,
2069 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +00002070 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002071 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002072 if (break_block != NULL) {
2073 if (loop_successor != NULL) loop_successor->Goto(break_block);
2074 break_block->SetJoinId(statement->ExitId());
2075 return break_block;
2076 }
2077 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002078}
2079
2080
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002081void HBasicBlock::FinishExit(HControlInstruction* instruction) {
2082 Finish(instruction);
2083 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002084}
2085
2086
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002087HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002088 : isolate_(info->isolate()),
2089 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002090 entry_block_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002091 blocks_(8, info->zone()),
2092 values_(16, info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002093 phi_list_(NULL),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002094 uint32_instructions_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002095 info_(info),
2096 zone_(info->zone()),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002097 is_recursive_(false),
2098 use_optimistic_licm_(false),
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002099 has_soft_deoptimize_(false),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002100 type_change_checksum_(0) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002101 if (info->IsStub()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00002102 HydrogenCodeStub* stub = info->code_stub();
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00002103 CodeStubInterfaceDescriptor* descriptor =
2104 stub->GetInterfaceDescriptor(isolate_);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002105 start_environment_ =
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00002106 new(zone_) HEnvironment(zone_, descriptor->environment_length());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002107 } else {
2108 start_environment_ =
2109 new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
2110 }
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00002111 start_environment_->set_ast_id(BailoutId::FunctionEntry());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002112 entry_block_ = CreateBasicBlock();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00002113 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002114}
2115
2116
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002117HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002118 HBasicBlock* result = new(zone()) HBasicBlock(this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002119 blocks_.Add(result, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002120 return result;
2121}
2122
2123
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00002124void HGraph::FinalizeUniqueValueIds() {
2125 AssertNoAllocation no_gc;
2126 ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread());
2127 for (int i = 0; i < blocks()->length(); ++i) {
2128 for (HInstruction* instr = blocks()->at(i)->first();
2129 instr != NULL;
2130 instr = instr->next()) {
2131 instr->FinalizeUniqueValueId();
2132 }
2133 }
2134}
2135
2136
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002137void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002138 if (!FLAG_use_canonicalizing) return;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002139 HPhase phase("H_Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002140 for (int i = 0; i < blocks()->length(); ++i) {
2141 HInstruction* instr = blocks()->at(i)->first();
2142 while (instr != NULL) {
2143 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002144 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002145 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002146 }
2147 }
2148}
2149
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002150// Block ordering was implemented with two mutually recursive methods,
2151// HGraph::Postorder and HGraph::PostorderLoopBlocks.
2152// The recursion could lead to stack overflow so the algorithm has been
2153// implemented iteratively.
2154// At a high level the algorithm looks like this:
2155//
2156// Postorder(block, loop_header) : {
2157// if (block has already been visited or is of another loop) return;
2158// mark block as visited;
2159// if (block is a loop header) {
2160// VisitLoopMembers(block, loop_header);
2161// VisitSuccessorsOfLoopHeader(block);
2162// } else {
2163// VisitSuccessors(block)
2164// }
2165// put block in result list;
2166// }
2167//
2168// VisitLoopMembers(block, outer_loop_header) {
2169// foreach (block b in block loop members) {
2170// VisitSuccessorsOfLoopMember(b, outer_loop_header);
2171// if (b is loop header) VisitLoopMembers(b);
2172// }
2173// }
2174//
2175// VisitSuccessorsOfLoopMember(block, outer_loop_header) {
2176// foreach (block b in block successors) Postorder(b, outer_loop_header)
2177// }
2178//
2179// VisitSuccessorsOfLoopHeader(block) {
2180// foreach (block b in block successors) Postorder(b, block)
2181// }
2182//
2183// VisitSuccessors(block, loop_header) {
2184// foreach (block b in block successors) Postorder(b, loop_header)
2185// }
2186//
2187// The ordering is started calling Postorder(entry, NULL).
2188//
2189// Each instance of PostorderProcessor represents the "stack frame" of the
2190// recursion, and particularly keeps the state of the loop (iteration) of the
2191// "Visit..." function it represents.
2192// To recycle memory we keep all the frames in a double linked list but
2193// this means that we cannot use constructors to initialize the frames.
2194//
2195class PostorderProcessor : public ZoneObject {
2196 public:
2197 // Back link (towards the stack bottom).
2198 PostorderProcessor* parent() {return father_; }
2199 // Forward link (towards the stack top).
2200 PostorderProcessor* child() {return child_; }
2201 HBasicBlock* block() { return block_; }
2202 HLoopInformation* loop() { return loop_; }
2203 HBasicBlock* loop_header() { return loop_header_; }
2204
2205 static PostorderProcessor* CreateEntryProcessor(Zone* zone,
2206 HBasicBlock* block,
2207 BitVector* visited) {
2208 PostorderProcessor* result = new(zone) PostorderProcessor(NULL);
2209 return result->SetupSuccessors(zone, block, NULL, visited);
2210 }
2211
2212 PostorderProcessor* PerformStep(Zone* zone,
2213 BitVector* visited,
2214 ZoneList<HBasicBlock*>* order) {
2215 PostorderProcessor* next =
2216 PerformNonBacktrackingStep(zone, visited, order);
2217 if (next != NULL) {
2218 return next;
2219 } else {
2220 return Backtrack(zone, visited, order);
2221 }
2222 }
2223
2224 private:
2225 explicit PostorderProcessor(PostorderProcessor* father)
2226 : father_(father), child_(NULL), successor_iterator(NULL) { }
2227
2228 // Each enum value states the cycle whose state is kept by this instance.
2229 enum LoopKind {
2230 NONE,
2231 SUCCESSORS,
2232 SUCCESSORS_OF_LOOP_HEADER,
2233 LOOP_MEMBERS,
2234 SUCCESSORS_OF_LOOP_MEMBER
2235 };
2236
2237 // Each "Setup..." method is like a constructor for a cycle state.
2238 PostorderProcessor* SetupSuccessors(Zone* zone,
2239 HBasicBlock* block,
2240 HBasicBlock* loop_header,
2241 BitVector* visited) {
2242 if (block == NULL || visited->Contains(block->block_id()) ||
2243 block->parent_loop_header() != loop_header) {
2244 kind_ = NONE;
2245 block_ = NULL;
2246 loop_ = NULL;
2247 loop_header_ = NULL;
2248 return this;
2249 } else {
2250 block_ = block;
2251 loop_ = NULL;
2252 visited->Add(block->block_id());
2253
2254 if (block->IsLoopHeader()) {
2255 kind_ = SUCCESSORS_OF_LOOP_HEADER;
2256 loop_header_ = block;
2257 InitializeSuccessors();
2258 PostorderProcessor* result = Push(zone);
2259 return result->SetupLoopMembers(zone, block, block->loop_information(),
2260 loop_header);
2261 } else {
2262 ASSERT(block->IsFinished());
2263 kind_ = SUCCESSORS;
2264 loop_header_ = loop_header;
2265 InitializeSuccessors();
2266 return this;
2267 }
2268 }
2269 }
2270
2271 PostorderProcessor* SetupLoopMembers(Zone* zone,
2272 HBasicBlock* block,
2273 HLoopInformation* loop,
2274 HBasicBlock* loop_header) {
2275 kind_ = LOOP_MEMBERS;
2276 block_ = block;
2277 loop_ = loop;
2278 loop_header_ = loop_header;
2279 InitializeLoopMembers();
2280 return this;
2281 }
2282
2283 PostorderProcessor* SetupSuccessorsOfLoopMember(
2284 HBasicBlock* block,
2285 HLoopInformation* loop,
2286 HBasicBlock* loop_header) {
2287 kind_ = SUCCESSORS_OF_LOOP_MEMBER;
2288 block_ = block;
2289 loop_ = loop;
2290 loop_header_ = loop_header;
2291 InitializeSuccessors();
2292 return this;
2293 }
2294
2295 // This method "allocates" a new stack frame.
2296 PostorderProcessor* Push(Zone* zone) {
2297 if (child_ == NULL) {
2298 child_ = new(zone) PostorderProcessor(this);
2299 }
2300 return child_;
2301 }
2302
2303 void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) {
2304 ASSERT(block_->end()->FirstSuccessor() == NULL ||
2305 order->Contains(block_->end()->FirstSuccessor()) ||
2306 block_->end()->FirstSuccessor()->IsLoopHeader());
2307 ASSERT(block_->end()->SecondSuccessor() == NULL ||
2308 order->Contains(block_->end()->SecondSuccessor()) ||
2309 block_->end()->SecondSuccessor()->IsLoopHeader());
2310 order->Add(block_, zone);
2311 }
2312
2313 // This method is the basic block to walk up the stack.
2314 PostorderProcessor* Pop(Zone* zone,
2315 BitVector* visited,
2316 ZoneList<HBasicBlock*>* order) {
2317 switch (kind_) {
2318 case SUCCESSORS:
2319 case SUCCESSORS_OF_LOOP_HEADER:
2320 ClosePostorder(order, zone);
2321 return father_;
2322 case LOOP_MEMBERS:
2323 return father_;
2324 case SUCCESSORS_OF_LOOP_MEMBER:
2325 if (block()->IsLoopHeader() && block() != loop_->loop_header()) {
2326 // In this case we need to perform a LOOP_MEMBERS cycle so we
2327 // initialize it and return this instead of father.
2328 return SetupLoopMembers(zone, block(),
2329 block()->loop_information(), loop_header_);
2330 } else {
2331 return father_;
2332 }
2333 case NONE:
2334 return father_;
2335 }
2336 UNREACHABLE();
2337 return NULL;
2338 }
2339
2340 // Walks up the stack.
2341 PostorderProcessor* Backtrack(Zone* zone,
2342 BitVector* visited,
2343 ZoneList<HBasicBlock*>* order) {
2344 PostorderProcessor* parent = Pop(zone, visited, order);
2345 while (parent != NULL) {
2346 PostorderProcessor* next =
2347 parent->PerformNonBacktrackingStep(zone, visited, order);
2348 if (next != NULL) {
2349 return next;
2350 } else {
2351 parent = parent->Pop(zone, visited, order);
2352 }
2353 }
2354 return NULL;
2355 }
2356
2357 PostorderProcessor* PerformNonBacktrackingStep(
2358 Zone* zone,
2359 BitVector* visited,
2360 ZoneList<HBasicBlock*>* order) {
2361 HBasicBlock* next_block;
2362 switch (kind_) {
2363 case SUCCESSORS:
2364 next_block = AdvanceSuccessors();
2365 if (next_block != NULL) {
2366 PostorderProcessor* result = Push(zone);
2367 return result->SetupSuccessors(zone, next_block,
2368 loop_header_, visited);
2369 }
2370 break;
2371 case SUCCESSORS_OF_LOOP_HEADER:
2372 next_block = AdvanceSuccessors();
2373 if (next_block != NULL) {
2374 PostorderProcessor* result = Push(zone);
2375 return result->SetupSuccessors(zone, next_block,
2376 block(), visited);
2377 }
2378 break;
2379 case LOOP_MEMBERS:
2380 next_block = AdvanceLoopMembers();
2381 if (next_block != NULL) {
2382 PostorderProcessor* result = Push(zone);
2383 return result->SetupSuccessorsOfLoopMember(next_block,
2384 loop_, loop_header_);
2385 }
2386 break;
2387 case SUCCESSORS_OF_LOOP_MEMBER:
2388 next_block = AdvanceSuccessors();
2389 if (next_block != NULL) {
2390 PostorderProcessor* result = Push(zone);
2391 return result->SetupSuccessors(zone, next_block,
2392 loop_header_, visited);
2393 }
2394 break;
2395 case NONE:
2396 return NULL;
2397 }
2398 return NULL;
2399 }
2400
2401 // The following two methods implement a "foreach b in successors" cycle.
2402 void InitializeSuccessors() {
2403 loop_index = 0;
2404 loop_length = 0;
2405 successor_iterator = HSuccessorIterator(block_->end());
2406 }
2407
2408 HBasicBlock* AdvanceSuccessors() {
2409 if (!successor_iterator.Done()) {
2410 HBasicBlock* result = successor_iterator.Current();
2411 successor_iterator.Advance();
2412 return result;
2413 }
2414 return NULL;
2415 }
2416
2417 // The following two methods implement a "foreach b in loop members" cycle.
2418 void InitializeLoopMembers() {
2419 loop_index = 0;
2420 loop_length = loop_->blocks()->length();
2421 }
2422
2423 HBasicBlock* AdvanceLoopMembers() {
2424 if (loop_index < loop_length) {
2425 HBasicBlock* result = loop_->blocks()->at(loop_index);
2426 loop_index++;
2427 return result;
2428 } else {
2429 return NULL;
2430 }
2431 }
2432
2433 LoopKind kind_;
2434 PostorderProcessor* father_;
2435 PostorderProcessor* child_;
2436 HLoopInformation* loop_;
2437 HBasicBlock* block_;
2438 HBasicBlock* loop_header_;
2439 int loop_index;
2440 int loop_length;
2441 HSuccessorIterator successor_iterator;
2442};
2443
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002444
2445void HGraph::OrderBlocks() {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002446 HPhase phase("H_Block ordering", isolate());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002447 BitVector visited(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002448
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002449 ZoneList<HBasicBlock*> reverse_result(8, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002450 HBasicBlock* start = blocks_[0];
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002451 PostorderProcessor* postorder =
2452 PostorderProcessor::CreateEntryProcessor(zone(), start, &visited);
2453 while (postorder != NULL) {
2454 postorder = postorder->PerformStep(zone(), &visited, &reverse_result);
2455 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002456 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002457 int index = 0;
2458 for (int i = reverse_result.length() - 1; i >= 0; --i) {
2459 HBasicBlock* b = reverse_result[i];
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002460 blocks_.Add(b, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002461 b->set_block_id(index++);
2462 }
2463}
2464
2465
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002466void HGraph::AssignDominators() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002467 HPhase phase("H_Assign dominators", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002468 for (int i = 0; i < blocks_.length(); ++i) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002469 HBasicBlock* block = blocks_[i];
2470 if (block->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002471 // Only the first predecessor of a loop header is from outside the loop.
2472 // All others are back edges, and thus cannot dominate the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002473 block->AssignCommonDominator(block->predecessors()->first());
2474 block->AssignLoopSuccessorDominators();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002475 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00002476 for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002477 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
2478 }
2479 }
2480 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002481}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002482
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002483
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002484// Mark all blocks that are dominated by an unconditional soft deoptimize to
2485// prevent code motion across those blocks.
2486void HGraph::PropagateDeoptimizingMark() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002487 HPhase phase("H_Propagate deoptimizing mark", this);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002488 // Skip this phase if there is nothing to be done anyway.
2489 if (!has_soft_deoptimize()) return;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002490 MarkAsDeoptimizingRecursively(entry_block());
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002491 NullifyUnreachableInstructions();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002492}
2493
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002494
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002495void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
2496 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
2497 HBasicBlock* dominated = block->dominated_blocks()->at(i);
2498 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
2499 MarkAsDeoptimizingRecursively(dominated);
2500 }
2501}
2502
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002503
2504void HGraph::NullifyUnreachableInstructions() {
yangguo@chromium.org28381b42013-01-21 14:39:38 +00002505 if (!FLAG_unreachable_code_elimination) return;
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00002506 int block_count = blocks_.length();
2507 for (int i = 0; i < block_count; ++i) {
2508 HBasicBlock* block = blocks_.at(i);
2509 bool nullify = false;
2510 const ZoneList<HBasicBlock*>* predecessors = block->predecessors();
2511 int predecessors_length = predecessors->length();
2512 bool all_predecessors_deoptimizing = (predecessors_length > 0);
2513 for (int j = 0; j < predecessors_length; ++j) {
2514 if (!predecessors->at(j)->IsDeoptimizing()) {
2515 all_predecessors_deoptimizing = false;
2516 break;
2517 }
2518 }
2519 if (all_predecessors_deoptimizing) nullify = true;
2520 for (HInstruction* instr = block->first(); instr != NULL;
2521 instr = instr->next()) {
2522 // Leave the basic structure of the graph intact.
2523 if (instr->IsBlockEntry()) continue;
2524 if (instr->IsControlInstruction()) continue;
2525 if (instr->IsSimulate()) continue;
2526 if (instr->IsEnterInlined()) continue;
2527 if (instr->IsLeaveInlined()) continue;
2528 if (nullify) {
2529 HInstruction* last_dummy = NULL;
2530 for (int j = 0; j < instr->OperandCount(); ++j) {
2531 HValue* operand = instr->OperandAt(j);
2532 // Insert an HDummyUse for each operand, unless the operand
2533 // is an HDummyUse itself. If it's even from the same block,
2534 // remember it as a potential replacement for the instruction.
2535 if (operand->IsDummyUse()) {
2536 if (operand->block() == instr->block() &&
2537 last_dummy == NULL) {
2538 last_dummy = HInstruction::cast(operand);
2539 }
2540 continue;
2541 }
2542 if (operand->IsControlInstruction()) {
2543 // Inserting a dummy use for a value that's not defined anywhere
2544 // will fail. Some instructions define fake inputs on such
2545 // values as control flow dependencies.
2546 continue;
2547 }
2548 HDummyUse* dummy = new(zone()) HDummyUse(operand);
2549 dummy->InsertBefore(instr);
2550 last_dummy = dummy;
2551 }
2552 if (last_dummy == NULL) last_dummy = GetConstant1();
2553 instr->DeleteAndReplaceWith(last_dummy);
2554 continue;
2555 }
2556 if (instr->IsSoftDeoptimize()) {
2557 ASSERT(block->IsDeoptimizing());
2558 nullify = true;
2559 }
2560 }
2561 }
2562}
2563
2564
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002565// Replace all phis consisting of a single non-loop operand plus any number of
2566// loop operands by that single non-loop operand.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002567void HGraph::EliminateRedundantPhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002568 HPhase phase("H_Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002569
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002570 // We do a simple fixed point iteration without any work list, because
2571 // machine-generated JavaScript can lead to a very dense Hydrogen graph with
2572 // an enormous work list and will consequently result in OOM. Experiments
2573 // showed that this simple algorithm is good enough, and even e.g. tracking
2574 // the set or range of blocks to consider is not a real improvement.
2575 bool need_another_iteration;
2576 ZoneList<HPhi*> redundant_phis(blocks_.length(), zone());
2577 do {
2578 need_another_iteration = false;
2579 for (int i = 0; i < blocks_.length(); ++i) {
2580 HBasicBlock* block = blocks_[i];
2581 for (int j = 0; j < block->phis()->length(); j++) {
2582 HPhi* phi = block->phis()->at(j);
2583 HValue* replacement = phi->GetRedundantReplacement();
2584 if (replacement != NULL) {
2585 // Remember phi to avoid concurrent modification of the block's phis.
2586 redundant_phis.Add(phi, zone());
2587 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2588 HValue* value = it.value();
2589 value->SetOperandAt(it.index(), replacement);
2590 need_another_iteration |= value->IsPhi();
2591 }
2592 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002593 }
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002594 for (int i = 0; i < redundant_phis.length(); i++) {
2595 block->RemovePhi(redundant_phis[i]);
2596 }
2597 redundant_phis.Clear();
2598 }
2599 } while (need_another_iteration);
2600
2601#if DEBUG
2602 // Make sure that we *really* removed all redundant phis.
2603 for (int i = 0; i < blocks_.length(); ++i) {
2604 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
2605 ASSERT(blocks_[i]->phis()->at(j)->GetRedundantReplacement() == NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002606 }
2607 }
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002608#endif
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002609}
2610
2611
2612void HGraph::EliminateUnreachablePhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002613 HPhase phase("H_Unreachable phi elimination", this);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002614
2615 // Initialize worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002616 ZoneList<HPhi*> phi_list(blocks_.length(), zone());
2617 ZoneList<HPhi*> worklist(blocks_.length(), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002618 for (int i = 0; i < blocks_.length(); ++i) {
2619 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
2620 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002621 phi_list.Add(phi, zone());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00002622 // We can't eliminate phis in the receiver position in the environment
2623 // because in case of throwing an error we need this value to
2624 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002625 if (phi->HasRealUses() || phi->IsReceiver()) {
2626 phi->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002627 worklist.Add(phi, zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002628 }
2629 }
2630 }
2631
2632 // Iteratively mark live phis.
2633 while (!worklist.is_empty()) {
2634 HPhi* phi = worklist.RemoveLast();
2635 for (int i = 0; i < phi->OperandCount(); i++) {
2636 HValue* operand = phi->OperandAt(i);
2637 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
2638 HPhi::cast(operand)->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002639 worklist.Add(HPhi::cast(operand), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002640 }
2641 }
2642 }
2643
2644 // Remove unreachable phis.
2645 for (int i = 0; i < phi_list.length(); i++) {
2646 HPhi* phi = phi_list[i];
2647 if (!phi->is_live()) {
2648 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002649 block->RemovePhi(phi);
2650 block->RecordDeletedPhi(phi->merged_index());
2651 }
2652 }
2653}
2654
2655
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002656bool HGraph::CheckArgumentsPhiUses() {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00002657 int block_count = blocks_.length();
2658 for (int i = 0; i < block_count; ++i) {
2659 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
2660 HPhi* phi = blocks_[i]->phis()->at(j);
2661 // We don't support phi uses of arguments for now.
2662 if (phi->CheckFlag(HValue::kIsArguments)) return false;
2663 }
2664 }
2665 return true;
2666}
2667
2668
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002669bool HGraph::CheckConstPhiUses() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002670 int block_count = blocks_.length();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002671 for (int i = 0; i < block_count; ++i) {
2672 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
2673 HPhi* phi = blocks_[i]->phis()->at(j);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002674 // Check for the hole value (from an uninitialized const).
2675 for (int k = 0; k < phi->OperandCount(); k++) {
2676 if (phi->OperandAt(k) == GetConstantHole()) return false;
2677 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002678 }
2679 }
2680 return true;
2681}
2682
2683
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002684void HGraph::CollectPhis() {
2685 int block_count = blocks_.length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002686 phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002687 for (int i = 0; i < block_count; ++i) {
2688 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
2689 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002690 phi_list_->Add(phi, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002691 }
2692 }
2693}
2694
2695
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002696void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002697 BitVector in_worklist(GetMaximumValueID(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002698 for (int i = 0; i < worklist->length(); ++i) {
2699 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
2700 in_worklist.Add(worklist->at(i)->id());
2701 }
2702
2703 while (!worklist->is_empty()) {
2704 HValue* current = worklist->RemoveLast();
2705 in_worklist.Remove(current->id());
2706 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002707 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
2708 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002709 if (!in_worklist.Contains(use->id())) {
2710 in_worklist.Add(use->id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002711 worklist->Add(use, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002712 }
2713 }
2714 }
2715 }
2716}
2717
2718
2719class HRangeAnalysis BASE_EMBEDDED {
2720 public:
ulan@chromium.org812308e2012-02-29 15:58:45 +00002721 explicit HRangeAnalysis(HGraph* graph) :
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002722 graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002723
2724 void Analyze();
2725
2726 private:
2727 void TraceRange(const char* msg, ...);
2728 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002729 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
2730 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002731 void InferRange(HValue* value);
2732 void RollBackTo(int index);
2733 void AddRange(HValue* value, Range* range);
2734
2735 HGraph* graph_;
ulan@chromium.org812308e2012-02-29 15:58:45 +00002736 Zone* zone_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002737 ZoneList<HValue*> changed_ranges_;
2738};
2739
2740
2741void HRangeAnalysis::TraceRange(const char* msg, ...) {
2742 if (FLAG_trace_range) {
2743 va_list arguments;
2744 va_start(arguments, msg);
2745 OS::VPrint(msg, arguments);
2746 va_end(arguments);
2747 }
2748}
2749
2750
2751void HRangeAnalysis::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002752 HPhase phase("H_Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002753 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002754}
2755
2756
2757void HRangeAnalysis::Analyze(HBasicBlock* block) {
2758 TraceRange("Analyzing block B%d\n", block->block_id());
2759
2760 int last_changed_range = changed_ranges_.length() - 1;
2761
2762 // Infer range based on control flow.
2763 if (block->predecessors()->length() == 1) {
2764 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002765 if (pred->end()->IsCompareIDAndBranch()) {
2766 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002767 }
2768 }
2769
2770 // Process phi instructions.
2771 for (int i = 0; i < block->phis()->length(); ++i) {
2772 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002773 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002774 }
2775
2776 // Go through all instructions of the current block.
2777 HInstruction* instr = block->first();
2778 while (instr != block->end()) {
2779 InferRange(instr);
2780 instr = instr->next();
2781 }
2782
2783 // Continue analysis in all dominated blocks.
2784 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
2785 Analyze(block->dominated_blocks()->at(i));
2786 }
2787
2788 RollBackTo(last_changed_range);
2789}
2790
2791
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002792void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
2793 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00002794 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002795 if (test->representation().IsInteger32()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002796 Token::Value op = test->token();
2797 if (test->SecondSuccessor() == dest) {
2798 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002799 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002800 Token::Value inverted_op = Token::ReverseCompareOp(op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002801 UpdateControlFlowRange(op, test->left(), test->right());
2802 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002803 }
2804}
2805
2806
2807// We know that value [op] other. Use this information to update the range on
2808// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002809void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
2810 HValue* value,
2811 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002812 Range temp_range;
2813 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002814 Range* new_range = NULL;
2815
2816 TraceRange("Control flow range infer %d %s %d\n",
2817 value->id(),
2818 Token::Name(op),
2819 other->id());
2820
2821 if (op == Token::EQ || op == Token::EQ_STRICT) {
2822 // The same range has to apply for value.
ulan@chromium.org812308e2012-02-29 15:58:45 +00002823 new_range = range->Copy(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002824 } else if (op == Token::LT || op == Token::LTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00002825 new_range = range->CopyClearLower(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002826 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002827 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002828 }
2829 } else if (op == Token::GT || op == Token::GTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00002830 new_range = range->CopyClearUpper(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002831 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002832 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002833 }
2834 }
2835
2836 if (new_range != NULL && !new_range->IsMostGeneric()) {
2837 AddRange(value, new_range);
2838 }
2839}
2840
2841
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002842void HRangeAnalysis::InferRange(HValue* value) {
2843 ASSERT(!value->HasRange());
2844 if (!value->representation().IsNone()) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00002845 value->ComputeInitialRange(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002846 Range* range = value->range();
2847 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
2848 value->id(),
2849 value->Mnemonic(),
2850 range->lower(),
2851 range->upper());
2852 }
2853}
2854
2855
2856void HRangeAnalysis::RollBackTo(int index) {
2857 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
2858 changed_ranges_[i]->RemoveLastAddedRange();
2859 }
2860 changed_ranges_.Rewind(index + 1);
2861}
2862
2863
2864void HRangeAnalysis::AddRange(HValue* value, Range* range) {
2865 Range* original_range = value->range();
ulan@chromium.org812308e2012-02-29 15:58:45 +00002866 value->AddNewRange(range, zone_);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002867 changed_ranges_.Add(value, zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002868 Range* new_range = value->range();
2869 TraceRange("Updated range of %d set to [%d,%d]\n",
2870 value->id(),
2871 new_range->lower(),
2872 new_range->upper());
2873 if (original_range != NULL) {
2874 TraceRange("Original range was [%d,%d]\n",
2875 original_range->lower(),
2876 original_range->upper());
2877 }
2878 TraceRange("New information was [%d,%d]\n",
2879 range->lower(),
2880 range->upper());
2881}
2882
2883
2884void TraceGVN(const char* msg, ...) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002885 va_list arguments;
2886 va_start(arguments, msg);
2887 OS::VPrint(msg, arguments);
2888 va_end(arguments);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002889}
2890
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002891// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when
2892// --trace-gvn is off.
2893#define TRACE_GVN_1(msg, a1) \
2894 if (FLAG_trace_gvn) { \
2895 TraceGVN(msg, a1); \
2896 }
2897
2898#define TRACE_GVN_2(msg, a1, a2) \
2899 if (FLAG_trace_gvn) { \
2900 TraceGVN(msg, a1, a2); \
2901 }
2902
2903#define TRACE_GVN_3(msg, a1, a2, a3) \
2904 if (FLAG_trace_gvn) { \
2905 TraceGVN(msg, a1, a2, a3); \
2906 }
2907
2908#define TRACE_GVN_4(msg, a1, a2, a3, a4) \
2909 if (FLAG_trace_gvn) { \
2910 TraceGVN(msg, a1, a2, a3, a4); \
2911 }
2912
2913#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \
2914 if (FLAG_trace_gvn) { \
2915 TraceGVN(msg, a1, a2, a3, a4, a5); \
2916 }
2917
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002918
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002919HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002920 : array_size_(other->array_size_),
2921 lists_size_(other->lists_size_),
2922 count_(other->count_),
2923 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002924 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
2925 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002926 free_list_head_(other->free_list_head_) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00002927 OS::MemCopy(
2928 array_, other->array_, array_size_ * sizeof(HValueMapListElement));
2929 OS::MemCopy(
2930 lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002931}
2932
2933
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002934void HValueMap::Kill(GVNFlagSet flags) {
2935 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags);
2936 if (!present_flags_.ContainsAnyOf(depends_flags)) return;
2937 present_flags_.RemoveAll();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002938 for (int i = 0; i < array_size_; ++i) {
2939 HValue* value = array_[i].value;
2940 if (value != NULL) {
2941 // Clear list of collisions first, so we know if it becomes empty.
2942 int kept = kNil; // List of kept elements.
2943 int next;
2944 for (int current = array_[i].next; current != kNil; current = next) {
2945 next = lists_[current].next;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002946 HValue* value = lists_[current].value;
2947 if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002948 // Drop it.
2949 count_--;
2950 lists_[current].next = free_list_head_;
2951 free_list_head_ = current;
2952 } else {
2953 // Keep it.
2954 lists_[current].next = kept;
2955 kept = current;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002956 present_flags_.Add(value->gvn_flags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002957 }
2958 }
2959 array_[i].next = kept;
2960
2961 // Now possibly drop directly indexed element.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002962 value = array_[i].value;
2963 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002964 count_--;
2965 int head = array_[i].next;
2966 if (head == kNil) {
2967 array_[i].value = NULL;
2968 } else {
2969 array_[i].value = lists_[head].value;
2970 array_[i].next = lists_[head].next;
2971 lists_[head].next = free_list_head_;
2972 free_list_head_ = head;
2973 }
2974 } else {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002975 present_flags_.Add(value->gvn_flags()); // Keep it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002976 }
2977 }
2978 }
2979}
2980
2981
2982HValue* HValueMap::Lookup(HValue* value) const {
2983 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
2984 uint32_t pos = Bound(hash);
2985 if (array_[pos].value != NULL) {
2986 if (array_[pos].value->Equals(value)) return array_[pos].value;
2987 int next = array_[pos].next;
2988 while (next != kNil) {
2989 if (lists_[next].value->Equals(value)) return lists_[next].value;
2990 next = lists_[next].next;
2991 }
2992 }
2993 return NULL;
2994}
2995
2996
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002997void HValueMap::Resize(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002998 ASSERT(new_size > count_);
2999 // Hashing the values into the new array has no more collisions than in the
3000 // old hash map, so we can use the existing lists_ array, if we are careful.
3001
3002 // Make sure we have at least one free element.
3003 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003004 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003005 }
3006
3007 HValueMapListElement* new_array =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003008 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003009 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
3010
3011 HValueMapListElement* old_array = array_;
3012 int old_size = array_size_;
3013
3014 int old_count = count_;
3015 count_ = 0;
3016 // Do not modify present_flags_. It is currently correct.
3017 array_size_ = new_size;
3018 array_ = new_array;
3019
3020 if (old_array != NULL) {
3021 // Iterate over all the elements in lists, rehashing them.
3022 for (int i = 0; i < old_size; ++i) {
3023 if (old_array[i].value != NULL) {
3024 int current = old_array[i].next;
3025 while (current != kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003026 Insert(lists_[current].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003027 int next = lists_[current].next;
3028 lists_[current].next = free_list_head_;
3029 free_list_head_ = current;
3030 current = next;
3031 }
3032 // Rehash the directly stored value.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003033 Insert(old_array[i].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003034 }
3035 }
3036 }
3037 USE(old_count);
3038 ASSERT(count_ == old_count);
3039}
3040
3041
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003042void HValueMap::ResizeLists(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003043 ASSERT(new_size > lists_size_);
3044
3045 HValueMapListElement* new_lists =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003046 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003047 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
3048
3049 HValueMapListElement* old_lists = lists_;
3050 int old_size = lists_size_;
3051
3052 lists_size_ = new_size;
3053 lists_ = new_lists;
3054
3055 if (old_lists != NULL) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003056 OS::MemCopy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003057 }
3058 for (int i = old_size; i < lists_size_; ++i) {
3059 lists_[i].next = free_list_head_;
3060 free_list_head_ = i;
3061 }
3062}
3063
3064
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003065void HValueMap::Insert(HValue* value, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003066 ASSERT(value != NULL);
3067 // Resizing when half of the hashtable is filled up.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003068 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003069 ASSERT(count_ < array_size_);
3070 count_++;
3071 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
3072 if (array_[pos].value == NULL) {
3073 array_[pos].value = value;
3074 array_[pos].next = kNil;
3075 } else {
3076 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003077 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003078 }
3079 int new_element_pos = free_list_head_;
3080 ASSERT(new_element_pos != kNil);
3081 free_list_head_ = lists_[free_list_head_].next;
3082 lists_[new_element_pos].value = value;
3083 lists_[new_element_pos].next = array_[pos].next;
3084 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
3085 array_[pos].next = new_element_pos;
3086 }
3087}
3088
3089
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003090HSideEffectMap::HSideEffectMap() : count_(0) {
3091 memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize);
3092}
3093
3094
3095HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003096 *this = *other; // Calls operator=.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003097}
3098
3099
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003100HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003101 if (this != &other) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003102 OS::MemCopy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003103 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003104 return *this;
3105}
3106
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003107void HSideEffectMap::Kill(GVNFlagSet flags) {
3108 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
3109 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
3110 if (flags.Contains(changes_flag)) {
3111 if (data_[i] != NULL) count_--;
3112 data_[i] = NULL;
3113 }
3114 }
3115}
3116
3117
3118void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) {
3119 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
3120 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
3121 if (flags.Contains(changes_flag)) {
3122 if (data_[i] == NULL) count_++;
3123 data_[i] = instr;
3124 }
3125 }
3126}
3127
3128
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003129class HStackCheckEliminator BASE_EMBEDDED {
3130 public:
3131 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
3132
3133 void Process();
3134
3135 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003136 HGraph* graph_;
3137};
3138
3139
3140void HStackCheckEliminator::Process() {
3141 // For each loop block walk the dominator tree from the backwards branch to
3142 // the loop header. If a call instruction is encountered the backwards branch
3143 // is dominated by a call and the stack check in the backwards branch can be
3144 // removed.
3145 for (int i = 0; i < graph_->blocks()->length(); i++) {
3146 HBasicBlock* block = graph_->blocks()->at(i);
3147 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003148 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
3149 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003150 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003151 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00003152 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003153 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00003154 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00003155 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003156 }
3157 instr = instr->next();
3158 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003159
3160 // Done when the loop header is processed.
3161 if (dominator == block) break;
3162
3163 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003164 dominator = dominator->dominator();
3165 }
3166 }
3167 }
3168}
3169
3170
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003171// Simple sparse set with O(1) add, contains, and clear.
3172class SparseSet {
3173 public:
3174 SparseSet(Zone* zone, int capacity)
3175 : capacity_(capacity),
3176 length_(0),
3177 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003178 sparse_(zone->NewArray<int>(capacity)) {
3179#ifndef NVALGRIND
3180 // Initialize the sparse array to make valgrind happy.
3181 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
3182#endif
3183 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003184
3185 bool Contains(int n) const {
3186 ASSERT(0 <= n && n < capacity_);
3187 int d = sparse_[n];
3188 return 0 <= d && d < length_ && dense_[d] == n;
3189 }
3190
3191 bool Add(int n) {
3192 if (Contains(n)) return false;
3193 dense_[length_] = n;
3194 sparse_[n] = length_;
3195 ++length_;
3196 return true;
3197 }
3198
3199 void Clear() { length_ = 0; }
3200
3201 private:
3202 int capacity_;
3203 int length_;
3204 int* dense_;
3205 int* sparse_;
3206
3207 DISALLOW_COPY_AND_ASSIGN(SparseSet);
3208};
3209
3210
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003211class HGlobalValueNumberer BASE_EMBEDDED {
3212 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003213 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003214 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003215 info_(info),
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003216 removed_side_effects_(false),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003217 block_side_effects_(graph->blocks()->length(), graph->zone()),
3218 loop_side_effects_(graph->blocks()->length(), graph->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003219 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
yangguo@chromium.org304cc332012-07-24 07:59:48 +00003220#ifdef DEBUG
3221 ASSERT(info->isolate()->optimizing_compiler_thread()->IsOptimizerThread() ||
3222 !info->isolate()->heap()->IsAllocationAllowed());
3223#endif
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003224 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
3225 graph_->zone());
3226 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
3227 graph_->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003228 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003229
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003230 // Returns true if values with side effects are removed.
3231 bool Analyze();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003232
3233 private:
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003234 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
3235 HBasicBlock* dominator,
3236 HBasicBlock* dominated);
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003237 void AnalyzeGraph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003238 void ComputeBlockSideEffects();
3239 void LoopInvariantCodeMotion();
3240 void ProcessLoopBlock(HBasicBlock* block,
3241 HBasicBlock* before_loop,
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003242 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00003243 GVNFlagSet* accumulated_first_time_depends,
3244 GVNFlagSet* accumulated_first_time_changes);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003245 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003246 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
3247
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003248 HGraph* graph() { return graph_; }
3249 CompilationInfo* info() { return info_; }
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003250 Zone* zone() const { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003251
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003252 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003253 CompilationInfo* info_;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003254 bool removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003255
3256 // A map of block IDs to their side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003257 ZoneList<GVNFlagSet> block_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003258
3259 // A map of loop header block IDs to their loop's side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003260 ZoneList<GVNFlagSet> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003261
3262 // Used when collecting side effects on paths from dominator to
3263 // dominated.
3264 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003265};
3266
3267
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003268bool HGlobalValueNumberer::Analyze() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003269 removed_side_effects_ = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003270 ComputeBlockSideEffects();
3271 if (FLAG_loop_invariant_code_motion) {
3272 LoopInvariantCodeMotion();
3273 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003274 AnalyzeGraph();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003275 return removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003276}
3277
3278
3279void HGlobalValueNumberer::ComputeBlockSideEffects() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003280 // The Analyze phase of GVN can be called multiple times. Clear loop side
3281 // effects before computing them to erase the contents from previous Analyze
3282 // passes.
3283 for (int i = 0; i < loop_side_effects_.length(); ++i) {
3284 loop_side_effects_[i].RemoveAll();
3285 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003286 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
3287 // Compute side effects for the block.
3288 HBasicBlock* block = graph_->blocks()->at(i);
3289 HInstruction* instr = block->first();
3290 int id = block->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003291 GVNFlagSet side_effects;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003292 while (instr != NULL) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003293 side_effects.Add(instr->ChangesFlags());
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00003294 if (instr->IsSoftDeoptimize()) {
3295 block_side_effects_[id].RemoveAll();
3296 side_effects.RemoveAll();
3297 break;
3298 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003299 instr = instr->next();
3300 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003301 block_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003302
3303 // Loop headers are part of their loop.
3304 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003305 loop_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003306 }
3307
3308 // Propagate loop side effects upwards.
3309 if (block->HasParentLoopHeader()) {
3310 int header_id = block->parent_loop_header()->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003311 loop_side_effects_[header_id].Add(block->IsLoopHeader()
3312 ? loop_side_effects_[id]
3313 : side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003314 }
3315 }
3316}
3317
3318
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003319SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) {
3320 char underlying_buffer[kLastFlag * 128];
3321 Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer));
3322#if DEBUG
3323 int offset = 0;
3324 const char* separator = "";
3325 const char* comma = ", ";
3326 buffer[0] = 0;
3327 uint32_t set_depends_on = 0;
3328 uint32_t set_changes = 0;
3329 for (int bit = 0; bit < kLastFlag; ++bit) {
3330 if ((flags.ToIntegral() & (1 << bit)) != 0) {
3331 if (bit % 2 == 0) {
3332 set_changes++;
3333 } else {
3334 set_depends_on++;
3335 }
3336 }
3337 }
3338 bool positive_changes = set_changes < (kLastFlag / 2);
3339 bool positive_depends_on = set_depends_on < (kLastFlag / 2);
3340 if (set_changes > 0) {
3341 if (positive_changes) {
3342 offset += OS::SNPrintF(buffer + offset, "changes [");
3343 } else {
3344 offset += OS::SNPrintF(buffer + offset, "changes all except [");
3345 }
3346 for (int bit = 0; bit < kLastFlag; ++bit) {
3347 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) {
3348 switch (static_cast<GVNFlag>(bit)) {
3349#define DECLARE_FLAG(type) \
3350 case kChanges##type: \
3351 offset += OS::SNPrintF(buffer + offset, separator); \
3352 offset += OS::SNPrintF(buffer + offset, #type); \
3353 separator = comma; \
3354 break;
3355GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
3356GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
3357#undef DECLARE_FLAG
3358 default:
3359 break;
3360 }
3361 }
3362 }
3363 offset += OS::SNPrintF(buffer + offset, "]");
3364 }
3365 if (set_depends_on > 0) {
3366 separator = "";
3367 if (set_changes > 0) {
3368 offset += OS::SNPrintF(buffer + offset, ", ");
3369 }
3370 if (positive_depends_on) {
3371 offset += OS::SNPrintF(buffer + offset, "depends on [");
3372 } else {
3373 offset += OS::SNPrintF(buffer + offset, "depends on all except [");
3374 }
3375 for (int bit = 0; bit < kLastFlag; ++bit) {
3376 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) {
3377 switch (static_cast<GVNFlag>(bit)) {
3378#define DECLARE_FLAG(type) \
3379 case kDependsOn##type: \
3380 offset += OS::SNPrintF(buffer + offset, separator); \
3381 offset += OS::SNPrintF(buffer + offset, #type); \
3382 separator = comma; \
3383 break;
3384GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
3385GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
3386#undef DECLARE_FLAG
3387 default:
3388 break;
3389 }
3390 }
3391 }
3392 offset += OS::SNPrintF(buffer + offset, "]");
3393 }
3394#else
3395 OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral());
3396#endif
3397 size_t string_len = strlen(underlying_buffer) + 1;
3398 ASSERT(string_len <= sizeof(underlying_buffer));
3399 char* result = new char[strlen(underlying_buffer) + 1];
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003400 OS::MemCopy(result, underlying_buffer, string_len);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003401 return SmartArrayPointer<char>(result);
3402}
3403
3404
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003405void HGlobalValueNumberer::LoopInvariantCodeMotion() {
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003406 TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n",
3407 graph_->use_optimistic_licm() ? "yes" : "no");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003408 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
3409 HBasicBlock* block = graph_->blocks()->at(i);
3410 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003411 GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003412 TRACE_GVN_2("Try loop invariant motion for block B%d %s\n",
3413 block->block_id(),
3414 *GetGVNFlagsString(side_effects));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003415
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003416 GVNFlagSet accumulated_first_time_depends;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00003417 GVNFlagSet accumulated_first_time_changes;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003418 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
3419 for (int j = block->block_id(); j <= last->block_id(); ++j) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003420 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00003421 &accumulated_first_time_depends,
3422 &accumulated_first_time_changes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003423 }
3424 }
3425 }
3426}
3427
3428
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003429void HGlobalValueNumberer::ProcessLoopBlock(
3430 HBasicBlock* block,
3431 HBasicBlock* loop_header,
3432 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00003433 GVNFlagSet* first_time_depends,
3434 GVNFlagSet* first_time_changes) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003435 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003436 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003437 TRACE_GVN_2("Loop invariant motion for B%d %s\n",
3438 block->block_id(),
3439 *GetGVNFlagsString(depends_flags));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003440 HInstruction* instr = block->first();
3441 while (instr != NULL) {
3442 HInstruction* next = instr->next();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003443 bool hoisted = false;
3444 if (instr->CheckFlag(HValue::kUseGVN)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003445 TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n",
3446 instr->id(),
3447 instr->Mnemonic(),
3448 *GetGVNFlagsString(instr->gvn_flags()),
3449 *GetGVNFlagsString(loop_kills));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003450 bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003451 if (can_hoist && !graph()->use_optimistic_licm()) {
3452 can_hoist = block->IsLoopSuccessorDominator();
3453 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003454
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003455 if (can_hoist) {
3456 bool inputs_loop_invariant = true;
3457 for (int i = 0; i < instr->OperandCount(); ++i) {
3458 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
3459 inputs_loop_invariant = false;
3460 }
3461 }
3462
3463 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003464 TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id());
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003465 // Move the instruction out of the loop.
3466 instr->Unlink();
3467 instr->InsertBefore(pre_header->end());
3468 if (instr->HasSideEffects()) removed_side_effects_ = true;
3469 hoisted = true;
3470 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003471 }
3472 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003473 if (!hoisted) {
3474 // If an instruction is not hoisted, we have to account for its side
3475 // effects when hoisting later HTransitionElementsKind instructions.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003476 GVNFlagSet previous_depends = *first_time_depends;
3477 GVNFlagSet previous_changes = *first_time_changes;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00003478 first_time_depends->Add(instr->DependsOnFlags());
3479 first_time_changes->Add(instr->ChangesFlags());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003480 if (!(previous_depends == *first_time_depends)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003481 TRACE_GVN_1("Updated first-time accumulated %s\n",
3482 *GetGVNFlagsString(*first_time_depends));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003483 }
3484 if (!(previous_changes == *first_time_changes)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003485 TRACE_GVN_1("Updated first-time accumulated %s\n",
3486 *GetGVNFlagsString(*first_time_changes));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00003487 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003488 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003489 instr = next;
3490 }
3491}
3492
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00003493
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003494bool HGlobalValueNumberer::AllowCodeMotion() {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003495 return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003496}
3497
3498
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003499bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
3500 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003501 // If we've disabled code motion or we're in a block that unconditionally
3502 // deoptimizes, don't move any instructions.
3503 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003504}
3505
3506
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003507GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003508 HBasicBlock* dominator, HBasicBlock* dominated) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003509 GVNFlagSet side_effects;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003510 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
3511 HBasicBlock* block = dominated->predecessors()->at(i);
3512 if (dominator->block_id() < block->block_id() &&
3513 block->block_id() < dominated->block_id() &&
3514 visited_on_paths_.Add(block->block_id())) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003515 side_effects.Add(block_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003516 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003517 side_effects.Add(loop_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003518 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003519 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
3520 dominator, block));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00003521 }
3522 }
3523 return side_effects;
3524}
3525
3526
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003527// Each instance of this class is like a "stack frame" for the recursive
3528// traversal of the dominator tree done during GVN (the stack is handled
3529// as a double linked list).
3530// We reuse frames when possible so the list length is limited by the depth
3531// of the dominator tree but this forces us to initialize each frame calling
3532// an explicit "Initialize" method instead of a using constructor.
3533class GvnBasicBlockState: public ZoneObject {
3534 public:
3535 static GvnBasicBlockState* CreateEntry(Zone* zone,
3536 HBasicBlock* entry_block,
3537 HValueMap* entry_map) {
3538 return new(zone)
3539 GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003540 }
3541
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003542 HBasicBlock* block() { return block_; }
3543 HValueMap* map() { return map_; }
3544 HSideEffectMap* dominators() { return &dominators_; }
3545
3546 GvnBasicBlockState* next_in_dominator_tree_traversal(
3547 Zone* zone,
3548 HBasicBlock** dominator) {
3549 // This assignment needs to happen before calling next_dominated() because
3550 // that call can reuse "this" if we are at the last dominated block.
3551 *dominator = block();
3552 GvnBasicBlockState* result = next_dominated(zone);
3553 if (result == NULL) {
3554 GvnBasicBlockState* dominator_state = pop();
3555 if (dominator_state != NULL) {
3556 // This branch is guaranteed not to return NULL because pop() never
3557 // returns a state where "is_done() == true".
3558 *dominator = dominator_state->block();
3559 result = dominator_state->next_dominated(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003560 } else {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003561 // Unnecessary (we are returning NULL) but done for cleanness.
3562 *dominator = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003563 }
3564 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003565 return result;
3566 }
3567
3568 private:
3569 void Initialize(HBasicBlock* block,
3570 HValueMap* map,
3571 HSideEffectMap* dominators,
3572 bool copy_map,
3573 Zone* zone) {
3574 block_ = block;
3575 map_ = copy_map ? map->Copy(zone) : map;
3576 dominated_index_ = -1;
3577 length_ = block->dominated_blocks()->length();
3578 if (dominators != NULL) {
3579 dominators_ = *dominators;
3580 }
3581 }
3582 bool is_done() { return dominated_index_ >= length_; }
3583
3584 GvnBasicBlockState(GvnBasicBlockState* previous,
3585 HBasicBlock* block,
3586 HValueMap* map,
3587 HSideEffectMap* dominators,
3588 Zone* zone)
3589 : previous_(previous), next_(NULL) {
3590 Initialize(block, map, dominators, true, zone);
3591 }
3592
3593 GvnBasicBlockState* next_dominated(Zone* zone) {
3594 dominated_index_++;
3595 if (dominated_index_ == length_ - 1) {
3596 // No need to copy the map for the last child in the dominator tree.
3597 Initialize(block_->dominated_blocks()->at(dominated_index_),
3598 map(),
3599 dominators(),
3600 false,
3601 zone);
3602 return this;
3603 } else if (dominated_index_ < length_) {
3604 return push(zone,
3605 block_->dominated_blocks()->at(dominated_index_),
3606 dominators());
3607 } else {
3608 return NULL;
3609 }
3610 }
3611
3612 GvnBasicBlockState* push(Zone* zone,
3613 HBasicBlock* block,
3614 HSideEffectMap* dominators) {
3615 if (next_ == NULL) {
3616 next_ =
3617 new(zone) GvnBasicBlockState(this, block, map(), dominators, zone);
3618 } else {
3619 next_->Initialize(block, map(), dominators, true, zone);
3620 }
3621 return next_;
3622 }
3623 GvnBasicBlockState* pop() {
3624 GvnBasicBlockState* result = previous_;
3625 while (result != NULL && result->is_done()) {
3626 TRACE_GVN_2("Backtracking from block B%d to block b%d\n",
3627 block()->block_id(),
3628 previous_->block()->block_id())
3629 result = result->previous_;
3630 }
3631 return result;
3632 }
3633
3634 GvnBasicBlockState* previous_;
3635 GvnBasicBlockState* next_;
3636 HBasicBlock* block_;
3637 HValueMap* map_;
3638 HSideEffectMap dominators_;
3639 int dominated_index_;
3640 int length_;
3641};
3642
3643// This is a recursive traversal of the dominator tree but it has been turned
3644// into a loop to avoid stack overflows.
3645// The logical "stack frames" of the recursion are kept in a list of
3646// GvnBasicBlockState instances.
3647void HGlobalValueNumberer::AnalyzeGraph() {
3648 HBasicBlock* entry_block = graph_->entry_block();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003649 HValueMap* entry_map = new(zone()) HValueMap(zone());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003650 GvnBasicBlockState* current =
3651 GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map);
3652
3653 while (current != NULL) {
3654 HBasicBlock* block = current->block();
3655 HValueMap* map = current->map();
3656 HSideEffectMap* dominators = current->dominators();
3657
3658 TRACE_GVN_2("Analyzing block B%d%s\n",
3659 block->block_id(),
3660 block->IsLoopHeader() ? " (loop header)" : "");
3661
3662 // If this is a loop header kill everything killed by the loop.
3663 if (block->IsLoopHeader()) {
3664 map->Kill(loop_side_effects_[block->block_id()]);
3665 }
3666
3667 // Go through all instructions of the current block.
3668 HInstruction* instr = block->first();
3669 while (instr != NULL) {
3670 HInstruction* next = instr->next();
3671 GVNFlagSet flags = instr->ChangesFlags();
3672 if (!flags.IsEmpty()) {
3673 // Clear all instructions in the map that are affected by side effects.
3674 // Store instruction as the dominating one for tracked side effects.
3675 map->Kill(flags);
3676 dominators->Store(flags, instr);
3677 TRACE_GVN_2("Instruction %d %s\n", instr->id(),
3678 *GetGVNFlagsString(flags));
3679 }
3680 if (instr->CheckFlag(HValue::kUseGVN)) {
3681 ASSERT(!instr->HasObservableSideEffects());
3682 HValue* other = map->Lookup(instr);
3683 if (other != NULL) {
3684 ASSERT(instr->Equals(other) && other->Equals(instr));
3685 TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n",
danno@chromium.org1044a4d2012-04-30 12:34:39 +00003686 instr->id(),
3687 instr->Mnemonic(),
3688 other->id(),
3689 other->Mnemonic());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003690 if (instr->HasSideEffects()) removed_side_effects_ = true;
3691 instr->DeleteAndReplaceWith(other);
3692 } else {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003693 map->Add(instr, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003694 }
3695 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00003696 if (instr->IsLinked() &&
3697 instr->CheckFlag(HValue::kTrackSideEffectDominators)) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003698 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
3699 HValue* other = dominators->at(i);
3700 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
3701 GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i);
3702 if (instr->DependsOnFlags().Contains(depends_on_flag) &&
3703 (other != NULL)) {
3704 TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n",
3705 i,
3706 instr->id(),
3707 instr->Mnemonic(),
3708 other->id(),
3709 other->Mnemonic());
3710 instr->SetSideEffectDominator(changes_flag, other);
3711 }
3712 }
3713 }
3714 instr = next;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003715 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003716
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003717 HBasicBlock* dominator_block;
3718 GvnBasicBlockState* next =
3719 current->next_in_dominator_tree_traversal(zone(), &dominator_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003720
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003721 if (next != NULL) {
3722 HBasicBlock* dominated = next->block();
3723 HValueMap* successor_map = next->map();
3724 HSideEffectMap* successor_dominators = next->dominators();
3725
3726 // Kill everything killed on any path between this block and the
3727 // dominated block. We don't have to traverse these paths if the
3728 // value map and the dominators list is already empty. If the range
3729 // of block ids (block_id, dominated_id) is empty there are no such
3730 // paths.
3731 if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) &&
3732 dominator_block->block_id() + 1 < dominated->block_id()) {
3733 visited_on_paths_.Clear();
3734 GVNFlagSet side_effects_on_all_paths =
3735 CollectSideEffectsOnPathsToDominatedBlock(dominator_block,
3736 dominated);
3737 successor_map->Kill(side_effects_on_all_paths);
3738 successor_dominators->Kill(side_effects_on_all_paths);
3739 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003740 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00003741 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003742 }
3743}
3744
3745
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003746void HInferRepresentation::AddToWorklist(HValue* current) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003747 if (current->representation().IsTagged()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003748 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
3749 if (in_worklist_.Contains(current->id())) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003750 worklist_.Add(current, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003751 in_worklist_.Add(current->id());
3752}
3753
3754
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003755void HInferRepresentation::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003756 HPhase phase("H_Infer representations", graph_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003757
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003758 // (1) Initialize bit vectors and count real uses. Each phi gets a
3759 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003760 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003761 int phi_count = phi_list->length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003762 ZoneList<BitVector*> connected_phis(phi_count, graph_->zone());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003763 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003764 phi_list->at(i)->InitRealUses(i);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003765 BitVector* connected_set = new(zone()) BitVector(phi_count, graph_->zone());
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00003766 connected_set->Add(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003767 connected_phis.Add(connected_set, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003768 }
3769
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003770 // (2) Do a fixed point iteration to find the set of connected phis. A
3771 // phi is connected to another phi if its value is used either directly or
3772 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003773 bool change = true;
3774 while (change) {
3775 change = false;
ricow@chromium.org55ee8072011-09-08 16:33:10 +00003776 // We normally have far more "forward edges" than "backward edges",
3777 // so we terminate faster when we walk backwards.
3778 for (int i = phi_count - 1; i >= 0; --i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003779 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003780 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
3781 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003782 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003783 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00003784 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
3785 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003786 }
3787 }
3788 }
3789 }
3790
ulan@chromium.org57ff8812013-05-10 08:16:55 +00003791 // Set truncation flags for groups of connected phis. This is a conservative
3792 // approximation; the flag will be properly re-computed after representations
3793 // have been determined.
3794 if (phi_count > 0) {
3795 BitVector* done = new(zone()) BitVector(phi_count, graph_->zone());
3796 for (int i = 0; i < phi_count; ++i) {
3797 if (done->Contains(i)) continue;
3798
3799 // Check if all uses of all connected phis in this group are truncating.
3800 bool all_uses_everywhere_truncating = true;
3801 for (BitVector::Iterator it(connected_phis.at(i));
3802 !it.Done();
3803 it.Advance()) {
3804 int index = it.Current();
3805 all_uses_everywhere_truncating &=
3806 phi_list->at(index)->CheckFlag(HInstruction::kTruncatingToInt32);
3807 done->Add(index);
3808 }
3809 if (all_uses_everywhere_truncating) {
3810 continue; // Great, nothing to do.
3811 }
3812 // Clear truncation flag of this group of connected phis.
3813 for (BitVector::Iterator it(connected_phis.at(i));
3814 !it.Done();
3815 it.Advance()) {
3816 int index = it.Current();
3817 phi_list->at(index)->ClearFlag(HInstruction::kTruncatingToInt32);
3818 }
3819 }
3820 }
3821
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003822 // Simplify constant phi inputs where possible.
ulan@chromium.org57ff8812013-05-10 08:16:55 +00003823 // This step uses kTruncatingToInt32 flags of phis.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003824 for (int i = 0; i < phi_count; ++i) {
3825 phi_list->at(i)->SimplifyConstantInputs();
3826 }
3827
3828 // Use the phi reachability information from step 2 to
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003829 // push information about values which can't be converted to integer
3830 // without deoptimization through the phi use-def chains, avoiding
3831 // unnecessary deoptimizations later.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003832 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003833 HPhi* phi = phi_list->at(i);
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003834 bool cti = phi->AllOperandsConvertibleToInteger();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003835 if (cti) continue;
3836
3837 for (BitVector::Iterator it(connected_phis.at(i));
3838 !it.Done();
3839 it.Advance()) {
3840 HPhi* phi = phi_list->at(it.Current());
3841 phi->set_is_convertible_to_integer(false);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003842 }
3843 }
3844
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003845 // Use the phi reachability information from step 2 to
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003846 // sum up the non-phi use counts of all connected phis.
3847 for (int i = 0; i < phi_count; ++i) {
3848 HPhi* phi = phi_list->at(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003849 for (BitVector::Iterator it(connected_phis.at(i));
3850 !it.Done();
3851 it.Advance()) {
3852 int index = it.Current();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003853 HPhi* it_use = phi_list->at(index);
3854 if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003855 }
3856 }
3857
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003858 // Initialize work list
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003859 for (int i = 0; i < graph_->blocks()->length(); ++i) {
3860 HBasicBlock* block = graph_->blocks()->at(i);
3861 const ZoneList<HPhi*>* phis = block->phis();
3862 for (int j = 0; j < phis->length(); ++j) {
3863 AddToWorklist(phis->at(j));
3864 }
3865
3866 HInstruction* current = block->first();
3867 while (current != NULL) {
3868 AddToWorklist(current);
3869 current = current->next();
3870 }
3871 }
3872
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003873 // Do a fixed point iteration, trying to improve representations
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003874 while (!worklist_.is_empty()) {
3875 HValue* current = worklist_.RemoveLast();
3876 in_worklist_.Remove(current->id());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003877 current->InferRepresentation(this);
3878 }
3879
3880 // Lastly: any instruction that we don't have representation information
3881 // for defaults to Tagged.
3882 for (int i = 0; i < graph_->blocks()->length(); ++i) {
3883 HBasicBlock* block = graph_->blocks()->at(i);
3884 const ZoneList<HPhi*>* phis = block->phis();
3885 for (int j = 0; j < phis->length(); ++j) {
3886 HPhi* phi = phis->at(j);
3887 if (phi->representation().IsNone()) {
3888 phi->ChangeRepresentation(Representation::Tagged());
3889 }
3890 }
3891 for (HInstruction* current = block->first();
3892 current != NULL; current = current->next()) {
3893 if (current->representation().IsNone() &&
3894 current->CheckFlag(HInstruction::kFlexibleRepresentation)) {
3895 current->ChangeRepresentation(Representation::Tagged());
3896 }
3897 }
3898 }
3899}
3900
3901
3902void HGraph::MergeRemovableSimulates() {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003903 ZoneList<HSimulate*> mergelist(2, zone());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003904 for (int i = 0; i < blocks()->length(); ++i) {
3905 HBasicBlock* block = blocks()->at(i);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003906 // Make sure the merge list is empty at the start of a block.
3907 ASSERT(mergelist.is_empty());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003908 // Nasty heuristic: Never remove the first simulate in a block. This
3909 // just so happens to have a beneficial effect on register allocation.
3910 bool first = true;
3911 for (HInstruction* current = block->first();
3912 current != NULL; current = current->next()) {
3913 if (current->IsLeaveInlined()) {
3914 // Never fold simulates from inlined environments into simulates
3915 // in the outer environment.
3916 // (Before each HEnterInlined, there is a non-foldable HSimulate
3917 // anyway, so we get the barrier in the other direction for free.)
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003918 // Simply remove all accumulated simulates without merging. This
3919 // is safe because simulates after instructions with side effects
3920 // are never added to the merge list.
3921 while (!mergelist.is_empty()) {
3922 mergelist.RemoveLast()->DeleteAndReplaceWith(NULL);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003923 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003924 continue;
3925 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003926 // Skip the non-simulates and the first simulate.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003927 if (!current->IsSimulate()) continue;
3928 if (first) {
3929 first = false;
3930 continue;
3931 }
3932 HSimulate* current_simulate = HSimulate::cast(current);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003933 if ((current_simulate->previous()->HasObservableSideEffects() &&
3934 !current_simulate->next()->IsSimulate()) ||
3935 !current_simulate->is_candidate_for_removal()) {
3936 // This simulate is not suitable for folding.
3937 // Fold the ones accumulated so far.
3938 current_simulate->MergeWith(&mergelist);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003939 continue;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003940 } else {
3941 // Accumulate this simulate for folding later on.
3942 mergelist.Add(current_simulate, zone());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003943 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003944 }
3945
3946 if (!mergelist.is_empty()) {
3947 // Merge the accumulated simulates at the end of the block.
3948 HSimulate* last = mergelist.RemoveLast();
3949 last->MergeWith(&mergelist);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003950 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003951 }
3952}
3953
3954
3955void HGraph::InitializeInferredTypes() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003956 HPhase phase("H_Inferring types", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003957 InitializeInferredTypes(0, this->blocks_.length() - 1);
3958}
3959
3960
3961void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
3962 for (int i = from_inclusive; i <= to_inclusive; ++i) {
3963 HBasicBlock* block = blocks_[i];
3964
3965 const ZoneList<HPhi*>* phis = block->phis();
3966 for (int j = 0; j < phis->length(); j++) {
3967 phis->at(j)->UpdateInferredType();
3968 }
3969
3970 HInstruction* current = block->first();
3971 while (current != NULL) {
3972 current->UpdateInferredType();
3973 current = current->next();
3974 }
3975
3976 if (block->IsLoopHeader()) {
3977 HBasicBlock* last_back_edge =
3978 block->loop_information()->GetLastBackEdge();
3979 InitializeInferredTypes(i + 1, last_back_edge->block_id());
3980 // Skip all blocks already processed by the recursive call.
3981 i = last_back_edge->block_id();
3982 // Update phis of the loop header now after the whole loop body is
3983 // guaranteed to be processed.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003984 ZoneList<HValue*> worklist(block->phis()->length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003985 for (int j = 0; j < block->phis()->length(); ++j) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003986 worklist.Add(block->phis()->at(j), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003987 }
3988 InferTypes(&worklist);
3989 }
3990 }
3991}
3992
3993
3994void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
3995 HValue* current = value;
3996 while (current != NULL) {
3997 if (visited->Contains(current->id())) return;
3998
3999 // For phis, we must propagate the check to all of its inputs.
4000 if (current->IsPhi()) {
4001 visited->Add(current->id());
4002 HPhi* phi = HPhi::cast(current);
4003 for (int i = 0; i < phi->OperandCount(); ++i) {
4004 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
4005 }
4006 break;
4007 }
4008
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004009 // For multiplication, division, and Math.min/max(), we must propagate
4010 // to the left and the right side.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004011 if (current->IsMul()) {
4012 HMul* mul = HMul::cast(current);
4013 mul->EnsureAndPropagateNotMinusZero(visited);
4014 PropagateMinusZeroChecks(mul->left(), visited);
4015 PropagateMinusZeroChecks(mul->right(), visited);
4016 } else if (current->IsDiv()) {
4017 HDiv* div = HDiv::cast(current);
4018 div->EnsureAndPropagateNotMinusZero(visited);
4019 PropagateMinusZeroChecks(div->left(), visited);
4020 PropagateMinusZeroChecks(div->right(), visited);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004021 } else if (current->IsMathMinMax()) {
4022 HMathMinMax* minmax = HMathMinMax::cast(current);
4023 visited->Add(minmax->id());
4024 PropagateMinusZeroChecks(minmax->left(), visited);
4025 PropagateMinusZeroChecks(minmax->right(), visited);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004026 }
4027
4028 current = current->EnsureAndPropagateNotMinusZero(visited);
4029 }
4030}
4031
4032
4033void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004034 HValue* use_value,
4035 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004036 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004037 // Insert the representation change right before its use. For phi-uses we
4038 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004039 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004040 if (use_value->IsPhi()) {
4041 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004042 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004043 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004044 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004045 // For constants we try to make the representation change at compile
4046 // time. When a representation change is not possible without loss of
4047 // information we treat constants like normal instructions and insert the
4048 // change instructions for them.
4049 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004050 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004051 bool deoptimize_on_undefined =
4052 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004053 if (value->IsConstant()) {
4054 HConstant* constant = HConstant::cast(value);
4055 // Try to create a new copy of the constant with the new representation.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004056 new_value = (is_truncating && to.IsInteger32())
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004057 ? constant->CopyToTruncatedInt32(zone())
4058 : constant->CopyToRepresentation(to, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004059 }
4060
4061 if (new_value == NULL) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004062 new_value = new(zone()) HChange(value, to,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004063 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004064 }
4065
4066 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004067 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004068}
4069
4070
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004071void HGraph::InsertRepresentationChangesForValue(HValue* value) {
4072 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004073 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004074 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004075
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004076 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
4077 HValue* use_value = it.value();
4078 int use_index = it.index();
4079 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004080 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004081 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004082 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004083 if (value->HasNoUses()) {
4084 ASSERT(value->IsConstant());
4085 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004086 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004087
4088 // The only purpose of a HForceRepresentation is to represent the value
4089 // after the (possible) HChange instruction. We make it disappear.
4090 if (value->IsForceRepresentation()) {
4091 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
4092 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004093}
4094
4095
4096void HGraph::InsertRepresentationChanges() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004097 HPhase phase("H_Representation changes", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004098
4099 // Compute truncation flag for phis: Initially assume that all
4100 // int32-phis allow truncation and iteratively remove the ones that
4101 // are used in an operation that does not allow a truncating
4102 // conversion.
ulan@chromium.org57ff8812013-05-10 08:16:55 +00004103 ZoneList<HPhi*> worklist(8, zone());
4104
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004105 for (int i = 0; i < phi_list()->length(); i++) {
4106 HPhi* phi = phi_list()->at(i);
4107 if (phi->representation().IsInteger32()) {
4108 phi->SetFlag(HValue::kTruncatingToInt32);
4109 }
4110 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00004111
4112 for (int i = 0; i < phi_list()->length(); i++) {
4113 HPhi* phi = phi_list()->at(i);
4114 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
4115 // If a Phi is used as a non-truncating int32 or as a double,
4116 // clear its "truncating" flag.
4117 HValue* use = it.value();
4118 Representation input_representation =
4119 use->RequiredInputRepresentation(it.index());
4120 if ((input_representation.IsInteger32() &&
4121 !use->CheckFlag(HValue::kTruncatingToInt32)) ||
4122 input_representation.IsDouble()) {
4123 if (FLAG_trace_representation) {
4124 PrintF("#%d Phi is not truncating because of #%d %s\n",
4125 phi->id(), it.value()->id(), it.value()->Mnemonic());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004126 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00004127 phi->ClearFlag(HValue::kTruncatingToInt32);
4128 worklist.Add(phi, zone());
4129 break;
4130 }
4131 }
4132 }
4133
4134 while (!worklist.is_empty()) {
4135 HPhi* current = worklist.RemoveLast();
4136 for (int i = 0; i < current->OperandCount(); ++i) {
4137 HValue* input = current->OperandAt(i);
4138 if (input->IsPhi() &&
4139 input->representation().IsInteger32() &&
4140 input->CheckFlag(HValue::kTruncatingToInt32)) {
4141 if (FLAG_trace_representation) {
4142 PrintF("#%d Phi is not truncating because of #%d %s\n",
4143 input->id(), current->id(), current->Mnemonic());
4144 }
4145 input->ClearFlag(HValue::kTruncatingToInt32);
4146 worklist.Add(HPhi::cast(input), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004147 }
4148 }
4149 }
4150
4151 for (int i = 0; i < blocks_.length(); ++i) {
4152 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004153 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
4154 for (int j = 0; j < phis->length(); j++) {
4155 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004156 }
4157
4158 // Process normal instructions.
4159 HInstruction* current = blocks_[i]->first();
4160 while (current != NULL) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004161 HInstruction* next = current->next();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00004162 InsertRepresentationChangesForValue(current);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004163 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004164 }
4165 }
4166}
4167
4168
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004169void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
4170 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
4171 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
4172 for (int i = 0; i < phi->OperandCount(); ++i) {
4173 HValue* input = phi->OperandAt(i);
4174 if (input->IsPhi()) {
4175 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
4176 }
4177 }
4178}
4179
4180
4181void HGraph::MarkDeoptimizeOnUndefined() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004182 HPhase phase("H_MarkDeoptimizeOnUndefined", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004183 // Compute DeoptimizeOnUndefined flag for phis.
4184 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004185 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004186 // double input representation, has this flag set.
4187 // The flag is used by HChange tagged->double, which must deoptimize
4188 // if one of its uses has this flag set.
4189 for (int i = 0; i < phi_list()->length(); i++) {
4190 HPhi* phi = phi_list()->at(i);
4191 if (phi->representation().IsDouble()) {
4192 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
4193 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
4194 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
4195 break;
4196 }
4197 }
4198 }
4199 }
4200}
4201
4202
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004203// Discover instructions that can be marked with kUint32 flag allowing
4204// them to produce full range uint32 values.
4205class Uint32Analysis BASE_EMBEDDED {
4206 public:
4207 explicit Uint32Analysis(Zone* zone) : zone_(zone), phis_(4, zone) { }
4208
4209 void Analyze(HInstruction* current);
4210
4211 void UnmarkUnsafePhis();
4212
4213 private:
4214 bool IsSafeUint32Use(HValue* val, HValue* use);
4215 bool Uint32UsesAreSafe(HValue* uint32val);
4216 bool CheckPhiOperands(HPhi* phi);
4217 void UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist);
4218
4219 Zone* zone_;
4220 ZoneList<HPhi*> phis_;
4221};
4222
4223
4224bool Uint32Analysis::IsSafeUint32Use(HValue* val, HValue* use) {
4225 // Operations that operatate on bits are safe.
4226 if (use->IsBitwise() ||
4227 use->IsShl() ||
4228 use->IsSar() ||
4229 use->IsShr() ||
4230 use->IsBitNot()) {
4231 return true;
4232 } else if (use->IsChange() || use->IsSimulate()) {
4233 // Conversions and deoptimization have special support for unt32.
4234 return true;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00004235 } else if (use->IsStoreKeyed()) {
4236 HStoreKeyed* store = HStoreKeyed::cast(use);
4237 if (store->is_external()) {
4238 // Storing a value into an external integer array is a bit level
4239 // operation.
4240 if (store->value() == val) {
4241 // Clamping or a conversion to double should have beed inserted.
4242 ASSERT(store->elements_kind() != EXTERNAL_PIXEL_ELEMENTS);
4243 ASSERT(store->elements_kind() != EXTERNAL_FLOAT_ELEMENTS);
4244 ASSERT(store->elements_kind() != EXTERNAL_DOUBLE_ELEMENTS);
4245 return true;
4246 }
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004247 }
4248 }
4249
4250 return false;
4251}
4252
4253
4254// Iterate over all uses and verify that they are uint32 safe: either don't
4255// distinguish between int32 and uint32 due to their bitwise nature or
4256// have special support for uint32 values.
4257// Encountered phis are optimisitically treated as safe uint32 uses,
4258// marked with kUint32 flag and collected in the phis_ list. A separate
4259// path will be performed later by UnmarkUnsafePhis to clear kUint32 from
4260// phis that are not actually uint32-safe (it requries fix point iteration).
4261bool Uint32Analysis::Uint32UsesAreSafe(HValue* uint32val) {
4262 bool collect_phi_uses = false;
4263 for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
4264 HValue* use = it.value();
4265
4266 if (use->IsPhi()) {
4267 if (!use->CheckFlag(HInstruction::kUint32)) {
4268 // There is a phi use of this value from a phis that is not yet
4269 // collected in phis_ array. Separate pass is required.
4270 collect_phi_uses = true;
4271 }
4272
4273 // Optimistically treat phis as uint32 safe.
4274 continue;
4275 }
4276
4277 if (!IsSafeUint32Use(uint32val, use)) {
4278 return false;
4279 }
4280 }
4281
4282 if (collect_phi_uses) {
4283 for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
4284 HValue* use = it.value();
4285
4286 // There is a phi use of this value from a phis that is not yet
4287 // collected in phis_ array. Separate pass is required.
4288 if (use->IsPhi() && !use->CheckFlag(HInstruction::kUint32)) {
4289 use->SetFlag(HInstruction::kUint32);
4290 phis_.Add(HPhi::cast(use), zone_);
4291 }
4292 }
4293 }
4294
4295 return true;
4296}
4297
4298
4299// Analyze instruction and mark it with kUint32 if all its uses are uint32
4300// safe.
4301void Uint32Analysis::Analyze(HInstruction* current) {
4302 if (Uint32UsesAreSafe(current)) current->SetFlag(HInstruction::kUint32);
4303}
4304
4305
4306// Check if all operands to the given phi are marked with kUint32 flag.
4307bool Uint32Analysis::CheckPhiOperands(HPhi* phi) {
4308 if (!phi->CheckFlag(HInstruction::kUint32)) {
4309 // This phi is not uint32 safe. No need to check operands.
4310 return false;
4311 }
4312
4313 for (int j = 0; j < phi->OperandCount(); j++) {
4314 HValue* operand = phi->OperandAt(j);
4315 if (!operand->CheckFlag(HInstruction::kUint32)) {
4316 // Lazyly mark constants that fit into uint32 range with kUint32 flag.
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00004317 if (operand->IsInteger32Constant() &&
4318 operand->GetInteger32Constant() >= 0) {
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004319 operand->SetFlag(HInstruction::kUint32);
4320 continue;
4321 }
4322
4323 // This phi is not safe, some operands are not uint32 values.
4324 return false;
4325 }
4326 }
4327
4328 return true;
4329}
4330
4331
4332// Remove kUint32 flag from the phi itself and its operands. If any operand
4333// was a phi marked with kUint32 place it into a worklist for
4334// transitive clearing of kUint32 flag.
4335void Uint32Analysis::UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist) {
4336 phi->ClearFlag(HInstruction::kUint32);
4337 for (int j = 0; j < phi->OperandCount(); j++) {
4338 HValue* operand = phi->OperandAt(j);
4339 if (operand->CheckFlag(HInstruction::kUint32)) {
4340 operand->ClearFlag(HInstruction::kUint32);
4341 if (operand->IsPhi()) {
4342 worklist->Add(HPhi::cast(operand), zone_);
4343 }
4344 }
4345 }
4346}
4347
4348
4349void Uint32Analysis::UnmarkUnsafePhis() {
4350 // No phis were collected. Nothing to do.
4351 if (phis_.length() == 0) return;
4352
4353 // Worklist used to transitively clear kUint32 from phis that
4354 // are used as arguments to other phis.
4355 ZoneList<HPhi*> worklist(phis_.length(), zone_);
4356
4357 // Phi can be used as a uint32 value if and only if
4358 // all its operands are uint32 values and all its
4359 // uses are uint32 safe.
4360
4361 // Iterate over collected phis and unmark those that
4362 // are unsafe. When unmarking phi unmark its operands
4363 // and add it to the worklist if it is a phi as well.
4364 // Phis that are still marked as safe are shifted down
4365 // so that all safe phis form a prefix of the phis_ array.
4366 int phi_count = 0;
4367 for (int i = 0; i < phis_.length(); i++) {
4368 HPhi* phi = phis_[i];
4369
4370 if (CheckPhiOperands(phi) && Uint32UsesAreSafe(phi)) {
4371 phis_[phi_count++] = phi;
4372 } else {
4373 UnmarkPhi(phi, &worklist);
4374 }
4375 }
4376
4377 // Now phis array contains only those phis that have safe
4378 // non-phi uses. Start transitively clearing kUint32 flag
4379 // from phi operands of discovered non-safe phies until
4380 // only safe phies are left.
4381 while (!worklist.is_empty()) {
4382 while (!worklist.is_empty()) {
4383 HPhi* phi = worklist.RemoveLast();
4384 UnmarkPhi(phi, &worklist);
4385 }
4386
4387 // Check if any operands to safe phies were unmarked
4388 // turning a safe phi into unsafe. The same value
4389 // can flow into several phis.
4390 int new_phi_count = 0;
4391 for (int i = 0; i < phi_count; i++) {
4392 HPhi* phi = phis_[i];
4393
4394 if (CheckPhiOperands(phi)) {
4395 phis_[new_phi_count++] = phi;
4396 } else {
4397 UnmarkPhi(phi, &worklist);
4398 }
4399 }
4400 phi_count = new_phi_count;
4401 }
4402}
4403
4404
4405void HGraph::ComputeSafeUint32Operations() {
4406 if (!FLAG_opt_safe_uint32_operations || uint32_instructions_ == NULL) {
4407 return;
4408 }
4409
4410 Uint32Analysis analysis(zone());
4411 for (int i = 0; i < uint32_instructions_->length(); ++i) {
4412 HInstruction* current = uint32_instructions_->at(i);
4413 if (current->IsLinked() && current->representation().IsInteger32()) {
4414 analysis.Analyze(current);
4415 }
4416 }
4417
4418 // Some phis might have been optimistically marked with kUint32 flag.
4419 // Remove this flag from those phis that are unsafe and propagate
4420 // this information transitively potentially clearing kUint32 flag
4421 // from some non-phi operations that are used as operands to unsafe phis.
4422 analysis.UnmarkUnsafePhis();
4423}
4424
4425
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00004426void HGraph::ComputeMinusZeroChecks() {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004427 BitVector visited(GetMaximumValueID(), zone());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00004428 for (int i = 0; i < blocks_.length(); ++i) {
4429 for (HInstruction* current = blocks_[i]->first();
4430 current != NULL;
4431 current = current->next()) {
4432 if (current->IsChange()) {
4433 HChange* change = HChange::cast(current);
4434 // Propagate flags for negative zero checks upwards from conversions
4435 // int32-to-tagged and int32-to-double.
4436 Representation from = change->value()->representation();
4437 ASSERT(from.Equals(change->from()));
4438 if (from.IsInteger32()) {
4439 ASSERT(change->to().IsTagged() || change->to().IsDouble());
4440 ASSERT(visited.IsEmpty());
4441 PropagateMinusZeroChecks(change->value(), &visited);
4442 visited.Clear();
4443 }
4444 }
4445 }
4446 }
4447}
4448
4449
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004450// Implementation of utility class to encapsulate the translation state for
4451// a (possibly inlined) function.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004452FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004453 CompilationInfo* info,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004454 TypeFeedbackOracle* oracle,
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004455 InliningKind inlining_kind)
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004456 : owner_(owner),
4457 compilation_info_(info),
4458 oracle_(oracle),
4459 call_context_(NULL),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004460 inlining_kind_(inlining_kind),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004461 function_return_(NULL),
4462 test_context_(NULL),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00004463 entry_(NULL),
4464 arguments_elements_(NULL),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004465 outer_(owner->function_state()) {
4466 if (outer_ != NULL) {
4467 // State for an inline function.
4468 if (owner->ast_context()->IsTest()) {
4469 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
4470 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
4471 if_true->MarkAsInlineReturnTarget();
4472 if_false->MarkAsInlineReturnTarget();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004473 TestContext* outer_test_context = TestContext::cast(owner->ast_context());
4474 Expression* cond = outer_test_context->condition();
4475 TypeFeedbackOracle* outer_oracle = outer_test_context->oracle();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004476 // The AstContext constructor pushed on the context stack. This newed
4477 // instance is the reason that AstContext can't be BASE_EMBEDDED.
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004478 test_context_ =
4479 new TestContext(owner, cond, outer_oracle, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004480 } else {
4481 function_return_ = owner->graph()->CreateBasicBlock();
4482 function_return()->MarkAsInlineReturnTarget();
4483 }
4484 // Set this after possibly allocating a new TestContext above.
4485 call_context_ = owner->ast_context();
4486 }
4487
4488 // Push on the state stack.
4489 owner->set_function_state(this);
4490}
4491
4492
4493FunctionState::~FunctionState() {
4494 delete test_context_;
4495 owner_->set_function_state(outer_);
4496}
4497
4498
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004499// Implementation of utility classes to represent an expression's context in
4500// the AST.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004501AstContext::AstContext(HOptimizedGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004502 : owner_(owner),
4503 kind_(kind),
4504 outer_(owner->ast_context()),
4505 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004506 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004507#ifdef DEBUG
ulan@chromium.org967e2702012-02-28 09:49:15 +00004508 ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
lrn@chromium.org5d00b602011-01-05 09:51:43 +00004509 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004510#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004511}
4512
4513
4514AstContext::~AstContext() {
4515 owner_->set_ast_context(outer_); // Pop.
4516}
4517
4518
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004519EffectContext::~EffectContext() {
4520 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004521 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004522 (owner()->environment()->length() == original_length_ &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00004523 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004524}
4525
4526
4527ValueContext::~ValueContext() {
4528 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004529 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004530 (owner()->environment()->length() == original_length_ + 1 &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00004531 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004532}
4533
4534
4535void EffectContext::ReturnValue(HValue* value) {
4536 // The value is simply ignored.
4537}
4538
4539
4540void ValueContext::ReturnValue(HValue* value) {
4541 // The value is tracked in the bailout environment, and communicated
4542 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004543 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
4544 owner()->Bailout("bad value context for arguments value");
4545 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004546 owner()->Push(value);
4547}
4548
4549
4550void TestContext::ReturnValue(HValue* value) {
4551 BuildBranch(value);
4552}
4553
4554
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004555void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004556 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004557 owner()->AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004558 if (instr->HasObservableSideEffects()) {
4559 owner()->AddSimulate(ast_id, REMOVABLE_SIMULATE);
4560 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004561}
4562
4563
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004564void EffectContext::ReturnControl(HControlInstruction* instr,
4565 BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004566 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004567 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
4568 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
4569 instr->SetSuccessorAt(0, empty_true);
4570 instr->SetSuccessorAt(1, empty_false);
4571 owner()->current_block()->Finish(instr);
4572 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
4573 owner()->set_current_block(join);
4574}
4575
4576
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00004577void EffectContext::ReturnContinuation(HIfContinuation* continuation,
4578 BailoutId ast_id) {
4579 HBasicBlock* true_branch = NULL;
4580 HBasicBlock* false_branch = NULL;
4581 continuation->Continue(&true_branch, &false_branch, NULL);
4582 if (!continuation->IsTrueReachable()) {
4583 owner()->set_current_block(false_branch);
4584 } else if (!continuation->IsFalseReachable()) {
4585 owner()->set_current_block(true_branch);
4586 } else {
4587 HBasicBlock* join = owner()->CreateJoin(true_branch, false_branch, ast_id);
4588 owner()->set_current_block(join);
4589 }
4590}
4591
4592
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004593void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004594 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004595 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004596 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004597 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004598 owner()->AddInstruction(instr);
4599 owner()->Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004600 if (instr->HasObservableSideEffects()) {
4601 owner()->AddSimulate(ast_id, REMOVABLE_SIMULATE);
4602 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004603}
4604
4605
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004606void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004607 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004608 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
4609 return owner()->Bailout("bad value context for arguments object value");
4610 }
4611 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
4612 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
4613 instr->SetSuccessorAt(0, materialize_true);
4614 instr->SetSuccessorAt(1, materialize_false);
4615 owner()->current_block()->Finish(instr);
4616 owner()->set_current_block(materialize_true);
4617 owner()->Push(owner()->graph()->GetConstantTrue());
4618 owner()->set_current_block(materialize_false);
4619 owner()->Push(owner()->graph()->GetConstantFalse());
4620 HBasicBlock* join =
4621 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
4622 owner()->set_current_block(join);
4623}
4624
4625
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00004626void ValueContext::ReturnContinuation(HIfContinuation* continuation,
4627 BailoutId ast_id) {
4628 HBasicBlock* materialize_true = NULL;
4629 HBasicBlock* materialize_false = NULL;
4630 continuation->Continue(&materialize_true, &materialize_false, NULL);
4631 if (continuation->IsTrueReachable()) {
4632 owner()->set_current_block(materialize_true);
4633 owner()->Push(owner()->graph()->GetConstantTrue());
4634 owner()->set_current_block(materialize_true);
4635 }
4636 if (continuation->IsFalseReachable()) {
4637 owner()->set_current_block(materialize_false);
4638 owner()->Push(owner()->graph()->GetConstantFalse());
4639 owner()->set_current_block(materialize_false);
4640 }
4641 if (continuation->TrueAndFalseReachable()) {
4642 HBasicBlock* join =
4643 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
4644 owner()->set_current_block(join);
4645 }
4646}
4647
4648
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004649void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004650 ASSERT(!instr->IsControlInstruction());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004651 HOptimizedGraphBuilder* builder = owner();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004652 builder->AddInstruction(instr);
4653 // We expect a simulate after every expression with side effects, though
4654 // this one isn't actually needed (and wouldn't work if it were targeted).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004655 if (instr->HasObservableSideEffects()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004656 builder->Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004657 builder->AddSimulate(ast_id, REMOVABLE_SIMULATE);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004658 builder->Pop();
4659 }
4660 BuildBranch(instr);
4661}
4662
4663
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004664void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004665 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004666 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
4667 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
4668 instr->SetSuccessorAt(0, empty_true);
4669 instr->SetSuccessorAt(1, empty_false);
4670 owner()->current_block()->Finish(instr);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00004671 empty_true->Goto(if_true(), owner()->function_state());
4672 empty_false->Goto(if_false(), owner()->function_state());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004673 owner()->set_current_block(NULL);
4674}
4675
4676
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00004677void TestContext::ReturnContinuation(HIfContinuation* continuation,
4678 BailoutId ast_id) {
4679 HBasicBlock* true_branch = NULL;
4680 HBasicBlock* false_branch = NULL;
4681 continuation->Continue(&true_branch, &false_branch, NULL);
4682 if (continuation->IsTrueReachable()) {
4683 true_branch->Goto(if_true(), owner()->function_state());
4684 }
4685 if (continuation->IsFalseReachable()) {
4686 false_branch->Goto(if_false(), owner()->function_state());
4687 }
4688 owner()->set_current_block(NULL);
4689}
4690
4691
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004692void TestContext::BuildBranch(HValue* value) {
4693 // We expect the graph to be in edge-split form: there is no edge that
4694 // connects a branch node to a join node. We conservatively ensure that
4695 // property by always adding an empty block on the outgoing edges of this
4696 // branch.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004697 HOptimizedGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004698 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004699 builder->Bailout("arguments object value in a test context");
4700 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004701 if (value->IsConstant()) {
4702 HConstant* constant_value = HConstant::cast(value);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00004703 if (constant_value->BooleanValue()) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004704 builder->current_block()->Goto(if_true(), builder->function_state());
4705 } else {
4706 builder->current_block()->Goto(if_false(), builder->function_state());
4707 }
4708 builder->set_current_block(NULL);
4709 return;
4710 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004711 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
4712 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004713 TypeFeedbackId test_id = condition()->test_id();
4714 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org2c99e282011-07-28 09:15:17 +00004715 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004716 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004717
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004718 empty_true->Goto(if_true(), builder->function_state());
4719 empty_false->Goto(if_false(), builder->function_state());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004720 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004721}
4722
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004723
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004724// HOptimizedGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004725#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004726 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00004727 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004728 if (HasStackOverflow()) return; \
4729 } while (false)
4730
4731
danno@chromium.org160a7b02011-04-18 15:51:38 +00004732#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004733 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00004734 call; \
4735 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004736 } while (false)
4737
4738
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004739void HOptimizedGraphBuilder::Bailout(const char* reason) {
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004740 info()->set_bailout_reason(reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004741 SetStackOverflow();
4742}
4743
4744
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004745void HOptimizedGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004746 EffectContext for_effect(this);
4747 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004748}
4749
4750
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004751void HOptimizedGraphBuilder::VisitForValue(Expression* expr,
4752 ArgumentsAllowedFlag flag) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004753 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004754 Visit(expr);
4755}
4756
4757
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004758void HOptimizedGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004759 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004760 for_value.set_for_typeof(true);
4761 Visit(expr);
4762}
4763
4764
4765
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004766void HOptimizedGraphBuilder::VisitForControl(Expression* expr,
4767 HBasicBlock* true_block,
4768 HBasicBlock* false_block) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004769 TestContext for_test(this, expr, oracle(), true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004770 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004771}
4772
4773
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004774void HOptimizedGraphBuilder::VisitArgument(Expression* expr) {
verwaest@chromium.org753aee42012-07-17 16:15:42 +00004775 CHECK_ALIVE(VisitForValue(expr));
4776 Push(AddInstruction(new(zone()) HPushArgument(Pop())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004777}
4778
4779
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004780void HOptimizedGraphBuilder::VisitArgumentList(
4781 ZoneList<Expression*>* arguments) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004782 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004783 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004784 }
4785}
4786
4787
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004788void HOptimizedGraphBuilder::VisitExpressions(
4789 ZoneList<Expression*>* exprs) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004790 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004791 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004792 }
4793}
4794
4795
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004796bool HOptimizedGraphBuilder::BuildGraph() {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004797 if (info()->function()->is_generator()) {
4798 Bailout("function is a generator");
4799 return false;
4800 }
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004801 Scope* scope = info()->scope();
4802 if (scope->HasIllegalRedeclaration()) {
4803 Bailout("function with illegal redeclaration");
4804 return false;
4805 }
4806 if (scope->calls_eval()) {
4807 Bailout("function calls eval");
4808 return false;
4809 }
4810 SetUpScope(scope);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004811
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004812 // Add an edge to the body entry. This is warty: the graph's start
4813 // environment will be used by the Lithium translation as the initial
4814 // environment on graph entry, but it has now been mutated by the
4815 // Hydrogen translation of the instructions in the start block. This
4816 // environment uses values which have not been defined yet. These
4817 // Hydrogen instructions will then be replayed by the Lithium
4818 // translation, so they cannot have an environment effect. The edge to
4819 // the body's entry block (along with some special logic for the start
4820 // block in HInstruction::InsertAfter) seals the start block from
4821 // getting unwanted instructions inserted.
4822 //
4823 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
4824 // Make the Hydrogen instructions in the initial block into Hydrogen
4825 // values (but not instructions), present in the initial environment and
4826 // not replayed by the Lithium translation.
4827 HEnvironment* initial_env = environment()->CopyWithoutHistory();
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00004828 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004829 current_block()->Goto(body_entry);
4830 body_entry->SetJoinId(BailoutId::FunctionEntry());
4831 set_current_block(body_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004832
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004833 // Handle implicit declaration of the function name in named function
4834 // expressions before other declarations.
4835 if (scope->is_function_scope() && scope->function() != NULL) {
4836 VisitVariableDeclaration(scope->function());
4837 }
4838 VisitDeclarations(scope->declarations());
4839 AddSimulate(BailoutId::Declarations());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004840
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004841 HValue* context = environment()->LookupContext();
4842 AddInstruction(
4843 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
fschneider@chromium.org1805e212011-09-05 10:49:12 +00004844
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004845 VisitStatements(info()->function()->body());
4846 if (HasStackOverflow()) return false;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00004847
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004848 if (current_block() != NULL) {
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00004849 AddReturn(graph()->GetConstantUndefined());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004850 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004851 }
4852
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004853 // If the checksum of the number of type info changes is the same as the
4854 // last time this function was compiled, then this recompile is likely not
4855 // due to missing/inadequate type feedback, but rather too aggressive
4856 // optimization. Disable optimistic LICM in that case.
4857 Handle<Code> unoptimized_code(info()->shared_info()->code());
4858 ASSERT(unoptimized_code->kind() == Code::FUNCTION);
4859 Handle<TypeFeedbackInfo> type_info(
4860 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
4861 int checksum = type_info->own_type_change_checksum();
4862 int composite_checksum = graph()->update_type_change_checksum(checksum);
4863 graph()->set_use_optimistic_licm(
4864 !type_info->matches_inlined_type_change_checksum(composite_checksum));
4865 type_info->set_inlined_type_change_checksum(composite_checksum);
4866
4867 return true;
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004868}
4869
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004870
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004871void HGraph::GlobalValueNumbering() {
4872 // Perform common subexpression elimination and loop-invariant code motion.
4873 if (FLAG_use_gvn) {
4874 HPhase phase("H_Global value numbering", this);
4875 HGlobalValueNumberer gvn(this, info());
4876 bool removed_side_effects = gvn.Analyze();
4877 // Trigger a second analysis pass to further eliminate duplicate values that
4878 // could only be discovered by removing side-effect-generating instructions
4879 // during the first pass.
4880 if (FLAG_smi_only_arrays && removed_side_effects) {
4881 removed_side_effects = gvn.Analyze();
4882 ASSERT(!removed_side_effects);
4883 }
4884 }
4885}
4886
4887
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004888bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004889 *bailout_reason = SmartArrayPointer<char>();
4890 OrderBlocks();
4891 AssignDominators();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004892
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00004893 // We need to create a HConstant "zero" now so that GVN will fold every
4894 // zero-valued constant in the graph together.
4895 // The constant is needed to make idef-based bounds check work: the pass
4896 // evaluates relations with "zero" and that zero cannot be created after GVN.
4897 GetConstant0();
4898
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004899#ifdef DEBUG
4900 // Do a full verify after building the graph and computing dominators.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004901 Verify(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004902#endif
4903
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004904 PropagateDeoptimizingMark();
4905 if (!CheckConstPhiUses()) {
4906 *bailout_reason = SmartArrayPointer<char>(StrDup(
4907 "Unsupported phi use of const variable"));
4908 return false;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004909 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004910 EliminateRedundantPhis();
4911 if (!CheckArgumentsPhiUses()) {
4912 *bailout_reason = SmartArrayPointer<char>(StrDup(
4913 "Unsupported phi use of arguments"));
4914 return false;
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00004915 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004916 if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
4917 CollectPhis();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004918
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004919 if (has_osr_loop_entry()) {
4920 const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004921 for (int j = 0; j < phis->length(); j++) {
4922 HPhi* phi = phis->at(j);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004923 osr_values()->at(phi->merged_index())->set_incoming_value(phi);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00004924 }
4925 }
4926
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004927 HInferRepresentation rep(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004928 rep.Analyze();
4929
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004930 // Remove HSimulate instructions that have turned out not to be needed
4931 // after all by folding them into the following HSimulate.
4932 // This must happen after inferring representations.
4933 MergeRemovableSimulates();
4934
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004935 MarkDeoptimizeOnUndefined();
4936 InsertRepresentationChanges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004937
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004938 InitializeInferredTypes();
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004939
4940 // Must be performed before canonicalization to ensure that Canonicalize
4941 // will not remove semantically meaningful ToInt32 operations e.g. BIT_OR with
4942 // zero.
4943 ComputeSafeUint32Operations();
4944
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004945 Canonicalize();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004946
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004947 GlobalValueNumbering();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004948
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004949 if (FLAG_use_range) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004950 HRangeAnalysis rangeAnalysis(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004951 rangeAnalysis.Analyze();
4952 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004953 ComputeMinusZeroChecks();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004954
4955 // Eliminate redundant stack checks on backwards branches.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004956 HStackCheckEliminator sce(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004957 sce.Process();
4958
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004959 if (FLAG_idefs) SetupInformativeDefinitions();
4960 if (FLAG_array_bounds_checks_elimination && !FLAG_idefs) {
4961 EliminateRedundantBoundsChecks();
4962 }
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004963 if (FLAG_array_index_dehoisting) DehoistSimpleArrayIndexComputations();
jkummerow@chromium.orgc1956672012-10-11 15:57:38 +00004964 if (FLAG_dead_code_elimination) DeadCodeElimination();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004965
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004966 RestoreActualValues();
4967
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00004968 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004969}
4970
4971
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004972void HGraph::SetupInformativeDefinitionsInBlock(HBasicBlock* block) {
4973 for (int phi_index = 0; phi_index < block->phis()->length(); phi_index++) {
4974 HPhi* phi = block->phis()->at(phi_index);
4975 phi->AddInformativeDefinitions();
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00004976 phi->SetFlag(HValue::kIDefsProcessingDone);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004977 // We do not support phis that "redefine just one operand".
4978 ASSERT(!phi->IsInformativeDefinition());
4979 }
4980
4981 for (HInstruction* i = block->first(); i != NULL; i = i->next()) {
4982 i->AddInformativeDefinitions();
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00004983 i->SetFlag(HValue::kIDefsProcessingDone);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00004984 i->UpdateRedefinedUsesWhileSettingUpInformativeDefinitions();
4985 }
4986}
4987
4988
4989// This method is recursive, so if its stack frame is large it could
4990// cause a stack overflow.
4991// To keep the individual stack frames small we do the actual work inside
4992// SetupInformativeDefinitionsInBlock();
4993void HGraph::SetupInformativeDefinitionsRecursively(HBasicBlock* block) {
4994 SetupInformativeDefinitionsInBlock(block);
4995 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
4996 SetupInformativeDefinitionsRecursively(block->dominated_blocks()->at(i));
4997 }
svenpanne@chromium.org876cca82013-03-18 14:43:20 +00004998
4999 for (HInstruction* i = block->first(); i != NULL; i = i->next()) {
5000 if (i->IsBoundsCheck()) {
5001 HBoundsCheck* check = HBoundsCheck::cast(i);
5002 check->ApplyIndexChange();
5003 }
5004 }
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00005005}
5006
5007
5008void HGraph::SetupInformativeDefinitions() {
5009 HPhase phase("H_Setup informative definitions", this);
5010 SetupInformativeDefinitionsRecursively(entry_block());
5011}
5012
5013
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005014// We try to "factor up" HBoundsCheck instructions towards the root of the
5015// dominator tree.
5016// For now we handle checks where the index is like "exp + int32value".
5017// If in the dominator tree we check "exp + v1" and later (dominated)
5018// "exp + v2", if v2 <= v1 we can safely remove the second check, and if
5019// v2 > v1 we can use v2 in the 1st check and again remove the second.
5020// To do so we keep a dictionary of all checks where the key if the pair
5021// "exp, length".
5022// The class BoundsCheckKey represents this key.
5023class BoundsCheckKey : public ZoneObject {
5024 public:
5025 HValue* IndexBase() const { return index_base_; }
5026 HValue* Length() const { return length_; }
5027
5028 uint32_t Hash() {
5029 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
5030 }
5031
5032 static BoundsCheckKey* Create(Zone* zone,
5033 HBoundsCheck* check,
5034 int32_t* offset) {
ulan@chromium.org56c14af2012-09-20 12:51:09 +00005035 if (!check->index()->representation().IsInteger32()) return NULL;
5036
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005037 HValue* index_base = NULL;
5038 HConstant* constant = NULL;
5039 bool is_sub = false;
5040
5041 if (check->index()->IsAdd()) {
5042 HAdd* index = HAdd::cast(check->index());
5043 if (index->left()->IsConstant()) {
5044 constant = HConstant::cast(index->left());
5045 index_base = index->right();
5046 } else if (index->right()->IsConstant()) {
5047 constant = HConstant::cast(index->right());
5048 index_base = index->left();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005049 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005050 } else if (check->index()->IsSub()) {
5051 HSub* index = HSub::cast(check->index());
5052 is_sub = true;
5053 if (index->left()->IsConstant()) {
5054 constant = HConstant::cast(index->left());
5055 index_base = index->right();
5056 } else if (index->right()->IsConstant()) {
5057 constant = HConstant::cast(index->right());
5058 index_base = index->left();
5059 }
5060 }
5061
5062 if (constant != NULL && constant->HasInteger32Value()) {
5063 *offset = is_sub ? - constant->Integer32Value()
5064 : constant->Integer32Value();
5065 } else {
5066 *offset = 0;
5067 index_base = check->index();
5068 }
5069
5070 return new(zone) BoundsCheckKey(index_base, check->length());
5071 }
5072
5073 private:
5074 BoundsCheckKey(HValue* index_base, HValue* length)
5075 : index_base_(index_base),
5076 length_(length) { }
5077
5078 HValue* index_base_;
5079 HValue* length_;
5080};
5081
5082
5083// Data about each HBoundsCheck that can be eliminated or moved.
5084// It is the "value" in the dictionary indexed by "base-index, length"
5085// (the key is BoundsCheckKey).
5086// We scan the code with a dominator tree traversal.
5087// Traversing the dominator tree we keep a stack (implemented as a singly
5088// linked list) of "data" for each basic block that contains a relevant check
5089// with the same key (the dictionary holds the head of the list).
5090// We also keep all the "data" created for a given basic block in a list, and
5091// use it to "clean up" the dictionary when backtracking in the dominator tree
5092// traversal.
5093// Doing this each dictionary entry always directly points to the check that
5094// is dominating the code being examined now.
5095// We also track the current "offset" of the index expression and use it to
5096// decide if any check is already "covered" (so it can be removed) or not.
5097class BoundsCheckBbData: public ZoneObject {
5098 public:
5099 BoundsCheckKey* Key() const { return key_; }
5100 int32_t LowerOffset() const { return lower_offset_; }
5101 int32_t UpperOffset() const { return upper_offset_; }
5102 HBasicBlock* BasicBlock() const { return basic_block_; }
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005103 HBoundsCheck* LowerCheck() const { return lower_check_; }
5104 HBoundsCheck* UpperCheck() const { return upper_check_; }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005105 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
5106 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
5107
5108 bool OffsetIsCovered(int32_t offset) const {
5109 return offset >= LowerOffset() && offset <= UpperOffset();
5110 }
5111
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005112 bool HasSingleCheck() { return lower_check_ == upper_check_; }
5113
5114 // The goal of this method is to modify either upper_offset_ or
5115 // lower_offset_ so that also new_offset is covered (the covered
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005116 // range grows).
5117 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005118 // The precondition is that new_check follows UpperCheck() and
5119 // LowerCheck() in the same basic block, and that new_offset is not
5120 // covered (otherwise we could simply remove new_check).
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005121 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005122 // If HasSingleCheck() is true then new_check is added as "second check"
5123 // (either upper or lower; note that HasSingleCheck() becomes false).
5124 // Otherwise one of the current checks is modified so that it also covers
5125 // new_offset, and new_check is removed.
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005126 //
5127 // If the check cannot be modified because the context is unknown it
5128 // returns false, otherwise it returns true.
5129 bool CoverCheck(HBoundsCheck* new_check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005130 int32_t new_offset) {
5131 ASSERT(new_check->index()->representation().IsInteger32());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005132 bool keep_new_check = false;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005133
5134 if (new_offset > upper_offset_) {
5135 upper_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005136 if (HasSingleCheck()) {
5137 keep_new_check = true;
5138 upper_check_ = new_check;
5139 } else {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005140 bool result = BuildOffsetAdd(upper_check_,
5141 &added_upper_index_,
5142 &added_upper_offset_,
5143 Key()->IndexBase(),
5144 new_check->index()->representation(),
5145 new_offset);
5146 if (!result) return false;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005147 upper_check_->ReplaceAllUsesWith(upper_check_->index());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005148 upper_check_->SetOperandAt(0, added_upper_index_);
5149 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005150 } else if (new_offset < lower_offset_) {
5151 lower_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005152 if (HasSingleCheck()) {
5153 keep_new_check = true;
5154 lower_check_ = new_check;
5155 } else {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005156 bool result = BuildOffsetAdd(lower_check_,
5157 &added_lower_index_,
5158 &added_lower_offset_,
5159 Key()->IndexBase(),
5160 new_check->index()->representation(),
5161 new_offset);
5162 if (!result) return false;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005163 lower_check_->ReplaceAllUsesWith(lower_check_->index());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005164 lower_check_->SetOperandAt(0, added_lower_index_);
5165 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005166 } else {
5167 ASSERT(false);
5168 }
5169
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005170 if (!keep_new_check) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005171 new_check->DeleteAndReplaceWith(new_check->ActualValue());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005172 }
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005173
5174 return true;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005175 }
5176
5177 void RemoveZeroOperations() {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005178 RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
5179 RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005180 }
5181
5182 BoundsCheckBbData(BoundsCheckKey* key,
5183 int32_t lower_offset,
5184 int32_t upper_offset,
5185 HBasicBlock* bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005186 HBoundsCheck* lower_check,
5187 HBoundsCheck* upper_check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005188 BoundsCheckBbData* next_in_bb,
5189 BoundsCheckBbData* father_in_dt)
5190 : key_(key),
5191 lower_offset_(lower_offset),
5192 upper_offset_(upper_offset),
5193 basic_block_(bb),
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005194 lower_check_(lower_check),
5195 upper_check_(upper_check),
5196 added_lower_index_(NULL),
5197 added_lower_offset_(NULL),
5198 added_upper_index_(NULL),
5199 added_upper_offset_(NULL),
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005200 next_in_bb_(next_in_bb),
5201 father_in_dt_(father_in_dt) { }
5202
5203 private:
5204 BoundsCheckKey* key_;
5205 int32_t lower_offset_;
5206 int32_t upper_offset_;
5207 HBasicBlock* basic_block_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005208 HBoundsCheck* lower_check_;
5209 HBoundsCheck* upper_check_;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005210 HInstruction* added_lower_index_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005211 HConstant* added_lower_offset_;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005212 HInstruction* added_upper_index_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005213 HConstant* added_upper_offset_;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005214 BoundsCheckBbData* next_in_bb_;
5215 BoundsCheckBbData* father_in_dt_;
5216
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005217 // Given an existing add instruction and a bounds check it tries to
5218 // find the current context (either of the add or of the check index).
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005219 HValue* IndexContext(HInstruction* add, HBoundsCheck* check) {
5220 if (add != NULL && add->IsAdd()) {
5221 return HAdd::cast(add)->context();
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005222 }
5223 if (check->index()->IsBinaryOperation()) {
5224 return HBinaryOperation::cast(check->index())->context();
5225 }
5226 return NULL;
5227 }
5228
5229 // This function returns false if it cannot build the add because the
5230 // current context cannot be determined.
5231 bool BuildOffsetAdd(HBoundsCheck* check,
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005232 HInstruction** add,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005233 HConstant** constant,
5234 HValue* original_value,
5235 Representation representation,
5236 int32_t new_offset) {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005237 HValue* index_context = IndexContext(*add, check);
5238 if (index_context == NULL) return false;
5239
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005240 HConstant* new_constant = new(BasicBlock()->zone())
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00005241 HConstant(new_offset, Representation::Integer32());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005242 if (*add == NULL) {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005243 new_constant->InsertBefore(check);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005244 (*add) = HAdd::New(
5245 BasicBlock()->zone(), index_context, original_value, new_constant);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005246 (*add)->AssumeRepresentation(representation);
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005247 (*add)->InsertBefore(check);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005248 } else {
5249 new_constant->InsertBefore(*add);
5250 (*constant)->DeleteAndReplaceWith(new_constant);
5251 }
5252 *constant = new_constant;
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005253 return true;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005254 }
5255
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005256 void RemoveZeroAdd(HInstruction** add, HConstant** constant) {
5257 if (*add != NULL && (*add)->IsAdd() && (*constant)->Integer32Value() == 0) {
5258 (*add)->DeleteAndReplaceWith(HAdd::cast(*add)->left());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005259 (*constant)->DeleteAndReplaceWith(NULL);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005260 }
5261 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005262};
5263
5264
5265static bool BoundsCheckKeyMatch(void* key1, void* key2) {
5266 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
5267 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
5268 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
5269}
5270
5271
5272class BoundsCheckTable : private ZoneHashMap {
5273 public:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005274 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005275 return reinterpret_cast<BoundsCheckBbData**>(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005276 &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005277 }
5278
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005279 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone) {
5280 Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005281 }
5282
5283 void Delete(BoundsCheckKey* key) {
5284 Remove(key, key->Hash());
5285 }
5286
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005287 explicit BoundsCheckTable(Zone* zone)
5288 : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
5289 ZoneAllocationPolicy(zone)) { }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005290};
5291
5292
5293// Eliminates checks in bb and recursively in the dominated blocks.
5294// Also replace the results of check instructions with the original value, if
5295// the result is used. This is safe now, since we don't do code motion after
5296// this point. It enables better register allocation since the value produced
5297// by check instructions is really a copy of the original value.
5298void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
5299 BoundsCheckTable* table) {
5300 BoundsCheckBbData* bb_data_list = NULL;
5301
5302 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
5303 if (!i->IsBoundsCheck()) continue;
5304
5305 HBoundsCheck* check = HBoundsCheck::cast(i);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005306 int32_t offset;
5307 BoundsCheckKey* key =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005308 BoundsCheckKey::Create(zone(), check, &offset);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00005309 if (key == NULL) continue;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005310 BoundsCheckBbData** data_p = table->LookupOrInsert(key, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005311 BoundsCheckBbData* data = *data_p;
5312 if (data == NULL) {
5313 bb_data_list = new(zone()) BoundsCheckBbData(key,
5314 offset,
5315 offset,
5316 bb,
5317 check,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005318 check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005319 bb_data_list,
5320 NULL);
5321 *data_p = bb_data_list;
5322 } else if (data->OffsetIsCovered(offset)) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005323 check->DeleteAndReplaceWith(check->ActualValue());
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00005324 } else if (data->BasicBlock() != bb ||
5325 !data->CoverCheck(check, offset)) {
5326 // If the check is in the current BB we try to modify it by calling
5327 // "CoverCheck", but if also that fails we record the current offsets
5328 // in a new data instance because from now on they are covered.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005329 int32_t new_lower_offset = offset < data->LowerOffset()
5330 ? offset
5331 : data->LowerOffset();
5332 int32_t new_upper_offset = offset > data->UpperOffset()
5333 ? offset
5334 : data->UpperOffset();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005335 bb_data_list = new(zone()) BoundsCheckBbData(key,
5336 new_lower_offset,
5337 new_upper_offset,
5338 bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00005339 data->LowerCheck(),
5340 data->UpperCheck(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005341 bb_data_list,
5342 data);
5343 table->Insert(key, bb_data_list, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005344 }
5345 }
5346
5347 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
5348 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table);
5349 }
5350
5351 for (BoundsCheckBbData* data = bb_data_list;
5352 data != NULL;
5353 data = data->NextInBasicBlock()) {
5354 data->RemoveZeroOperations();
5355 if (data->FatherInDominatorTree()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005356 table->Insert(data->Key(), data->FatherInDominatorTree(), zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005357 } else {
5358 table->Delete(data->Key());
5359 }
5360 }
5361}
5362
5363
5364void HGraph::EliminateRedundantBoundsChecks() {
5365 HPhase phase("H_Eliminate bounds checks", this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005366 BoundsCheckTable checks_table(zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00005367 EliminateRedundantBoundsChecks(entry_block(), &checks_table);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005368}
5369
5370
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00005371static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005372 HValue* index = array_operation->GetKey()->ActualValue();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00005373 if (!index->representation().IsInteger32()) return;
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00005374
5375 HConstant* constant;
5376 HValue* subexpression;
5377 int32_t sign;
5378 if (index->IsAdd()) {
5379 sign = 1;
5380 HAdd* add = HAdd::cast(index);
5381 if (add->left()->IsConstant()) {
5382 subexpression = add->right();
5383 constant = HConstant::cast(add->left());
5384 } else if (add->right()->IsConstant()) {
5385 subexpression = add->left();
5386 constant = HConstant::cast(add->right());
5387 } else {
5388 return;
5389 }
5390 } else if (index->IsSub()) {
5391 sign = -1;
5392 HSub* sub = HSub::cast(index);
5393 if (sub->left()->IsConstant()) {
5394 subexpression = sub->right();
5395 constant = HConstant::cast(sub->left());
5396 } else if (sub->right()->IsConstant()) {
5397 subexpression = sub->left();
5398 constant = HConstant::cast(sub->right());
5399 } return;
5400 } else {
5401 return;
5402 }
5403
5404 if (!constant->HasInteger32Value()) return;
5405 int32_t value = constant->Integer32Value() * sign;
5406 // We limit offset values to 30 bits because we want to avoid the risk of
5407 // overflows when the offset is added to the object header size.
5408 if (value >= 1 << 30 || value < 0) return;
5409 array_operation->SetKey(subexpression);
5410 if (index->HasNoUses()) {
5411 index->DeleteAndReplaceWith(NULL);
5412 }
5413 ASSERT(value >= 0);
5414 array_operation->SetIndexOffset(static_cast<uint32_t>(value));
5415 array_operation->SetDehoisted(true);
5416}
5417
5418
5419void HGraph::DehoistSimpleArrayIndexComputations() {
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00005420 HPhase phase("H_Dehoist index computations", this);
5421 for (int i = 0; i < blocks()->length(); ++i) {
5422 for (HInstruction* instr = blocks()->at(i)->first();
5423 instr != NULL;
5424 instr = instr->next()) {
5425 ArrayInstructionInterface* array_instruction = NULL;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005426 if (instr->IsLoadKeyed()) {
5427 HLoadKeyed* op = HLoadKeyed::cast(instr);
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00005428 array_instruction = static_cast<ArrayInstructionInterface*>(op);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005429 } else if (instr->IsStoreKeyed()) {
5430 HStoreKeyed* op = HStoreKeyed::cast(instr);
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00005431 array_instruction = static_cast<ArrayInstructionInterface*>(op);
5432 } else {
5433 continue;
5434 }
5435 DehoistArrayIndex(array_instruction);
5436 }
5437 }
5438}
5439
5440
jkummerow@chromium.orgc1956672012-10-11 15:57:38 +00005441void HGraph::DeadCodeElimination() {
5442 HPhase phase("H_Dead code elimination", this);
5443 ZoneList<HInstruction*> worklist(blocks_.length(), zone());
5444 for (int i = 0; i < blocks()->length(); ++i) {
5445 for (HInstruction* instr = blocks()->at(i)->first();
5446 instr != NULL;
5447 instr = instr->next()) {
5448 if (instr->IsDead()) worklist.Add(instr, zone());
5449 }
5450 }
5451
5452 while (!worklist.is_empty()) {
5453 HInstruction* instr = worklist.RemoveLast();
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005454 // This happens when an instruction is used multiple times as operand. That
5455 // in turn could happen through GVN.
5456 if (!instr->IsLinked()) continue;
jkummerow@chromium.orgc1956672012-10-11 15:57:38 +00005457 if (FLAG_trace_dead_code_elimination) {
5458 HeapStringAllocator allocator;
5459 StringStream stream(&allocator);
5460 instr->PrintNameTo(&stream);
5461 stream.Add(" = ");
5462 instr->PrintTo(&stream);
5463 PrintF("[removing dead instruction %s]\n", *stream.ToCString());
5464 }
5465 instr->DeleteAndReplaceWith(NULL);
5466 for (int i = 0; i < instr->OperandCount(); ++i) {
5467 HValue* operand = instr->OperandAt(i);
5468 if (operand->IsDead()) worklist.Add(HInstruction::cast(operand), zone());
5469 }
5470 }
5471}
5472
5473
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005474void HGraph::RestoreActualValues() {
5475 HPhase phase("H_Restore actual values", this);
5476
5477 for (int block_index = 0; block_index < blocks()->length(); block_index++) {
5478 HBasicBlock* block = blocks()->at(block_index);
5479
5480#ifdef DEBUG
5481 for (int i = 0; i < block->phis()->length(); i++) {
5482 HPhi* phi = block->phis()->at(i);
5483 ASSERT(phi->ActualValue() == phi);
5484 }
5485#endif
5486
5487 for (HInstruction* instruction = block->first();
5488 instruction != NULL;
5489 instruction = instruction->next()) {
5490 if (instruction->ActualValue() != instruction) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005491 ASSERT(instruction->IsInformativeDefinition());
5492 if (instruction->IsPurelyInformativeDefinition()) {
5493 instruction->DeleteAndReplaceWith(instruction->RedefinedOperand());
5494 } else {
5495 instruction->ReplaceAllUsesWith(instruction->ActualValue());
5496 }
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005497 }
5498 }
5499 }
5500}
5501
5502
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005503void HOptimizedGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005504 ASSERT(current_block() != NULL);
5505 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005506}
5507
5508
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005509void HOptimizedGraphBuilder::PushAndAdd(HInstruction* instr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005510 Push(instr);
5511 AddInstruction(instr);
5512}
5513
5514
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005515void HOptimizedGraphBuilder::AddSoftDeoptimize() {
5516 if (FLAG_always_opt) return;
5517 if (current_block()->IsDeoptimizing()) return;
5518 AddInstruction(new(zone()) HSoftDeoptimize());
5519 current_block()->MarkAsDeoptimizing();
5520 graph()->set_has_soft_deoptimize(true);
5521}
5522
5523
ulan@chromium.org967e2702012-02-28 09:49:15 +00005524template <class Instruction>
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005525HInstruction* HOptimizedGraphBuilder::PreProcessCall(Instruction* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005526 int count = call->argument_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005527 ZoneList<HValue*> arguments(count, zone());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005528 for (int i = 0; i < count; ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005529 arguments.Add(Pop(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005530 }
5531
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005532 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005533 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005534 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005535 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005536}
5537
5538
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005539void HOptimizedGraphBuilder::SetUpScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005540 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005541 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005542 AddInstruction(undefined_constant);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005543 graph()->set_undefined_constant(undefined_constant);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005544
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005545 HArgumentsObject* object = new(zone()) HArgumentsObject;
5546 AddInstruction(object);
5547 graph()->SetArgumentsObject(object);
5548
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005549 // Set the initial values of parameters including "this". "This" has
5550 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005551 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
5552
5553 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005554 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005555 environment()->Bind(i, parameter);
5556 }
5557
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005558 // First special is HContext.
5559 HInstruction* context = AddInstruction(new(zone()) HContext);
5560 environment()->BindContext(context);
5561
5562 // Initialize specials and locals to undefined.
5563 for (int i = environment()->parameter_count() + 1;
5564 i < environment()->length();
5565 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005566 environment()->Bind(i, undefined_constant);
5567 }
5568
5569 // Handle the arguments and arguments shadow variables specially (they do
5570 // not have declarations).
5571 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00005572 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005573 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005574 }
yangguo@chromium.org154ff992012-03-13 08:09:54 +00005575
yangguo@chromium.org154ff992012-03-13 08:09:54 +00005576 environment()->Bind(scope->arguments(),
5577 graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005578 }
5579}
5580
5581
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005582void HOptimizedGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005583 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005584 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005585 }
5586}
5587
5588
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005589void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005590 ASSERT(!HasStackOverflow());
5591 ASSERT(current_block() != NULL);
5592 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00005593 if (stmt->scope() != NULL) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005594 return Bailout("ScopedBlock");
5595 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005596 BreakAndContinueInfo break_info(stmt);
5597 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005598 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005599 }
5600 HBasicBlock* break_block = break_info.break_block();
5601 if (break_block != NULL) {
5602 if (current_block() != NULL) current_block()->Goto(break_block);
5603 break_block->SetJoinId(stmt->ExitId());
5604 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005605 }
5606}
5607
5608
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005609void HOptimizedGraphBuilder::VisitExpressionStatement(
5610 ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005611 ASSERT(!HasStackOverflow());
5612 ASSERT(current_block() != NULL);
5613 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005614 VisitForEffect(stmt->expression());
5615}
5616
5617
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005618void HOptimizedGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005619 ASSERT(!HasStackOverflow());
5620 ASSERT(current_block() != NULL);
5621 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005622}
5623
5624
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005625void HOptimizedGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005626 ASSERT(!HasStackOverflow());
5627 ASSERT(current_block() != NULL);
5628 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005629 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005630 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005631 Visit(stmt->then_statement());
5632 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005633 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005634 Visit(stmt->else_statement());
5635 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005636 HBasicBlock* cond_true = graph()->CreateBasicBlock();
5637 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005638 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005639
danno@chromium.org160a7b02011-04-18 15:51:38 +00005640 if (cond_true->HasPredecessor()) {
5641 cond_true->SetJoinId(stmt->ThenId());
5642 set_current_block(cond_true);
5643 CHECK_BAILOUT(Visit(stmt->then_statement()));
5644 cond_true = current_block();
5645 } else {
5646 cond_true = NULL;
5647 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005648
danno@chromium.org160a7b02011-04-18 15:51:38 +00005649 if (cond_false->HasPredecessor()) {
5650 cond_false->SetJoinId(stmt->ElseId());
5651 set_current_block(cond_false);
5652 CHECK_BAILOUT(Visit(stmt->else_statement()));
5653 cond_false = current_block();
5654 } else {
5655 cond_false = NULL;
5656 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005657
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005658 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005659 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005660 }
5661}
5662
5663
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005664HBasicBlock* HOptimizedGraphBuilder::BreakAndContinueScope::Get(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005665 BreakableStatement* stmt,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005666 BreakType type,
5667 int* drop_extra) {
5668 *drop_extra = 0;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005669 BreakAndContinueScope* current = this;
5670 while (current != NULL && current->info()->target() != stmt) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005671 *drop_extra += current->info()->drop_extra();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005672 current = current->next();
5673 }
5674 ASSERT(current != NULL); // Always found (unless stack is malformed).
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005675
5676 if (type == BREAK) {
5677 *drop_extra += current->info()->drop_extra();
5678 }
5679
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005680 HBasicBlock* block = NULL;
5681 switch (type) {
5682 case BREAK:
5683 block = current->info()->break_block();
5684 if (block == NULL) {
5685 block = current->owner()->graph()->CreateBasicBlock();
5686 current->info()->set_break_block(block);
5687 }
5688 break;
5689
5690 case CONTINUE:
5691 block = current->info()->continue_block();
5692 if (block == NULL) {
5693 block = current->owner()->graph()->CreateBasicBlock();
5694 current->info()->set_continue_block(block);
5695 }
5696 break;
5697 }
5698
5699 return block;
5700}
5701
5702
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005703void HOptimizedGraphBuilder::VisitContinueStatement(
5704 ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005705 ASSERT(!HasStackOverflow());
5706 ASSERT(current_block() != NULL);
5707 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005708 int drop_extra = 0;
5709 HBasicBlock* continue_block = break_scope()->Get(stmt->target(),
5710 CONTINUE,
5711 &drop_extra);
5712 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005713 current_block()->Goto(continue_block);
5714 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005715}
5716
5717
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005718void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005719 ASSERT(!HasStackOverflow());
5720 ASSERT(current_block() != NULL);
5721 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005722 int drop_extra = 0;
5723 HBasicBlock* break_block = break_scope()->Get(stmt->target(),
5724 BREAK,
5725 &drop_extra);
5726 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005727 current_block()->Goto(break_block);
5728 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005729}
5730
5731
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005732void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005733 ASSERT(!HasStackOverflow());
5734 ASSERT(current_block() != NULL);
5735 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005736 FunctionState* state = function_state();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005737 AstContext* context = call_context();
5738 if (context == NULL) {
5739 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005740 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005741 HValue* result = environment()->Pop();
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00005742 AddReturn(result);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005743 } else if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
5744 // Return from an inlined construct call. In a test context the return value
5745 // will always evaluate to true, in a value context the return value needs
5746 // to be a JSObject.
ulan@chromium.org967e2702012-02-28 09:49:15 +00005747 if (context->IsTest()) {
5748 TestContext* test = TestContext::cast(context);
5749 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005750 current_block()->Goto(test->if_true(), state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00005751 } else if (context->IsEffect()) {
5752 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005753 current_block()->Goto(function_return(), state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00005754 } else {
5755 ASSERT(context->IsValue());
5756 CHECK_ALIVE(VisitForValue(stmt->expression()));
5757 HValue* return_value = Pop();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005758 HValue* receiver = environment()->arguments_environment()->Lookup(0);
ulan@chromium.org967e2702012-02-28 09:49:15 +00005759 HHasInstanceTypeAndBranch* typecheck =
5760 new(zone()) HHasInstanceTypeAndBranch(return_value,
5761 FIRST_SPEC_OBJECT_TYPE,
5762 LAST_SPEC_OBJECT_TYPE);
5763 HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
5764 HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
5765 typecheck->SetSuccessorAt(0, if_spec_object);
5766 typecheck->SetSuccessorAt(1, not_spec_object);
5767 current_block()->Finish(typecheck);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005768 if_spec_object->AddLeaveInlined(return_value, state);
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00005769 not_spec_object->AddLeaveInlined(receiver, state);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005770 }
5771 } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
5772 // Return from an inlined setter call. The returned value is never used, the
5773 // value of an assignment is always the value of the RHS of the assignment.
5774 CHECK_ALIVE(VisitForEffect(stmt->expression()));
5775 if (context->IsTest()) {
5776 HValue* rhs = environment()->arguments_environment()->Lookup(1);
5777 context->ReturnValue(rhs);
5778 } else if (context->IsEffect()) {
5779 current_block()->Goto(function_return(), state);
5780 } else {
5781 ASSERT(context->IsValue());
5782 HValue* rhs = environment()->arguments_environment()->Lookup(1);
5783 current_block()->AddLeaveInlined(rhs, state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00005784 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005785 } else {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005786 // Return from a normal inlined function. Visit the subexpression in the
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005787 // expression context of the call.
5788 if (context->IsTest()) {
5789 TestContext* test = TestContext::cast(context);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005790 VisitForControl(stmt->expression(), test->if_true(), test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005791 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005792 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005793 current_block()->Goto(function_return(), state);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005794 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005795 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005796 CHECK_ALIVE(VisitForValue(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005797 current_block()->AddLeaveInlined(Pop(), state);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005798 }
5799 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00005800 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005801}
5802
5803
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005804void HOptimizedGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005805 ASSERT(!HasStackOverflow());
5806 ASSERT(current_block() != NULL);
5807 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005808 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005809}
5810
5811
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005812void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005813 ASSERT(!HasStackOverflow());
5814 ASSERT(current_block() != NULL);
5815 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005816 // We only optimize switch statements with smi-literal smi comparisons,
5817 // with a bounded number of clauses.
5818 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005819 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005820 int clause_count = clauses->length();
5821 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005822 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005823 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005824
erikcorry0ad885c2011-11-21 13:51:57 +00005825 HValue* context = environment()->LookupContext();
5826
danno@chromium.org160a7b02011-04-18 15:51:38 +00005827 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005828 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005829 HValue* tag_value = Pop();
5830 HBasicBlock* first_test_block = current_block();
5831
erikcorry0ad885c2011-11-21 13:51:57 +00005832 SwitchType switch_type = UNKNOWN_SWITCH;
5833
5834 // 1. Extract clause type
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005835 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005836 CaseClause* clause = clauses->at(i);
5837 if (clause->is_default()) continue;
erikcorry0ad885c2011-11-21 13:51:57 +00005838
5839 if (switch_type == UNKNOWN_SWITCH) {
5840 if (clause->label()->IsSmiLiteral()) {
5841 switch_type = SMI_SWITCH;
5842 } else if (clause->label()->IsStringLiteral()) {
5843 switch_type = STRING_SWITCH;
5844 } else {
5845 return Bailout("SwitchStatement: non-literal switch label");
5846 }
5847 } else if ((switch_type == STRING_SWITCH &&
5848 !clause->label()->IsStringLiteral()) ||
5849 (switch_type == SMI_SWITCH &&
5850 !clause->label()->IsSmiLiteral())) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005851 return Bailout("SwitchStatement: mixed label types are not supported");
erikcorry0ad885c2011-11-21 13:51:57 +00005852 }
5853 }
5854
5855 HUnaryControlInstruction* string_check = NULL;
5856 HBasicBlock* not_string_block = NULL;
5857
5858 // Test switch's tag value if all clauses are string literals
5859 if (switch_type == STRING_SWITCH) {
5860 string_check = new(zone()) HIsStringAndBranch(tag_value);
5861 first_test_block = graph()->CreateBasicBlock();
5862 not_string_block = graph()->CreateBasicBlock();
5863
5864 string_check->SetSuccessorAt(0, first_test_block);
5865 string_check->SetSuccessorAt(1, not_string_block);
5866 current_block()->Finish(string_check);
5867
5868 set_current_block(first_test_block);
5869 }
5870
5871 // 2. Build all the tests, with dangling true branches
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005872 BailoutId default_id = BailoutId::None();
erikcorry0ad885c2011-11-21 13:51:57 +00005873 for (int i = 0; i < clause_count; ++i) {
5874 CaseClause* clause = clauses->at(i);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005875 if (clause->is_default()) {
5876 default_id = clause->EntryId();
5877 continue;
5878 }
erikcorry0ad885c2011-11-21 13:51:57 +00005879 if (switch_type == SMI_SWITCH) {
5880 clause->RecordTypeFeedback(oracle());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005881 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005882
erikcorry0ad885c2011-11-21 13:51:57 +00005883 // Generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005884 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005885 HValue* label_value = Pop();
erikcorry0ad885c2011-11-21 13:51:57 +00005886
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005887 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
erikcorry0ad885c2011-11-21 13:51:57 +00005888 HBasicBlock* body_block = graph()->CreateBasicBlock();
5889
5890 HControlInstruction* compare;
5891
5892 if (switch_type == SMI_SWITCH) {
5893 if (!clause->IsSmiCompare()) {
5894 // Finish with deoptimize and add uses of enviroment values to
5895 // account for invisible uses.
5896 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
5897 set_current_block(NULL);
5898 break;
5899 }
5900
5901 HCompareIDAndBranch* compare_ =
5902 new(zone()) HCompareIDAndBranch(tag_value,
5903 label_value,
5904 Token::EQ_STRICT);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005905 compare_->set_observed_input_representation(
5906 Representation::Integer32(), Representation::Integer32());
erikcorry0ad885c2011-11-21 13:51:57 +00005907 compare = compare_;
5908 } else {
5909 compare = new(zone()) HStringCompareAndBranch(context, tag_value,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005910 label_value,
5911 Token::EQ_STRICT);
erikcorry0ad885c2011-11-21 13:51:57 +00005912 }
5913
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005914 compare->SetSuccessorAt(0, body_block);
5915 compare->SetSuccessorAt(1, next_test_block);
5916 current_block()->Finish(compare);
erikcorry0ad885c2011-11-21 13:51:57 +00005917
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005918 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005919 }
5920
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005921 // Save the current block to use for the default or to join with the
5922 // exit. This block is NULL if we deoptimized.
5923 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005924
erikcorry0ad885c2011-11-21 13:51:57 +00005925 if (not_string_block != NULL) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005926 BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005927 last_block = CreateJoin(last_block, not_string_block, join_id);
erikcorry0ad885c2011-11-21 13:51:57 +00005928 }
5929
5930 // 3. Loop over the clauses and the linked list of tests in lockstep,
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005931 // translating the clause bodies.
5932 HBasicBlock* curr_test_block = first_test_block;
5933 HBasicBlock* fall_through_block = NULL;
erikcorry0ad885c2011-11-21 13:51:57 +00005934
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005935 BreakAndContinueInfo break_info(stmt);
5936 { BreakAndContinueScope push(&break_info, this);
5937 for (int i = 0; i < clause_count; ++i) {
5938 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005939
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005940 // Identify the block where normal (non-fall-through) control flow
5941 // goes to.
5942 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00005943 if (clause->is_default()) {
5944 if (last_block != NULL) {
5945 normal_block = last_block;
5946 last_block = NULL; // Cleared to indicate we've handled it.
5947 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005948 } else if (!curr_test_block->end()->IsDeoptimize()) {
5949 normal_block = curr_test_block->end()->FirstSuccessor();
5950 curr_test_block = curr_test_block->end()->SecondSuccessor();
5951 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005952
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005953 // Identify a block to emit the body into.
5954 if (normal_block == NULL) {
5955 if (fall_through_block == NULL) {
5956 // (a) Unreachable.
5957 if (clause->is_default()) {
5958 continue; // Might still be reachable clause bodies.
5959 } else {
5960 break;
5961 }
5962 } else {
5963 // (b) Reachable only as fall through.
5964 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005965 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005966 } else if (fall_through_block == NULL) {
5967 // (c) Reachable only normally.
5968 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005969 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005970 // (d) Reachable both ways.
5971 HBasicBlock* join = CreateJoin(fall_through_block,
5972 normal_block,
5973 clause->EntryId());
5974 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005975 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005976
danno@chromium.org160a7b02011-04-18 15:51:38 +00005977 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005978 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005979 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005980 }
5981
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005982 // Create an up-to-3-way join. Use the break block if it exists since
5983 // it's already a join block.
5984 HBasicBlock* break_block = break_info.break_block();
5985 if (break_block == NULL) {
5986 set_current_block(CreateJoin(fall_through_block,
5987 last_block,
5988 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005989 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005990 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
5991 if (last_block != NULL) last_block->Goto(break_block);
5992 break_block->SetJoinId(stmt->ExitId());
5993 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005994 }
5995}
5996
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005997
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005998bool HOptimizedGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005999 return statement->OsrEntryId() == info()->osr_ast_id();
6000}
6001
6002
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006003bool HOptimizedGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006004 if (!HasOsrEntryAt(statement)) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006005
6006 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
6007 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
6008 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006009 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006010 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006011
6012 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
6013 non_osr_entry->Goto(loop_predecessor);
6014
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006015 set_current_block(osr_entry);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00006016 osr_entry->set_osr_entry();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00006017 BailoutId osr_entry_id = statement->OsrEntryId();
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006018 int first_expression_index = environment()->first_expression_index();
6019 int length = environment()->length();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006020 ZoneList<HUnknownOSRValue*>* osr_values =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006021 new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006022
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006023 for (int i = 0; i < first_expression_index; ++i) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006024 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
6025 AddInstruction(osr_value);
6026 environment()->Bind(i, osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006027 osr_values->Add(osr_value, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006028 }
6029
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006030 if (first_expression_index != length) {
6031 environment()->Drop(length - first_expression_index);
6032 for (int i = first_expression_index; i < length; ++i) {
6033 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
6034 AddInstruction(osr_value);
6035 environment()->Push(osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006036 osr_values->Add(osr_value, zone());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006037 }
6038 }
6039
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006040 graph()->set_osr_values(osr_values);
6041
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006042 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006043 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006044 HContext* context = new(zone()) HContext;
6045 AddInstruction(context);
6046 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006047 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006048 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006049 set_current_block(loop_predecessor);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006050 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006051}
6052
6053
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006054void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
6055 HBasicBlock* loop_entry,
6056 BreakAndContinueInfo* break_info) {
ager@chromium.org04921a82011-06-27 13:21:41 +00006057 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006058 AddSimulate(stmt->StackCheckId());
6059 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00006060 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006061 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00006062 AddInstruction(stack_check);
6063 ASSERT(loop_entry->IsLoopHeader());
6064 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006065 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00006066}
6067
6068
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006069void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006070 ASSERT(!HasStackOverflow());
6071 ASSERT(current_block() != NULL);
6072 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006073 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006074 bool osr_entry = PreProcessOsrEntry(stmt);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00006075 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00006076 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006077 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006078 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006079
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006080 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006081 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006082 HBasicBlock* body_exit =
6083 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006084 HBasicBlock* loop_successor = NULL;
6085 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006086 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006087 // The block for a true condition, the actual predecessor block of the
6088 // back edge.
6089 body_exit = graph()->CreateBasicBlock();
6090 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006091 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
6092 if (body_exit->HasPredecessor()) {
6093 body_exit->SetJoinId(stmt->BackEdgeId());
6094 } else {
6095 body_exit = NULL;
6096 }
6097 if (loop_successor->HasPredecessor()) {
6098 loop_successor->SetJoinId(stmt->ExitId());
6099 } else {
6100 loop_successor = NULL;
6101 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006102 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006103 HBasicBlock* loop_exit = CreateLoop(stmt,
6104 loop_entry,
6105 body_exit,
6106 loop_successor,
6107 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006108 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006109}
6110
6111
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006112void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006113 ASSERT(!HasStackOverflow());
6114 ASSERT(current_block() != NULL);
6115 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006116 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006117 bool osr_entry = PreProcessOsrEntry(stmt);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00006118 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00006119 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006120 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006121 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
6122
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006123
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006124 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006125 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006126 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006127 HBasicBlock* body_entry = graph()->CreateBasicBlock();
6128 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006129 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
6130 if (body_entry->HasPredecessor()) {
6131 body_entry->SetJoinId(stmt->BodyId());
6132 set_current_block(body_entry);
6133 }
6134 if (loop_successor->HasPredecessor()) {
6135 loop_successor->SetJoinId(stmt->ExitId());
6136 } else {
6137 loop_successor = NULL;
6138 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006139 }
6140
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006141 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006142 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006143 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006144 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006145 HBasicBlock* body_exit =
6146 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006147 HBasicBlock* loop_exit = CreateLoop(stmt,
6148 loop_entry,
6149 body_exit,
6150 loop_successor,
6151 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006152 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006153}
6154
6155
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006156void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006157 ASSERT(!HasStackOverflow());
6158 ASSERT(current_block() != NULL);
6159 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006160 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006161 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006162 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006163 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006164 bool osr_entry = PreProcessOsrEntry(stmt);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00006165 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00006166 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006167 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006168 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006169
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006170 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006171 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006172 HBasicBlock* body_entry = graph()->CreateBasicBlock();
6173 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006174 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
6175 if (body_entry->HasPredecessor()) {
6176 body_entry->SetJoinId(stmt->BodyId());
6177 set_current_block(body_entry);
6178 }
6179 if (loop_successor->HasPredecessor()) {
6180 loop_successor->SetJoinId(stmt->ExitId());
6181 } else {
6182 loop_successor = NULL;
6183 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006184 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006185
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006186 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006187 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006188 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006189 }
6190 HBasicBlock* body_exit =
6191 JoinContinue(stmt, current_block(), break_info.continue_block());
6192
6193 if (stmt->next() != NULL && body_exit != NULL) {
6194 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006195 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006196 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006197 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006198
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006199 HBasicBlock* loop_exit = CreateLoop(stmt,
6200 loop_entry,
6201 body_exit,
6202 loop_successor,
6203 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00006204 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006205}
6206
6207
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006208void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006209 ASSERT(!HasStackOverflow());
6210 ASSERT(current_block() != NULL);
6211 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006212
yangguo@chromium.orga7d3df92012-02-27 11:46:55 +00006213 if (!FLAG_optimize_for_in) {
6214 return Bailout("ForInStatement optimization is disabled");
6215 }
6216
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006217 if (!oracle()->IsForInFastCase(stmt)) {
6218 return Bailout("ForInStatement is not fast case");
6219 }
6220
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006221 if (!stmt->each()->IsVariableProxy() ||
6222 !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
6223 return Bailout("ForInStatement with non-local each variable");
6224 }
6225
6226 Variable* each_var = stmt->each()->AsVariableProxy()->var();
6227
6228 CHECK_ALIVE(VisitForValue(stmt->enumerable()));
6229 HValue* enumerable = Top(); // Leave enumerable at the top.
6230
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006231 HInstruction* map = AddInstruction(new(zone()) HForInPrepareMap(
ulan@chromium.org967e2702012-02-28 09:49:15 +00006232 environment()->LookupContext(), enumerable));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006233 AddSimulate(stmt->PrepareId());
6234
6235 HInstruction* array = AddInstruction(
6236 new(zone()) HForInCacheArray(
6237 enumerable,
6238 map,
6239 DescriptorArray::kEnumCacheBridgeCacheIndex));
6240
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00006241 HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006242
6243 HInstruction* start_index = AddInstruction(new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006244 Handle<Object>(Smi::FromInt(0), isolate()), Representation::Integer32()));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006245
6246 Push(map);
6247 Push(array);
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00006248 Push(enum_length);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006249 Push(start_index);
6250
6251 HInstruction* index_cache = AddInstruction(
6252 new(zone()) HForInCacheArray(
6253 enumerable,
6254 map,
6255 DescriptorArray::kEnumCacheBridgeIndicesCacheIndex));
6256 HForInCacheArray::cast(array)->set_index_cache(
6257 HForInCacheArray::cast(index_cache));
6258
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006259 bool osr_entry = PreProcessOsrEntry(stmt);
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00006260 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006261 current_block()->Goto(loop_entry);
6262 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00006263 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006264
6265 HValue* index = environment()->ExpressionStackAt(0);
6266 HValue* limit = environment()->ExpressionStackAt(1);
6267
6268 // Check that we still have more keys.
6269 HCompareIDAndBranch* compare_index =
6270 new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006271 compare_index->set_observed_input_representation(
6272 Representation::Integer32(), Representation::Integer32());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006273
6274 HBasicBlock* loop_body = graph()->CreateBasicBlock();
6275 HBasicBlock* loop_successor = graph()->CreateBasicBlock();
6276
6277 compare_index->SetSuccessorAt(0, loop_body);
6278 compare_index->SetSuccessorAt(1, loop_successor);
6279 current_block()->Finish(compare_index);
6280
6281 set_current_block(loop_successor);
6282 Drop(5);
6283
6284 set_current_block(loop_body);
6285
6286 HValue* key = AddInstruction(
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006287 new(zone()) HLoadKeyed(
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006288 environment()->ExpressionStackAt(2), // Enum cache.
yangguo@chromium.org304cc332012-07-24 07:59:48 +00006289 environment()->ExpressionStackAt(0), // Iteration index.
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006290 environment()->ExpressionStackAt(0),
6291 FAST_ELEMENTS));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006292
6293 // Check if the expected map still matches that of the enumerable.
6294 // If not just deoptimize.
6295 AddInstruction(new(zone()) HCheckMapValue(
6296 environment()->ExpressionStackAt(4),
6297 environment()->ExpressionStackAt(3)));
6298
6299 Bind(each_var, key);
6300
6301 BreakAndContinueInfo break_info(stmt, 5);
6302 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
6303
6304 HBasicBlock* body_exit =
6305 JoinContinue(stmt, current_block(), break_info.continue_block());
6306
6307 if (body_exit != NULL) {
6308 set_current_block(body_exit);
6309
6310 HValue* current_index = Pop();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006311 HInstruction* new_index = HAdd::New(zone(),
6312 environment()->LookupContext(),
6313 current_index,
6314 graph()->GetConstant1());
ulan@chromium.org967e2702012-02-28 09:49:15 +00006315 new_index->AssumeRepresentation(Representation::Integer32());
6316 PushAndAdd(new_index);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00006317 body_exit = current_block();
6318 }
6319
6320 HBasicBlock* loop_exit = CreateLoop(stmt,
6321 loop_entry,
6322 body_exit,
6323 loop_successor,
6324 break_info.break_block());
6325
6326 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006327}
6328
6329
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006330void HOptimizedGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006331 ASSERT(!HasStackOverflow());
6332 ASSERT(current_block() != NULL);
6333 ASSERT(current_block()->HasPredecessor());
6334 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006335}
6336
6337
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006338void HOptimizedGraphBuilder::VisitTryFinallyStatement(
6339 TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006340 ASSERT(!HasStackOverflow());
6341 ASSERT(current_block() != NULL);
6342 ASSERT(current_block()->HasPredecessor());
6343 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006344}
6345
6346
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006347void HOptimizedGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006348 ASSERT(!HasStackOverflow());
6349 ASSERT(current_block() != NULL);
6350 ASSERT(current_block()->HasPredecessor());
6351 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006352}
6353
6354
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006355static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
6356 Code* unoptimized_code, FunctionLiteral* expr) {
6357 int start_position = expr->start_position();
6358 RelocIterator it(unoptimized_code);
6359 for (;!it.done(); it.next()) {
6360 RelocInfo* rinfo = it.rinfo();
6361 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
6362 Object* obj = rinfo->target_object();
6363 if (obj->IsSharedFunctionInfo()) {
6364 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
6365 if (shared->start_position() == start_position) {
6366 return Handle<SharedFunctionInfo>(shared);
6367 }
6368 }
6369 }
6370
6371 return Handle<SharedFunctionInfo>();
6372}
6373
6374
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006375void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006376 ASSERT(!HasStackOverflow());
6377 ASSERT(current_block() != NULL);
6378 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006379 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006380 SearchSharedFunctionInfo(info()->shared_info()->code(),
6381 expr);
6382 if (shared_info.is_null()) {
6383 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
6384 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00006385 // We also have a stack overflow if the recursive compilation did.
6386 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006387 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006388 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006389 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006390 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006391}
6392
6393
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006394void HOptimizedGraphBuilder::VisitSharedFunctionInfoLiteral(
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006395 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006396 ASSERT(!HasStackOverflow());
6397 ASSERT(current_block() != NULL);
6398 ASSERT(current_block()->HasPredecessor());
6399 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006400}
6401
6402
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006403void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006404 ASSERT(!HasStackOverflow());
6405 ASSERT(current_block() != NULL);
6406 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006407 HBasicBlock* cond_true = graph()->CreateBasicBlock();
6408 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006409 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006410
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006411 // Visit the true and false subexpressions in the same AST context as the
6412 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006413 if (cond_true->HasPredecessor()) {
6414 cond_true->SetJoinId(expr->ThenId());
6415 set_current_block(cond_true);
6416 CHECK_BAILOUT(Visit(expr->then_expression()));
6417 cond_true = current_block();
6418 } else {
6419 cond_true = NULL;
6420 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006421
danno@chromium.org160a7b02011-04-18 15:51:38 +00006422 if (cond_false->HasPredecessor()) {
6423 cond_false->SetJoinId(expr->ElseId());
6424 set_current_block(cond_false);
6425 CHECK_BAILOUT(Visit(expr->else_expression()));
6426 cond_false = current_block();
6427 } else {
6428 cond_false = NULL;
6429 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006430
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006431 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006432 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006433 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006434 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006435 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006436 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00006437 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006438}
6439
6440
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006441HOptimizedGraphBuilder::GlobalPropertyAccess
6442 HOptimizedGraphBuilder::LookupGlobalProperty(
6443 Variable* var, LookupResult* lookup, bool is_store) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006444 if (var->is_this() || !info()->has_global_object()) {
6445 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006446 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006447 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006448 global->Lookup(*var->name(), lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00006449 if (!lookup->IsNormal() ||
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006450 (is_store && lookup->IsReadOnly()) ||
6451 lookup->holder() != *global) {
6452 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006453 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006454
6455 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006456}
6457
6458
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006459HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006460 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006461 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006462 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006463 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006464 HInstruction* context_instruction = new(zone()) HOuterContext(context);
6465 AddInstruction(context_instruction);
6466 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006467 }
6468 return context;
6469}
6470
6471
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006472void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006473 ASSERT(!HasStackOverflow());
6474 ASSERT(current_block() != NULL);
6475 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006476 Variable* variable = expr->var();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006477 switch (variable->location()) {
6478 case Variable::UNALLOCATED: {
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00006479 if (IsLexicalVariableMode(variable->mode())) {
6480 // TODO(rossberg): should this be an ASSERT?
6481 return Bailout("reference to global lexical variable");
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006482 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006483 // Handle known global constants like 'undefined' specially to avoid a
6484 // load from a global cell for them.
6485 Handle<Object> constant_value =
6486 isolate()->factory()->GlobalConstantFor(variable->name());
6487 if (!constant_value.is_null()) {
6488 HConstant* instr =
6489 new(zone()) HConstant(constant_value, Representation::Tagged());
6490 return ast_context()->ReturnInstruction(instr, expr->id());
6491 }
6492
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006493 LookupResult lookup(isolate());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006494 GlobalPropertyAccess type =
6495 LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006496
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006497 if (type == kUseCell &&
6498 info()->global_object()->IsAccessCheckNeeded()) {
6499 type = kUseGeneric;
6500 }
6501
6502 if (type == kUseCell) {
6503 Handle<GlobalObject> global(info()->global_object());
6504 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006505 HLoadGlobalCell* instr =
6506 new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006507 return ast_context()->ReturnInstruction(instr, expr->id());
6508 } else {
6509 HValue* context = environment()->LookupContext();
6510 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
6511 AddInstruction(global_object);
6512 HLoadGlobalGeneric* instr =
6513 new(zone()) HLoadGlobalGeneric(context,
6514 global_object,
6515 variable->name(),
6516 ast_context()->is_for_typeof());
6517 instr->set_position(expr->position());
6518 return ast_context()->ReturnInstruction(instr, expr->id());
6519 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006520 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006521
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006522 case Variable::PARAMETER:
6523 case Variable::LOCAL: {
6524 HValue* value = environment()->Lookup(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006525 if (value == graph()->GetConstantHole()) {
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00006526 ASSERT(IsDeclaredVariableMode(variable->mode()) &&
6527 variable->mode() != VAR);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006528 return Bailout("reference to uninitialized variable");
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006529 }
6530 return ast_context()->ReturnValue(value);
6531 }
6532
6533 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006534 HValue* context = BuildContextChainWalk(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006535 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006536 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006537 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006538
6539 case Variable::LOOKUP:
6540 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006541 }
6542}
6543
6544
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006545void HOptimizedGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006546 ASSERT(!HasStackOverflow());
6547 ASSERT(current_block() != NULL);
6548 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006549 HConstant* instr =
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006550 new(zone()) HConstant(expr->handle(), Representation::None());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006551 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006552}
6553
6554
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006555void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006556 ASSERT(!HasStackOverflow());
6557 ASSERT(current_block() != NULL);
6558 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00006559 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
6560 Handle<FixedArray> literals(closure->literals());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006561 HValue* context = environment()->LookupContext();
6562
6563 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00006564 literals,
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006565 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006566 expr->flags(),
6567 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006568 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006569}
6570
6571
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006572static void LookupInPrototypes(Handle<Map> map,
6573 Handle<String> name,
6574 LookupResult* lookup) {
6575 while (map->prototype()->IsJSObject()) {
6576 Handle<JSObject> holder(JSObject::cast(map->prototype()));
6577 if (!holder->HasFastProperties()) break;
6578 map = Handle<Map>(holder->map());
6579 map->LookupDescriptor(*holder, *name, lookup);
6580 if (lookup->IsFound()) return;
6581 }
6582 lookup->NotFound();
6583}
6584
6585
6586// Tries to find a JavaScript accessor of the given name in the prototype chain
6587// starting at the given map. Return true iff there is one, including the
6588// corresponding AccessorPair plus its holder (which could be null when the
6589// accessor is found directly in the given map).
6590static bool LookupAccessorPair(Handle<Map> map,
6591 Handle<String> name,
6592 Handle<AccessorPair>* accessors,
6593 Handle<JSObject>* holder) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006594 Isolate* isolate = map->GetIsolate();
6595 LookupResult lookup(isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006596
6597 // Check for a JavaScript accessor directly in the map.
6598 map->LookupDescriptor(NULL, *name, &lookup);
6599 if (lookup.IsPropertyCallbacks()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006600 Handle<Object> callback(lookup.GetValueFromMap(*map), isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006601 if (!callback->IsAccessorPair()) return false;
6602 *accessors = Handle<AccessorPair>::cast(callback);
6603 *holder = Handle<JSObject>();
6604 return true;
6605 }
6606
6607 // Everything else, e.g. a field, can't be an accessor call.
6608 if (lookup.IsFound()) return false;
6609
6610 // Check for a JavaScript accessor somewhere in the proto chain.
6611 LookupInPrototypes(map, name, &lookup);
6612 if (lookup.IsPropertyCallbacks()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006613 Handle<Object> callback(lookup.GetValue(), isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006614 if (!callback->IsAccessorPair()) return false;
6615 *accessors = Handle<AccessorPair>::cast(callback);
6616 *holder = Handle<JSObject>(lookup.holder());
6617 return true;
6618 }
6619
6620 // We haven't found a JavaScript accessor anywhere.
6621 return false;
6622}
6623
6624
6625static bool LookupGetter(Handle<Map> map,
6626 Handle<String> name,
6627 Handle<JSFunction>* getter,
6628 Handle<JSObject>* holder) {
6629 Handle<AccessorPair> accessors;
6630 if (LookupAccessorPair(map, name, &accessors, holder) &&
6631 accessors->getter()->IsJSFunction()) {
6632 *getter = Handle<JSFunction>(JSFunction::cast(accessors->getter()));
6633 return true;
6634 }
6635 return false;
6636}
6637
6638
6639static bool LookupSetter(Handle<Map> map,
6640 Handle<String> name,
6641 Handle<JSFunction>* setter,
6642 Handle<JSObject>* holder) {
6643 Handle<AccessorPair> accessors;
6644 if (LookupAccessorPair(map, name, &accessors, holder) &&
6645 accessors->setter()->IsJSFunction()) {
6646 *setter = Handle<JSFunction>(JSFunction::cast(accessors->setter()));
6647 return true;
6648 }
6649 return false;
6650}
6651
6652
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006653// Determines whether the given array or object literal boilerplate satisfies
6654// all limits to be considered for fast deep-copying and computes the total
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006655// size of all objects that are part of the graph.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006656static bool IsFastLiteral(Handle<JSObject> boilerplate,
6657 int max_depth,
6658 int* max_properties,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006659 int* data_size,
6660 int* pointer_size) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006661 ASSERT(max_depth >= 0 && *max_properties >= 0);
6662 if (max_depth == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006663
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006664 Isolate* isolate = boilerplate->GetIsolate();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006665 Handle<FixedArrayBase> elements(boilerplate->elements());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006666 if (elements->length() > 0 &&
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006667 elements->map() != isolate->heap()->fixed_cow_array_map()) {
danno@chromium.org88aa0582012-03-23 15:11:57 +00006668 if (boilerplate->HasFastDoubleElements()) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006669 *data_size += FixedDoubleArray::SizeFor(elements->length());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006670 } else if (boilerplate->HasFastObjectElements()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00006671 Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
danno@chromium.org88aa0582012-03-23 15:11:57 +00006672 int length = elements->length();
6673 for (int i = 0; i < length; i++) {
6674 if ((*max_properties)-- == 0) return false;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006675 Handle<Object> value(fast_elements->get(i), isolate);
danno@chromium.org88aa0582012-03-23 15:11:57 +00006676 if (value->IsJSObject()) {
6677 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
6678 if (!IsFastLiteral(value_object,
6679 max_depth - 1,
6680 max_properties,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006681 data_size,
6682 pointer_size)) {
danno@chromium.org88aa0582012-03-23 15:11:57 +00006683 return false;
6684 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006685 }
6686 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006687 *pointer_size += FixedArray::SizeFor(length);
danno@chromium.org88aa0582012-03-23 15:11:57 +00006688 } else {
6689 return false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006690 }
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006691 }
6692
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006693 Handle<FixedArray> properties(boilerplate->properties());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006694 if (properties->length() > 0) {
6695 return false;
6696 } else {
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006697 Handle<DescriptorArray> descriptors(
6698 boilerplate->map()->instance_descriptors());
6699 int limit = boilerplate->map()->NumberOfOwnDescriptors();
6700 for (int i = 0; i < limit; i++) {
6701 PropertyDetails details = descriptors->GetDetails(i);
6702 if (details.type() != FIELD) continue;
6703 Representation representation = details.representation();
6704 int index = descriptors->GetFieldIndex(i);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006705 if ((*max_properties)-- == 0) return false;
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006706 Handle<Object> value(boilerplate->InObjectPropertyAt(index), isolate);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006707 if (value->IsJSObject()) {
6708 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006709 if (!IsFastLiteral(value_object,
6710 max_depth - 1,
6711 max_properties,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006712 data_size,
6713 pointer_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006714 return false;
6715 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006716 } else if (representation.IsDouble()) {
6717 *data_size += HeapNumber::kSize;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006718 }
6719 }
6720 }
6721
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006722 *pointer_size += boilerplate->map()->instance_size();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006723 return true;
6724}
6725
6726
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006727void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006728 ASSERT(!HasStackOverflow());
6729 ASSERT(current_block() != NULL);
6730 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006731 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006732 HValue* context = environment()->LookupContext();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006733 HInstruction* literal;
6734
6735 // Check whether to use fast or slow deep-copying for boilerplate.
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006736 int data_size = 0;
6737 int pointer_size = 0;
6738 int max_properties = kMaxFastLiteralProperties;
6739 Handle<Object> original_boilerplate(closure->literals()->get(
6740 expr->literal_index()), isolate());
6741 if (original_boilerplate->IsJSObject() &&
6742 IsFastLiteral(Handle<JSObject>::cast(original_boilerplate),
6743 kMaxFastLiteralDepth,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006744 &max_properties,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006745 &data_size,
6746 &pointer_size)) {
6747 Handle<JSObject> original_boilerplate_object =
6748 Handle<JSObject>::cast(original_boilerplate);
6749 Handle<JSObject> boilerplate_object =
6750 DeepCopy(original_boilerplate_object);
6751
6752 literal = BuildFastLiteral(context,
6753 boilerplate_object,
6754 original_boilerplate_object,
6755 data_size,
6756 pointer_size,
6757 DONT_TRACK_ALLOCATION_SITE);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006758 } else {
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00006759 Handle<FixedArray> closure_literals(closure->literals(), isolate());
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006760 literal = AddInstruction(
6761 new(zone()) HObjectLiteral(context,
6762 expr->constant_properties(),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00006763 closure_literals,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006764 expr->fast_elements(),
6765 expr->literal_index(),
6766 expr->depth(),
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006767 expr->may_store_doubles(),
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006768 expr->has_function()));
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006769 }
6770
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006771 // The object is expected in the bailout environment during computation
6772 // of the property values and is the value of the entire expression.
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006773 Push(literal);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006774
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006775 expr->CalculateEmitStore(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006776
6777 for (int i = 0; i < expr->properties()->length(); i++) {
6778 ObjectLiteral::Property* property = expr->properties()->at(i);
6779 if (property->IsCompileTimeValue()) continue;
6780
6781 Literal* key = property->key();
6782 Expression* value = property->value();
6783
6784 switch (property->kind()) {
6785 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
6786 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
6787 // Fall through.
6788 case ObjectLiteral::Property::COMPUTED:
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006789 if (key->handle()->IsInternalizedString()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006790 if (property->emit_store()) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00006791 property->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006792 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006793 HValue* value = Pop();
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006794 Handle<Map> map = property->GetReceiverType();
6795 Handle<String> name = property->key()->AsPropertyName();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006796 HInstruction* store;
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006797 if (map.is_null()) {
6798 // If we don't know the monomorphic type, do a generic store.
6799 CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value));
6800 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006801#if DEBUG
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006802 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006803 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006804 ASSERT(!LookupSetter(map, name, &setter, &holder));
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006805#endif
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006806 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal,
6807 name,
6808 value,
6809 map));
6810 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006811 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006812 if (store->HasObservableSideEffects()) {
6813 AddSimulate(key->id(), REMOVABLE_SIMULATE);
6814 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006815 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006816 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006817 }
6818 break;
6819 }
6820 // Fall through.
6821 case ObjectLiteral::Property::PROTOTYPE:
6822 case ObjectLiteral::Property::SETTER:
6823 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00006824 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006825 default: UNREACHABLE();
6826 }
6827 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006828
6829 if (expr->has_function()) {
6830 // Return the result of the transformation to fast properties
6831 // instead of the original since this operation changes the map
6832 // of the object. This makes sure that the original object won't
6833 // be used by other optimized code before it is transformed
6834 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006835 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006836 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006837 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006838 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006839 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006840 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006841}
6842
6843
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006844void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006845 ASSERT(!HasStackOverflow());
6846 ASSERT(current_block() != NULL);
6847 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006848 ZoneList<Expression*>* subexprs = expr->values();
6849 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006850 HValue* context = environment()->LookupContext();
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006851 HInstruction* literal;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006852
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00006853 Handle<FixedArray> literals(environment()->closure()->literals(), isolate());
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006854 Handle<Object> raw_boilerplate(literals->get(expr->literal_index()),
6855 isolate());
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006856
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006857 if (raw_boilerplate->IsUndefined()) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006858 raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
6859 isolate(), literals, expr->constant_elements());
6860 if (raw_boilerplate.is_null()) {
6861 return Bailout("array boilerplate creation failed");
6862 }
6863 literals->set(expr->literal_index(), *raw_boilerplate);
6864 if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
6865 isolate()->heap()->fixed_cow_array_map()) {
6866 isolate()->counters()->cow_arrays_created_runtime()->Increment();
6867 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006868 }
6869
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006870 Handle<JSObject> original_boilerplate_object =
6871 Handle<JSObject>::cast(raw_boilerplate);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006872 ElementsKind boilerplate_elements_kind =
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006873 Handle<JSObject>::cast(original_boilerplate_object)->GetElementsKind();
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00006874
6875 // TODO(mvstanton): This heuristic is only a temporary solution. In the
6876 // end, we want to quit creating allocation site info after a certain number
6877 // of GCs for a call site.
6878 AllocationSiteMode mode = AllocationSiteInfo::GetMode(
6879 boilerplate_elements_kind);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006880
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006881 // Check whether to use fast or slow deep-copying for boilerplate.
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006882 int data_size = 0;
6883 int pointer_size = 0;
6884 int max_properties = kMaxFastLiteralProperties;
6885 if (IsFastLiteral(original_boilerplate_object,
6886 kMaxFastLiteralDepth,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006887 &max_properties,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006888 &data_size,
6889 &pointer_size)) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00006890 if (mode == TRACK_ALLOCATION_SITE) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006891 pointer_size += AllocationSiteInfo::kSize;
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00006892 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006893
6894 Handle<JSObject> boilerplate_object = DeepCopy(original_boilerplate_object);
6895 literal = BuildFastLiteral(context,
6896 boilerplate_object,
6897 original_boilerplate_object,
6898 data_size,
6899 pointer_size,
6900 mode);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006901 } else {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006902 literal = AddInstruction(
6903 new(zone()) HArrayLiteral(context,
6904 original_boilerplate_object,
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00006905 literals,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006906 length,
6907 expr->literal_index(),
6908 expr->depth(),
6909 mode));
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00006910 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006911
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006912 // The array is expected in the bailout environment during computation
6913 // of the property values and is the value of the entire expression.
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00006914 Push(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006915
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006916 HInstruction* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006917
6918 for (int i = 0; i < length; i++) {
6919 Expression* subexpr = subexprs->at(i);
6920 // If the subexpression is a literal or a simple materialized literal it
6921 // is already set in the cloned array.
6922 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
6923
danno@chromium.org160a7b02011-04-18 15:51:38 +00006924 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006925 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006926 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006927
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006928 elements = AddLoadElements(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006929
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006930 HValue* key = AddInstruction(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00006931 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i), isolate()),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006932 Representation::Integer32()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006933
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006934 switch (boilerplate_elements_kind) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006935 case FAST_SMI_ELEMENTS:
6936 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00006937 // Smi-only arrays need a smi check.
6938 AddInstruction(new(zone()) HCheckSmi(value));
6939 // Fall through.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006940 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006941 case FAST_HOLEY_ELEMENTS:
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006942 case FAST_DOUBLE_ELEMENTS:
6943 case FAST_HOLEY_DOUBLE_ELEMENTS:
6944 AddInstruction(new(zone()) HStoreKeyed(
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006945 elements,
6946 key,
6947 value,
6948 boilerplate_elements_kind));
6949 break;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006950 default:
6951 UNREACHABLE();
6952 break;
6953 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006954
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006955 AddSimulate(expr->GetIdForElement(i));
6956 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006957 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006958}
6959
6960
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006961// Sets the lookup result and returns true if the load/store can be inlined.
6962static bool ComputeLoadStoreField(Handle<Map> type,
6963 Handle<String> name,
6964 LookupResult* lookup,
6965 bool is_store) {
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006966 if (type->has_named_interceptor()) {
6967 lookup->InterceptorResult(NULL);
6968 return false;
6969 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006970 // If we directly find a field, the access can be inlined.
6971 type->LookupDescriptor(NULL, *name, lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00006972 if (lookup->IsField()) return true;
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006973
6974 // For a load, we are out of luck if there is no such field.
6975 if (!is_store) return false;
6976
6977 // 2nd chance: A store into a non-existent field can still be inlined if we
6978 // have a matching transition and some room left in the object.
6979 type->LookupTransition(NULL, *name, lookup);
6980 return lookup->IsTransitionToField(*type) &&
6981 (type->unused_property_fields() > 0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006982}
6983
6984
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006985static int ComputeLoadStoreFieldIndex(Handle<Map> type,
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006986 LookupResult* lookup) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006987 ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type));
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00006988 if (lookup->IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006989 return lookup->GetLocalFieldIndexFromMap(*type);
6990 } else {
6991 Map* transition = lookup->GetTransitionMapFromMap(*type);
danno@chromium.orgf005df62013-04-30 16:36:45 +00006992 int descriptor = transition->LastAdded();
6993 int index = transition->instance_descriptors()->GetFieldIndex(descriptor);
6994 return index - type->inobject_properties();
6995 }
6996}
6997
6998
6999static Representation ComputeLoadStoreRepresentation(Handle<Map> type,
7000 LookupResult* lookup) {
7001 if (lookup->IsField()) {
7002 return lookup->representation();
7003 } else {
7004 Map* transition = lookup->GetTransitionMapFromMap(*type);
7005 int descriptor = transition->LastAdded();
7006 PropertyDetails details =
7007 transition->instance_descriptors()->GetDetails(descriptor);
7008 return details.representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007009 }
7010}
7011
7012
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007013void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle<Map> map) {
7014 AddInstruction(new(zone()) HCheckNonSmi(object));
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00007015 AddInstruction(HCheckMaps::New(object, map, zone()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007016}
7017
7018
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007019void HOptimizedGraphBuilder::AddCheckMapsWithTransitions(HValue* object,
7020 Handle<Map> map) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007021 AddInstruction(new(zone()) HCheckNonSmi(object));
7022 AddInstruction(HCheckMaps::NewWithTransitions(object, map, zone()));
7023}
7024
7025
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007026HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
7027 HValue* object,
7028 Handle<String> name,
7029 HValue* value,
7030 Handle<Map> map,
7031 LookupResult* lookup) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007032 ASSERT(lookup->IsFound());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007033 // If the property does not exist yet, we have to check that it wasn't made
7034 // readonly or turned into a setter by some meanwhile modifications on the
7035 // prototype chain.
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007036 if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) {
7037 Object* proto = map->prototype();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007038 // First check that the prototype chain isn't affected already.
7039 LookupResult proto_result(isolate());
7040 proto->Lookup(*name, &proto_result);
7041 if (proto_result.IsProperty()) {
7042 // If the inherited property could induce readonly-ness, bail out.
7043 if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
7044 Bailout("improper object on prototype chain for store");
7045 return NULL;
7046 }
7047 // We only need to check up to the preexisting property.
7048 proto = proto_result.holder();
7049 } else {
7050 // Otherwise, find the top prototype.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007051 while (proto->GetPrototype(isolate())->IsJSObject()) {
7052 proto = proto->GetPrototype(isolate());
7053 }
7054 ASSERT(proto->GetPrototype(isolate())->IsNull());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007055 }
7056 ASSERT(proto->IsJSObject());
7057 AddInstruction(new(zone()) HCheckPrototypeMaps(
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007058 Handle<JSObject>(JSObject::cast(map->prototype())),
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007059 Handle<JSObject>(JSObject::cast(proto)),
7060 zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007061 }
7062
danno@chromium.orgf005df62013-04-30 16:36:45 +00007063 int index = ComputeLoadStoreFieldIndex(map, lookup);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007064 bool is_in_object = index < 0;
danno@chromium.orgf005df62013-04-30 16:36:45 +00007065 Representation representation = ComputeLoadStoreRepresentation(map, lookup);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007066 int offset = index * kPointerSize;
7067 if (index < 0) {
7068 // Negative property indices are in-object properties, indexed
7069 // from the end of the fixed part of the object.
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007070 offset += map->instance_size();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007071 } else {
7072 offset += FixedArray::kHeaderSize;
7073 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007074 bool transition_to_field = lookup->IsTransitionToField(*map);
7075 if (FLAG_track_double_fields && representation.IsDouble()) {
7076 if (transition_to_field) {
7077 NoObservableSideEffectsScope no_side_effects(this);
7078 HInstruction* heap_number_size = AddInstruction(new(zone()) HConstant(
7079 HeapNumber::kSize, Representation::Integer32()));
7080 HInstruction* double_box = AddInstruction(new(zone()) HAllocate(
7081 environment()->LookupContext(), heap_number_size,
7082 HType::HeapNumber(), HAllocate::CAN_ALLOCATE_IN_NEW_SPACE));
7083 BuildStoreMap(double_box, isolate()->factory()->heap_number_map());
7084 AddInstruction(new(zone()) HStoreNamedField(
7085 double_box, name, value, true,
7086 Representation::Double(), HeapNumber::kValueOffset));
7087 value = double_box;
7088 representation = Representation::Tagged();
7089 } else {
7090 HInstruction* double_box = AddInstruction(new(zone()) HLoadNamedField(
7091 object, is_in_object, Representation::Tagged(), offset));
7092 double_box->set_type(HType::HeapNumber());
7093 return new(zone()) HStoreNamedField(
7094 double_box, name, value, true,
7095 Representation::Double(), HeapNumber::kValueOffset);
7096 }
7097 }
danno@chromium.orgf005df62013-04-30 16:36:45 +00007098 HStoreNamedField* instr = new(zone()) HStoreNamedField(
7099 object, name, value, is_in_object, representation, offset);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007100 if (transition_to_field) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007101 Handle<Map> transition(lookup->GetTransitionMapFromMap(*map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007102 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007103 // TODO(fschneider): Record the new map type of the object in the IR to
7104 // enable elimination of redundant checks after the transition store.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00007105 instr->SetGVNFlag(kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007106 }
7107 return instr;
7108}
7109
7110
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007111HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
7112 HValue* object,
7113 Handle<String> name,
7114 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007115 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007116 return new(zone()) HStoreNamedGeneric(
7117 context,
7118 object,
7119 name,
7120 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007121 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007122}
7123
7124
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007125HInstruction* HOptimizedGraphBuilder::BuildCallSetter(
7126 HValue* object,
7127 HValue* value,
7128 Handle<Map> map,
7129 Handle<JSFunction> setter,
7130 Handle<JSObject> holder) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007131 AddCheckConstantFunction(holder, object, map);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007132 AddInstruction(new(zone()) HPushArgument(object));
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007133 AddInstruction(new(zone()) HPushArgument(value));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007134 return new(zone()) HCallConstantFunction(setter, 2);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007135}
7136
7137
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007138HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
7139 HValue* object,
7140 Handle<String> name,
7141 HValue* value,
7142 Handle<Map> map) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007143 // Handle a store to a known field.
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00007144 LookupResult lookup(isolate());
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007145 if (ComputeLoadStoreField(map, name, &lookup, true)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007146 AddCheckMapsWithTransitions(object, map);
7147 return BuildStoreNamedField(object, name, value, map, &lookup);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007148 }
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00007149
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007150 // No luck, do a generic store.
7151 return BuildStoreNamedGeneric(object, name, value);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00007152}
7153
7154
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007155bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007156 Property* expr,
7157 HValue* object,
7158 SmallMapList* types,
7159 Handle<String> name) {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007160 if (!name->Equals(isolate()->heap()->length_string())) return false;
7161
7162 for (int i = 0; i < types->length(); i++) {
7163 if (types->at(i)->instance_type() != JS_ARRAY_TYPE) return false;
7164 }
7165
7166 AddInstruction(new(zone()) HCheckNonSmi(object));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007167
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007168 HInstruction* typecheck =
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00007169 AddInstruction(HCheckMaps::New(object, types, zone()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007170 HInstruction* instr =
7171 HLoadNamedField::NewArrayLength(zone(), object, typecheck);
7172 instr->set_position(expr->position());
7173 ast_context()->ReturnInstruction(instr, expr->id());
7174 return true;
7175}
7176
7177
7178void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
7179 HValue* object,
7180 SmallMapList* types,
7181 Handle<String> name) {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007182
7183 if (HandlePolymorphicArrayLengthLoad(expr, object, types, name))
7184 return;
7185
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007186 AddInstruction(new(zone()) HCheckNonSmi(object));
7187
7188 // Use monomorphic load if property lookup results in the same field index
7189 // for all maps. Requires special map check on the set of all handled maps.
7190 HInstruction* instr = NULL;
7191 if (types->length() > 0 && types->length() <= kMaxLoadPolymorphism) {
7192 LookupResult lookup(isolate());
7193 int previous_field_offset = 0;
7194 bool previous_field_is_in_object = false;
7195 Representation representation = Representation::None();
7196 int count;
7197 for (count = 0; count < types->length(); ++count) {
7198 Handle<Map> map = types->at(count);
7199 if (!ComputeLoadStoreField(map, name, &lookup, false)) break;
7200
danno@chromium.orgf005df62013-04-30 16:36:45 +00007201 int index = ComputeLoadStoreFieldIndex(map, &lookup);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007202 Representation new_representation =
7203 ComputeLoadStoreRepresentation(map, &lookup);
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007204 bool is_in_object = index < 0;
7205 int offset = index * kPointerSize;
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007206
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007207 if (index < 0) {
7208 // Negative property indices are in-object properties, indexed
7209 // from the end of the fixed part of the object.
7210 offset += map->instance_size();
7211 } else {
7212 offset += FixedArray::kHeaderSize;
7213 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007214
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007215 if (count == 0) {
7216 previous_field_offset = offset;
7217 previous_field_is_in_object = is_in_object;
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007218 representation = new_representation;
7219 } else if (offset != previous_field_offset ||
7220 is_in_object != previous_field_is_in_object ||
7221 (FLAG_track_fields &&
7222 !representation.IsCompatibleForLoad(new_representation))) {
7223 break;
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007224 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007225
7226 representation = representation.generalize(new_representation);
7227 }
7228
7229 if (count == types->length()) {
7230 AddInstruction(HCheckMaps::New(object, types, zone()));
7231 instr = DoBuildLoadNamedField(
7232 object, previous_field_is_in_object,
7233 representation, previous_field_offset);
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007234 }
7235 }
7236
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007237 if (instr == NULL) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007238 HValue* context = environment()->LookupContext();
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007239 instr = new(zone()) HLoadNamedFieldPolymorphic(
7240 context, object, types, name, zone());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007241 }
7242
7243 instr->set_position(expr->position());
7244 return ast_context()->ReturnInstruction(instr, expr->id());
7245}
7246
7247
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007248void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
7249 Assignment* expr,
7250 HValue* object,
7251 HValue* value,
7252 SmallMapList* types,
7253 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007254 // TODO(ager): We should recognize when the prototype chains for different
7255 // maps are identical. In that case we can avoid repeatedly generating the
7256 // same prototype map checks.
7257 int count = 0;
7258 HBasicBlock* join = NULL;
7259 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007260 Handle<Map> map = types->at(i);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007261 LookupResult lookup(isolate());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007262 if (ComputeLoadStoreField(map, name, &lookup, true)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007263 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007264 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007265 join = graph()->CreateBasicBlock();
7266 }
7267 ++count;
7268 HBasicBlock* if_true = graph()->CreateBasicBlock();
7269 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007270 HCompareMap* compare =
7271 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007272 current_block()->Finish(compare);
7273
7274 set_current_block(if_true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007275 HInstruction* instr;
7276 CHECK_ALIVE(instr =
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007277 BuildStoreNamedField(object, name, value, map, &lookup));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007278 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007279 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007280 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007281 if (!ast_context()->IsEffect()) Push(value);
7282 current_block()->Goto(join);
7283
7284 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007285 }
7286 }
7287
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007288 // Finish up. Unconditionally deoptimize if we've handled all the maps we
7289 // know about and do not want to handle ones we've never seen. Otherwise
7290 // use a generic IC.
7291 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007292 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007293 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007294 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007295 instr->set_position(expr->position());
7296 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007297
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007298 if (join != NULL) {
7299 if (!ast_context()->IsEffect()) Push(value);
7300 current_block()->Goto(join);
7301 } else {
7302 // The HSimulate for the store should not see the stored value in
7303 // effect contexts (it is not materialized at expr->id() in the
7304 // unoptimized code).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007305 if (instr->HasObservableSideEffects()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007306 if (ast_context()->IsEffect()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007307 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007308 } else {
7309 Push(value);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007310 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007311 Drop(1);
7312 }
7313 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007314 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00007315 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00007316 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007317
7318 ASSERT(join != NULL);
7319 join->SetJoinId(expr->id());
7320 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007321 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007322}
7323
7324
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007325void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007326 Property* prop = expr->target()->AsProperty();
7327 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007328 expr->RecordTypeFeedback(oracle(), zone());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007329 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007330
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007331 if (prop->key()->IsPropertyName()) {
7332 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00007333 CHECK_ALIVE(VisitForValue(expr->value()));
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007334 HValue* value = environment()->ExpressionStackAt(0);
7335 HValue* object = environment()->ExpressionStackAt(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007336
7337 Literal* key = prop->key()->AsLiteral();
7338 Handle<String> name = Handle<String>::cast(key->handle());
7339 ASSERT(!name.is_null());
7340
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007341 HInstruction* instr = NULL;
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007342 SmallMapList* types = expr->GetReceiverTypes();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007343 bool monomorphic = expr->IsMonomorphic();
7344 Handle<Map> map;
7345 if (monomorphic) {
7346 map = types->first();
7347 if (map->is_dictionary_map()) monomorphic = false;
7348 }
7349 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007350 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007351 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007352 if (LookupSetter(map, name, &setter, &holder)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007353 AddCheckConstantFunction(holder, object, map);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007354 if (FLAG_inline_accessors && TryInlineSetter(setter, expr, value)) {
7355 return;
7356 }
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007357 Drop(2);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007358 AddInstruction(new(zone()) HPushArgument(object));
7359 AddInstruction(new(zone()) HPushArgument(value));
7360 instr = new(zone()) HCallConstantFunction(setter, 2);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007361 } else {
7362 Drop(2);
7363 CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object,
7364 name,
7365 value,
7366 map));
7367 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007368
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007369 } else if (types != NULL && types->length() > 1) {
7370 Drop(2);
7371 return HandlePolymorphicStoreNamedField(expr, object, value, types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007372 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007373 Drop(2);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007374 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007375 }
7376
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007377 Push(value);
7378 instr->set_position(expr->position());
7379 AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007380 if (instr->HasObservableSideEffects()) {
7381 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
7382 }
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007383 return ast_context()->ReturnValue(Pop());
7384
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007385 } else {
7386 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00007387 CHECK_ALIVE(VisitForValue(prop->key()));
7388 CHECK_ALIVE(VisitForValue(expr->value()));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007389 HValue* value = environment()->ExpressionStackAt(0);
7390 HValue* key = environment()->ExpressionStackAt(1);
7391 HValue* object = environment()->ExpressionStackAt(2);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007392 bool has_side_effects = false;
7393 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
7394 expr->position(),
7395 true, // is_store
7396 &has_side_effects);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007397 Drop(3);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007398 Push(value);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007399 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007400 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007401 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007402}
7403
7404
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007405// Because not every expression has a position and there is not common
7406// superclass of Assignment and CountOperation, we cannot just pass the
7407// owning expression instead of position and ast_id separately.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007408void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
7409 Variable* var,
7410 HValue* value,
7411 int position,
7412 BailoutId ast_id) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007413 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007414 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
7415 if (type == kUseCell) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007416 Handle<GlobalObject> global(info()->global_object());
7417 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007418 HInstruction* instr =
7419 new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007420 instr->set_position(position);
7421 AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007422 if (instr->HasObservableSideEffects()) {
7423 AddSimulate(ast_id, REMOVABLE_SIMULATE);
7424 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007425 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007426 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007427 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
7428 AddInstruction(global_object);
7429 HStoreGlobalGeneric* instr =
7430 new(zone()) HStoreGlobalGeneric(context,
7431 global_object,
7432 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007433 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007434 function_strict_mode_flag());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007435 instr->set_position(position);
7436 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007437 ASSERT(instr->HasObservableSideEffects());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007438 AddSimulate(ast_id, REMOVABLE_SIMULATE);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007439 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007440}
7441
7442
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007443void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007444 Expression* target = expr->target();
7445 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007446 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007447 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007448
7449 // We have a second position recorded in the FullCodeGenerator to have
7450 // type feedback for the binary operation.
7451 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007452
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007453 if (proxy != NULL) {
7454 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007455 if (var->mode() == LET) {
7456 return Bailout("unsupported let compound assignment");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007457 }
7458
danno@chromium.org160a7b02011-04-18 15:51:38 +00007459 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007460
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007461 switch (var->location()) {
7462 case Variable::UNALLOCATED:
7463 HandleGlobalVariableAssignment(var,
7464 Top(),
7465 expr->position(),
7466 expr->AssignmentId());
7467 break;
7468
7469 case Variable::PARAMETER:
7470 case Variable::LOCAL:
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007471 if (var->mode() == CONST) {
7472 return Bailout("unsupported const compound assignment");
7473 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007474 Bind(var, Top());
7475 break;
7476
7477 case Variable::CONTEXT: {
7478 // Bail out if we try to mutate a parameter value in a function
7479 // using the arguments object. We do not (yet) correctly handle the
7480 // arguments property of the function.
7481 if (info()->scope()->arguments() != NULL) {
7482 // Parameters will be allocated to context slots. We have no
7483 // direct way to detect that the variable is a parameter so we do
7484 // a linear search of the parameter variables.
7485 int count = info()->scope()->num_parameters();
7486 for (int i = 0; i < count; ++i) {
7487 if (var == info()->scope()->parameter(i)) {
7488 Bailout(
7489 "assignment to parameter, function uses arguments object");
7490 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007491 }
7492 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007493
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007494 HStoreContextSlot::Mode mode;
7495
7496 switch (var->mode()) {
7497 case LET:
7498 mode = HStoreContextSlot::kCheckDeoptimize;
7499 break;
7500 case CONST:
7501 return ast_context()->ReturnValue(Pop());
7502 case CONST_HARMONY:
7503 // This case is checked statically so no need to
7504 // perform checks here
7505 UNREACHABLE();
7506 default:
7507 mode = HStoreContextSlot::kNoCheck;
7508 }
7509
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007510 HValue* context = BuildContextChainWalk(var);
7511 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007512 new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007513 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007514 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007515 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007516 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007517 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00007518 }
7519
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007520 case Variable::LOOKUP:
7521 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007522 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007523 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007524
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007525 } else if (prop != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007526 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007527
7528 if (prop->key()->IsPropertyName()) {
7529 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00007530 CHECK_ALIVE(VisitForValue(prop->obj()));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007531 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007532
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007533 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007534 Handle<Map> map;
7535 HInstruction* load;
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007536 bool monomorphic = prop->IsMonomorphic();
7537 if (monomorphic) {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00007538 map = prop->GetReceiverTypes()->first();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007539 // We can't generate code for a monomorphic dict mode load so
7540 // just pretend it is not monomorphic.
7541 if (map->is_dictionary_map()) monomorphic = false;
7542 }
7543 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007544 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007545 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007546 if (LookupGetter(map, name, &getter, &holder)) {
7547 load = BuildCallGetter(object, map, getter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007548 } else {
7549 load = BuildLoadNamedMonomorphic(object, name, prop, map);
7550 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007551 } else {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007552 load = BuildLoadNamedGeneric(object, name, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007553 }
7554 PushAndAdd(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007555 if (load->HasObservableSideEffects()) {
7556 AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
7557 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007558
danno@chromium.org160a7b02011-04-18 15:51:38 +00007559 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007560 HValue* right = Pop();
7561 HValue* left = Pop();
7562
7563 HInstruction* instr = BuildBinaryOperation(operation, left, right);
7564 PushAndAdd(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007565 if (instr->HasObservableSideEffects()) {
7566 AddSimulate(operation->id(), REMOVABLE_SIMULATE);
7567 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007568
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007569 HInstruction* store;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007570 if (!monomorphic || map->is_observed()) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007571 // If we don't know the monomorphic type, do a generic store.
7572 CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, instr));
7573 } else {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007574 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007575 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007576 if (LookupSetter(map, name, &setter, &holder)) {
7577 store = BuildCallSetter(object, instr, map, setter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007578 } else {
7579 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object,
7580 name,
7581 instr,
7582 map));
7583 }
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007584 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007585 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007586 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007587 Drop(2);
7588 Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007589 if (store->HasObservableSideEffects()) {
7590 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
7591 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007592 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007593
7594 } else {
7595 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00007596 CHECK_ALIVE(VisitForValue(prop->obj()));
7597 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007598 HValue* obj = environment()->ExpressionStackAt(1);
7599 HValue* key = environment()->ExpressionStackAt(0);
7600
whesse@chromium.org7b260152011-06-20 15:33:18 +00007601 bool has_side_effects = false;
7602 HValue* load = HandleKeyedElementAccess(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007603 obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
whesse@chromium.org7b260152011-06-20 15:33:18 +00007604 false, // is_store
7605 &has_side_effects);
7606 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007607 if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007608
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007609
danno@chromium.org160a7b02011-04-18 15:51:38 +00007610 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007611 HValue* right = Pop();
7612 HValue* left = Pop();
7613
7614 HInstruction* instr = BuildBinaryOperation(operation, left, right);
7615 PushAndAdd(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007616 if (instr->HasObservableSideEffects()) {
7617 AddSimulate(operation->id(), REMOVABLE_SIMULATE);
7618 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007619
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007620 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00007621 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
7622 RelocInfo::kNoPosition,
7623 true, // is_store
7624 &has_side_effects);
7625
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007626 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007627 Drop(3);
7628 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007629 ASSERT(has_side_effects); // Stores always have side effects.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007630 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007631 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007632 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007633
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007634 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007635 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007636 }
7637}
7638
7639
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007640void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007641 ASSERT(!HasStackOverflow());
7642 ASSERT(current_block() != NULL);
7643 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007644 VariableProxy* proxy = expr->target()->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007645 Property* prop = expr->target()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007646 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007647
7648 if (expr->is_compound()) {
7649 HandleCompoundAssignment(expr);
7650 return;
7651 }
7652
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007653 if (prop != NULL) {
7654 HandlePropertyAssignment(expr);
7655 } else if (proxy != NULL) {
7656 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007657
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00007658 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007659 if (expr->op() != Token::INIT_CONST) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007660 CHECK_ALIVE(VisitForValue(expr->value()));
7661 return ast_context()->ReturnValue(Pop());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007662 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007663
7664 if (var->IsStackAllocated()) {
7665 // We insert a use of the old value to detect unsupported uses of const
7666 // variables (e.g. initialization inside a loop).
7667 HValue* old_value = environment()->Lookup(var);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007668 AddInstruction(new(zone()) HUseConst(old_value));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007669 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007670 } else if (var->mode() == CONST_HARMONY) {
7671 if (expr->op() != Token::INIT_CONST_HARMONY) {
7672 return Bailout("non-initializer assignment to const");
7673 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007674 }
7675
danno@chromium.org160a7b02011-04-18 15:51:38 +00007676 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007677
7678 // Handle the assignment.
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007679 switch (var->location()) {
7680 case Variable::UNALLOCATED:
7681 CHECK_ALIVE(VisitForValue(expr->value()));
7682 HandleGlobalVariableAssignment(var,
7683 Top(),
7684 expr->position(),
7685 expr->AssignmentId());
7686 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007687
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007688 case Variable::PARAMETER:
7689 case Variable::LOCAL: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007690 // Perform an initialization check for let declared variables
7691 // or parameters.
7692 if (var->mode() == LET && expr->op() == Token::ASSIGN) {
7693 HValue* env_value = environment()->Lookup(var);
7694 if (env_value == graph()->GetConstantHole()) {
7695 return Bailout("assignment to let variable before initialization");
7696 }
7697 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007698 // We do not allow the arguments object to occur in a context where it
7699 // may escape, but assignments to stack-allocated locals are
7700 // permitted.
7701 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
7702 HValue* value = Pop();
7703 Bind(var, value);
7704 return ast_context()->ReturnValue(value);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007705 }
7706
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007707 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007708 // Bail out if we try to mutate a parameter value in a function using
7709 // the arguments object. We do not (yet) correctly handle the
7710 // arguments property of the function.
7711 if (info()->scope()->arguments() != NULL) {
7712 // Parameters will rewrite to context slots. We have no direct way
7713 // to detect that the variable is a parameter.
7714 int count = info()->scope()->num_parameters();
7715 for (int i = 0; i < count; ++i) {
7716 if (var == info()->scope()->parameter(i)) {
7717 return Bailout("assignment to parameter in arguments object");
7718 }
7719 }
7720 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007721
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007722 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007723 HStoreContextSlot::Mode mode;
7724 if (expr->op() == Token::ASSIGN) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007725 switch (var->mode()) {
7726 case LET:
7727 mode = HStoreContextSlot::kCheckDeoptimize;
7728 break;
7729 case CONST:
7730 return ast_context()->ReturnValue(Pop());
7731 case CONST_HARMONY:
7732 // This case is checked statically so no need to
7733 // perform checks here
7734 UNREACHABLE();
7735 default:
7736 mode = HStoreContextSlot::kNoCheck;
7737 }
7738 } else if (expr->op() == Token::INIT_VAR ||
7739 expr->op() == Token::INIT_LET ||
7740 expr->op() == Token::INIT_CONST_HARMONY) {
7741 mode = HStoreContextSlot::kNoCheck;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007742 } else {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007743 ASSERT(expr->op() == Token::INIT_CONST);
7744
7745 mode = HStoreContextSlot::kCheckIgnoreAssignment;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007746 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00007747
7748 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007749 HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
7750 context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007751 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007752 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007753 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007754 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007755 return ast_context()->ReturnValue(Pop());
7756 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007757
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00007758 case Variable::LOOKUP:
7759 return Bailout("assignment to LOOKUP variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007760 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007761 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007762 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007763 }
7764}
7765
7766
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007767void HOptimizedGraphBuilder::VisitYield(Yield* expr) {
7768 // Generators are not optimized, so we should never get here.
7769 UNREACHABLE();
7770}
7771
7772
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007773void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007774 ASSERT(!HasStackOverflow());
7775 ASSERT(current_block() != NULL);
7776 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007777 // We don't optimize functions with invalid left-hand sides in
7778 // assignments, count operations, or for-in. Consequently throw can
7779 // currently only occur in an effect context.
7780 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007781 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007782
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007783 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007784 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007785 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007786 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00007787 AddInstruction(instr);
7788 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007789 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00007790 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007791}
7792
7793
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007794HLoadNamedField* HOptimizedGraphBuilder::BuildLoadNamedField(
7795 HValue* object,
7796 Handle<Map> map,
7797 LookupResult* lookup) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007798 int index = lookup->GetLocalFieldIndexFromMap(*map);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007799 // Negative property indices are in-object properties, indexed from the end of
7800 // the fixed part of the object. Non-negative property indices are in the
7801 // properties array.
7802 int inobject = index < 0;
7803 Representation representation = lookup->representation();
7804 int offset = inobject
7805 ? index * kPointerSize + map->instance_size()
7806 : index * kPointerSize + FixedArray::kHeaderSize;
7807 return DoBuildLoadNamedField(object, inobject, representation, offset);
7808}
7809
7810
7811HLoadNamedField* HGraphBuilder::DoBuildLoadNamedField(
7812 HValue* object,
7813 bool inobject,
7814 Representation representation,
7815 int offset) {
7816 bool load_double = false;
7817 if (representation.IsDouble()) {
7818 representation = Representation::Tagged();
7819 load_double = FLAG_track_double_fields;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007820 }
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007821 HLoadNamedField* field =
7822 new(zone()) HLoadNamedField(object, inobject, representation, offset);
7823 if (load_double) {
7824 AddInstruction(field);
7825 field->set_type(HType::HeapNumber());
7826 return new(zone()) HLoadNamedField(
7827 field, true, Representation::Double(), HeapNumber::kValueOffset);
7828 }
7829 return field;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007830}
7831
7832
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007833HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric(
7834 HValue* object,
7835 Handle<String> name,
7836 Property* expr) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00007837 if (expr->IsUninitialized()) {
7838 AddSoftDeoptimize();
jkummerow@chromium.org531dfe82012-03-20 13:01:16 +00007839 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007840 HValue* context = environment()->LookupContext();
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007841 return new(zone()) HLoadNamedGeneric(context, object, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007842}
7843
7844
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007845HInstruction* HOptimizedGraphBuilder::BuildCallGetter(
7846 HValue* object,
7847 Handle<Map> map,
7848 Handle<JSFunction> getter,
7849 Handle<JSObject> holder) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007850 AddCheckConstantFunction(holder, object, map);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007851 AddInstruction(new(zone()) HPushArgument(object));
7852 return new(zone()) HCallConstantFunction(getter, 1);
danno@chromium.org81cac2b2012-07-10 11:28:27 +00007853}
7854
7855
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007856HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
7857 HValue* object,
7858 Handle<String> name,
7859 Property* expr,
7860 Handle<Map> map) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007861 // Handle a load from a known field.
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007862 ASSERT(!map->is_dictionary_map());
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007863
7864 // Handle access to various length properties
7865 if (name->Equals(isolate()->heap()->length_string())) {
7866 if (map->instance_type() == JS_ARRAY_TYPE) {
7867 AddCheckMapsWithTransitions(object, map);
7868 return HLoadNamedField::NewArrayLength(zone(), object, object);
7869 }
7870 }
7871
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007872 LookupResult lookup(isolate());
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007873 map->LookupDescriptor(NULL, *name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00007874 if (lookup.IsField()) {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007875 AddCheckMap(object, map);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007876 return BuildLoadNamedField(object, map, &lookup);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007877 }
7878
7879 // Handle a load of a constant known function.
7880 if (lookup.IsConstantFunction()) {
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007881 AddCheckMap(object, map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007882 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007883 return new(zone()) HConstant(function, Representation::Tagged());
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007884 }
7885
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007886 // Handle a load from a known field somewhere in the prototype chain.
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007887 LookupInPrototypes(map, name, &lookup);
7888 if (lookup.IsField()) {
7889 Handle<JSObject> prototype(JSObject::cast(map->prototype()));
7890 Handle<JSObject> holder(lookup.holder());
7891 Handle<Map> holder_map(holder->map());
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007892 AddCheckMap(object, map);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007893 AddInstruction(
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007894 new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007895 HValue* holder_value = AddInstruction(
7896 new(zone()) HConstant(holder, Representation::Tagged()));
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007897 return BuildLoadNamedField(holder_value, holder_map, &lookup);
7898 }
7899
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007900 // Handle a load of a constant function somewhere in the prototype chain.
7901 if (lookup.IsConstantFunction()) {
7902 Handle<JSObject> prototype(JSObject::cast(map->prototype()));
7903 Handle<JSObject> holder(lookup.holder());
7904 Handle<Map> holder_map(holder->map());
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007905 AddCheckMap(object, map);
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007906 AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
7907 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*holder_map));
7908 return new(zone()) HConstant(function, Representation::Tagged());
7909 }
7910
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00007911 // No luck, do a generic load.
7912 return BuildLoadNamedGeneric(object, name, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007913}
7914
7915
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007916HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
7917 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007918 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007919 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007920}
7921
7922
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007923HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
7924 HValue* object,
7925 HValue* key,
whesse@chromium.org7b260152011-06-20 15:33:18 +00007926 HValue* val,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007927 HValue* dependency,
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007928 Handle<Map> map,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00007929 bool is_store,
7930 KeyedAccessStoreMode store_mode) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00007931 HCheckMaps* mapcheck = HCheckMaps::New(object, map, zone(), dependency);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00007932 AddInstruction(mapcheck);
7933 if (dependency) {
7934 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
7935 }
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007936 return BuildUncheckedMonomorphicElementAccess(
7937 object, key, val,
7938 mapcheck, map->instance_type() == JS_ARRAY_TYPE,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00007939 map->elements_kind(), is_store, store_mode);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00007940}
7941
7942
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007943HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad(
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00007944 HValue* object,
7945 HValue* key,
7946 HValue* val,
7947 SmallMapList* maps) {
7948 // For polymorphic loads of similar elements kinds (i.e. all tagged or all
7949 // double), always use the "worst case" code without a transition. This is
7950 // much faster than transitioning the elements to the worst case, trading a
7951 // HTransitionElements for a HCheckMaps, and avoiding mutation of the array.
7952 bool has_double_maps = false;
7953 bool has_smi_or_object_maps = false;
7954 bool has_js_array_access = false;
7955 bool has_non_js_array_access = false;
7956 Handle<Map> most_general_consolidated_map;
7957 for (int i = 0; i < maps->length(); ++i) {
7958 Handle<Map> map = maps->at(i);
7959 // Don't allow mixing of JSArrays with JSObjects.
7960 if (map->instance_type() == JS_ARRAY_TYPE) {
7961 if (has_non_js_array_access) return NULL;
7962 has_js_array_access = true;
7963 } else if (has_js_array_access) {
7964 return NULL;
7965 } else {
7966 has_non_js_array_access = true;
7967 }
7968 // Don't allow mixed, incompatible elements kinds.
7969 if (map->has_fast_double_elements()) {
7970 if (has_smi_or_object_maps) return NULL;
7971 has_double_maps = true;
7972 } else if (map->has_fast_smi_or_object_elements()) {
7973 if (has_double_maps) return NULL;
7974 has_smi_or_object_maps = true;
7975 } else {
7976 return NULL;
7977 }
7978 // Remember the most general elements kind, the code for its load will
7979 // properly handle all of the more specific cases.
7980 if ((i == 0) || IsMoreGeneralElementsKindTransition(
7981 most_general_consolidated_map->elements_kind(),
7982 map->elements_kind())) {
7983 most_general_consolidated_map = map;
7984 }
7985 }
7986 if (!has_double_maps && !has_smi_or_object_maps) return NULL;
7987
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00007988 HCheckMaps* check_maps = HCheckMaps::New(object, maps, zone());
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00007989 AddInstruction(check_maps);
7990 HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007991 object, key, val, check_maps,
7992 most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE,
7993 most_general_consolidated_map->elements_kind(),
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00007994 false, STANDARD_STORE);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00007995 return instr;
7996}
7997
7998
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007999HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
8000 HValue* object,
8001 HValue* key,
8002 HValue* val,
8003 Expression* prop,
8004 BailoutId ast_id,
8005 int position,
8006 bool is_store,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008007 KeyedAccessStoreMode store_mode,
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008008 bool* has_side_effects) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008009 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008010 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00008011 SmallMapList* maps = prop->GetReceiverTypes();
whesse@chromium.org7b260152011-06-20 15:33:18 +00008012 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008013
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00008014 if (!is_store) {
8015 HInstruction* consolidated_load =
8016 TryBuildConsolidatedElementLoad(object, key, val, maps);
8017 if (consolidated_load != NULL) {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00008018 *has_side_effects |= consolidated_load->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00008019 if (position != RelocInfo::kNoPosition) {
8020 consolidated_load->set_position(position);
8021 }
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00008022 return consolidated_load;
8023 }
8024 }
8025
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00008026 static const int kNumElementTypes = kElementsKindCount;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008027 bool type_todo[kNumElementTypes];
8028 for (int i = 0; i < kNumElementTypes; ++i) {
8029 type_todo[i] = false;
8030 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008031
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008032 // Elements_kind transition support.
8033 MapHandleList transition_target(maps->length());
8034 // Collect possible transition targets.
8035 MapHandleList possible_transitioned_maps(maps->length());
whesse@chromium.org7b260152011-06-20 15:33:18 +00008036 for (int i = 0; i < maps->length(); ++i) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008037 Handle<Map> map = maps->at(i);
8038 ElementsKind elements_kind = map->elements_kind();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008039 if (IsFastElementsKind(elements_kind) &&
8040 elements_kind != GetInitialFastElementsKind()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008041 possible_transitioned_maps.Add(map);
8042 }
8043 }
8044 // Get transition target for each map (NULL == no transition).
8045 for (int i = 0; i < maps->length(); ++i) {
8046 Handle<Map> map = maps->at(i);
8047 Handle<Map> transitioned_map =
8048 map->FindTransitionedMap(&possible_transitioned_maps);
8049 transition_target.Add(transitioned_map);
8050 }
8051
8052 int num_untransitionable_maps = 0;
8053 Handle<Map> untransitionable_map;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008054 HTransitionElementsKind* transition = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008055 for (int i = 0; i < maps->length(); ++i) {
8056 Handle<Map> map = maps->at(i);
8057 ASSERT(map->IsMap());
8058 if (!transition_target.at(i).is_null()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008059 ASSERT(Map::IsValidElementsTransition(
8060 map->elements_kind(),
8061 transition_target.at(i)->elements_kind()));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00008062 HValue* context = environment()->LookupContext();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008063 transition = new(zone()) HTransitionElementsKind(
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00008064 context, object, map, transition_target.at(i));
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008065 AddInstruction(transition);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008066 } else {
8067 type_todo[map->elements_kind()] = true;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008068 if (IsExternalArrayElementsKind(map->elements_kind())) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008069 todo_external_array = true;
8070 }
8071 num_untransitionable_maps++;
8072 untransitionable_map = map;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00008073 }
8074 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008075
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008076 // If only one map is left after transitioning, handle this case
8077 // monomorphically.
8078 if (num_untransitionable_maps == 1) {
ulan@chromium.org2efb9002012-01-19 15:36:35 +00008079 HInstruction* instr = NULL;
8080 if (untransitionable_map->has_slow_elements_kind()) {
8081 instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
8082 : BuildLoadKeyedGeneric(object, key));
8083 } else {
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008084 instr = BuildMonomorphicElementAccess(
8085 object, key, val, transition, untransitionable_map, is_store,
8086 store_mode);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00008087 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008088 *has_side_effects |= instr->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00008089 if (position != RelocInfo::kNoPosition) instr->set_position(position);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008090 return is_store ? NULL : instr;
8091 }
8092
verwaest@chromium.org68c05782012-09-04 09:58:32 +00008093 HInstruction* checkspec =
8094 AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00008095 HBasicBlock* join = graph()->CreateBasicBlock();
8096
8097 HInstruction* elements_kind_instr =
8098 AddInstruction(new(zone()) HElementsKind(object));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00008099 HInstruction* elements = AddLoadElements(object, checkspec);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008100 HLoadExternalArrayPointer* external_elements = NULL;
8101 HInstruction* checked_key = NULL;
8102
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008103 // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds
8104 // are handled before external arrays.
8105 STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
8106 STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008107 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
8108 STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008109
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008110 for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00008111 elements_kind <= LAST_ELEMENTS_KIND;
8112 elements_kind = ElementsKind(elements_kind + 1)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008113 // After having handled FAST_* and DICTIONARY_ELEMENTS, we need to add some
8114 // code that's executed for all external array cases.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00008115 STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
8116 LAST_ELEMENTS_KIND);
8117 if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008118 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008119 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008120 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008121 checked_key = AddBoundsCheck(key, length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008122 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
8123 AddInstruction(external_elements);
8124 }
8125 if (type_todo[elements_kind]) {
8126 HBasicBlock* if_true = graph()->CreateBasicBlock();
8127 HBasicBlock* if_false = graph()->CreateBasicBlock();
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008128 HCompareConstantEqAndBranch* elements_kind_branch =
8129 new(zone()) HCompareConstantEqAndBranch(
8130 elements_kind_instr, elements_kind, Token::EQ_STRICT);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008131 elements_kind_branch->SetSuccessorAt(0, if_true);
8132 elements_kind_branch->SetSuccessorAt(1, if_false);
8133 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008134
8135 set_current_block(if_true);
8136 HInstruction* access;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00008137 if (IsFastElementsKind(elements_kind)) {
8138 if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +00008139 AddInstruction(HCheckMaps::New(
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008140 elements, isolate()->factory()->fixed_array_map(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008141 zone(), elements_kind_branch));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008142 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008143 // TODO(jkummerow): The need for these two blocks could be avoided
8144 // in one of two ways:
8145 // (1) Introduce ElementsKinds for JSArrays that are distinct from
8146 // those for fast objects.
8147 // (2) Put the common instructions into a third "join" block. This
8148 // requires additional AST IDs that we can deopt to from inside
8149 // that join block. They must be added to the Property class (when
8150 // it's a keyed property) and registered in the full codegen.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008151 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
8152 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008153 HHasInstanceTypeAndBranch* typecheck =
8154 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
8155 typecheck->SetSuccessorAt(0, if_jsarray);
8156 typecheck->SetSuccessorAt(1, if_fastobject);
8157 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008158
8159 set_current_block(if_jsarray);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008160 HInstruction* length;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00008161 length = AddInstruction(
8162 HLoadNamedField::NewArrayLength(zone(), object, typecheck,
8163 HType::Smi()));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008164 checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008165 access = AddInstruction(BuildFastElementAccess(
yangguo@chromium.org304cc332012-07-24 07:59:48 +00008166 elements, checked_key, val, elements_kind_branch,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008167 elements_kind, is_store, STANDARD_STORE));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008168 if (!is_store) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008169 Push(access);
8170 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008171
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008172 *has_side_effects |= access->HasObservableSideEffects();
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00008173 // The caller will use has_side_effects and add correct Simulate.
8174 access->SetFlag(HValue::kHasNoObservableSideEffects);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008175 if (position != -1) {
8176 access->set_position(position);
8177 }
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00008178 if_jsarray->GotoNoSimulate(join);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008179
8180 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008181 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008182 checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008183 access = AddInstruction(BuildFastElementAccess(
yangguo@chromium.org304cc332012-07-24 07:59:48 +00008184 elements, checked_key, val, elements_kind_branch,
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008185 elements_kind, is_store, STANDARD_STORE));
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00008186 } else if (elements_kind == DICTIONARY_ELEMENTS) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008187 if (is_store) {
8188 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
8189 } else {
8190 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
8191 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008192 } else { // External array elements.
8193 access = AddInstruction(BuildExternalArrayElementAccess(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008194 external_elements, checked_key, val,
8195 elements_kind_branch, elements_kind, is_store));
whesse@chromium.org7b260152011-06-20 15:33:18 +00008196 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008197 *has_side_effects |= access->HasObservableSideEffects();
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00008198 // The caller will use has_side_effects and add correct Simulate.
8199 access->SetFlag(HValue::kHasNoObservableSideEffects);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00008200 if (position != RelocInfo::kNoPosition) access->set_position(position);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008201 if (!is_store) {
8202 Push(access);
8203 }
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00008204 current_block()->GotoNoSimulate(join);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008205 set_current_block(if_false);
8206 }
8207 }
8208
8209 // Deopt if none of the cases matched.
8210 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008211 set_current_block(join);
8212 return is_store ? NULL : Pop();
8213}
8214
8215
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008216HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
8217 HValue* obj,
8218 HValue* key,
8219 HValue* val,
8220 Expression* expr,
8221 BailoutId ast_id,
8222 int position,
8223 bool is_store,
8224 bool* has_side_effects) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008225 ASSERT(!expr->IsPropertyName());
8226 HInstruction* instr = NULL;
8227 if (expr->IsMonomorphic()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008228 Handle<Map> map = expr->GetMonomorphicReceiverType();
ulan@chromium.org2efb9002012-01-19 15:36:35 +00008229 if (map->has_slow_elements_kind()) {
8230 instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
8231 : BuildLoadKeyedGeneric(obj, key);
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008232 AddInstruction(instr);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00008233 } else {
8234 AddInstruction(new(zone()) HCheckNonSmi(obj));
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008235 instr = BuildMonomorphicElementAccess(
8236 obj, key, val, NULL, map, is_store, expr->GetStoreMode());
ulan@chromium.org2efb9002012-01-19 15:36:35 +00008237 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008238 } else if (expr->GetReceiverTypes() != NULL &&
8239 !expr->GetReceiverTypes()->is_empty()) {
8240 return HandlePolymorphicElementAccess(
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008241 obj, key, val, expr, ast_id, position, is_store,
8242 expr->GetStoreMode(), has_side_effects);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008243 } else {
8244 if (is_store) {
8245 instr = BuildStoreKeyedGeneric(obj, key, val);
8246 } else {
8247 instr = BuildLoadKeyedGeneric(obj, key);
8248 }
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +00008249 AddInstruction(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008250 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00008251 if (position != RelocInfo::kNoPosition) instr->set_position(position);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008252 *has_side_effects = instr->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00008253 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00008254}
8255
8256
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008257HInstruction* HOptimizedGraphBuilder::BuildStoreKeyedGeneric(
8258 HValue* object,
8259 HValue* key,
8260 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008261 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00008262 return new(zone()) HStoreKeyedGeneric(
8263 context,
8264 object,
8265 key,
8266 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008267 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008268}
8269
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008270
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008271void HOptimizedGraphBuilder::EnsureArgumentsArePushedForAccess() {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008272 // Outermost function already has arguments on the stack.
8273 if (function_state()->outer() == NULL) return;
8274
8275 if (function_state()->arguments_pushed()) return;
8276
8277 // Push arguments when entering inlined function.
8278 HEnterInlined* entry = function_state()->entry();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00008279 entry->set_arguments_pushed();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008280
8281 ZoneList<HValue*>* arguments_values = entry->arguments_values();
8282
8283 HInstruction* insert_after = entry;
8284 for (int i = 0; i < arguments_values->length(); i++) {
8285 HValue* argument = arguments_values->at(i);
8286 HInstruction* push_argument = new(zone()) HPushArgument(argument);
8287 push_argument->InsertAfter(insert_after);
8288 insert_after = push_argument;
8289 }
8290
8291 HArgumentsElements* arguments_elements =
8292 new(zone()) HArgumentsElements(true);
8293 arguments_elements->ClearFlag(HValue::kUseGVN);
8294 arguments_elements->InsertAfter(insert_after);
8295 function_state()->set_arguments_elements(arguments_elements);
8296}
8297
8298
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008299bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008300 VariableProxy* proxy = expr->obj()->AsVariableProxy();
8301 if (proxy == NULL) return false;
8302 if (!proxy->var()->IsStackAllocated()) return false;
8303 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
8304 return false;
8305 }
8306
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008307 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008308 if (expr->key()->IsPropertyName()) {
8309 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00008310 if (!name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("length"))) return false;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008311
8312 if (function_state()->outer() == NULL) {
8313 HInstruction* elements = AddInstruction(
8314 new(zone()) HArgumentsElements(false));
8315 result = new(zone()) HArgumentsLength(elements);
8316 } else {
8317 // Number of arguments without receiver.
8318 int argument_count = environment()->
8319 arguments_environment()->parameter_count() - 1;
8320 result = new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00008321 Handle<Object>(Smi::FromInt(argument_count), isolate()),
8322 Representation::Integer32());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008323 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008324 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00008325 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008326 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008327 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008328 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00008329 Drop(1); // Arguments object.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008330 if (function_state()->outer() == NULL) {
8331 HInstruction* elements = AddInstruction(
8332 new(zone()) HArgumentsElements(false));
8333 HInstruction* length = AddInstruction(
8334 new(zone()) HArgumentsLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008335 HInstruction* checked_key = AddBoundsCheck(key, length);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008336 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
8337 } else {
8338 EnsureArgumentsArePushedForAccess();
8339
8340 // Number of arguments without receiver.
8341 HInstruction* elements = function_state()->arguments_elements();
8342 int argument_count = environment()->
8343 arguments_environment()->parameter_count() - 1;
8344 HInstruction* length = AddInstruction(new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00008345 Handle<Object>(Smi::FromInt(argument_count), isolate()),
8346 Representation::Integer32()));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008347 HInstruction* checked_key = AddBoundsCheck(key, length);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008348 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
8349 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008350 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008351 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008352 return true;
8353}
8354
8355
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008356void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008357 ASSERT(!HasStackOverflow());
8358 ASSERT(current_block() != NULL);
8359 ASSERT(current_block()->HasPredecessor());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008360 expr->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008361
8362 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008363
danno@chromium.org160a7b02011-04-18 15:51:38 +00008364 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008365
8366 HInstruction* instr = NULL;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00008367 if (expr->IsStringLength()) {
ager@chromium.org378b34e2011-01-28 08:04:38 +00008368 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008369 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008370 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008371 instr = HStringLength::New(zone(), string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008372 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008373 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008374 HValue* index = Pop();
8375 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008376 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008377 HInstruction* char_code =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008378 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00008379 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008380 instr = HStringCharFromCode::New(zone(), context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00008381
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00008382 } else if (expr->IsFunctionPrototype()) {
8383 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008384 AddInstruction(new(zone()) HCheckNonSmi(function));
8385 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008386
8387 } else if (expr->key()->IsPropertyName()) {
8388 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00008389 SmallMapList* types = expr->GetReceiverTypes();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00008390 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008391
erik.corry@gmail.com88767242012-08-08 14:43:45 +00008392 Handle<Map> map;
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00008393 bool monomorphic = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008394 if (expr->IsMonomorphic()) {
erik.corry@gmail.com88767242012-08-08 14:43:45 +00008395 map = types->first();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00008396 monomorphic = !map->is_dictionary_map();
8397 } else if (object->HasMonomorphicJSObjectType()) {
8398 map = object->GetMonomorphicJSObjectMap();
8399 monomorphic = !map->is_dictionary_map();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00008400 }
8401 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008402 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008403 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008404 if (LookupGetter(map, name, &getter, &holder)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008405 AddCheckConstantFunction(holder, Top(), map);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008406 if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
8407 AddInstruction(new(zone()) HPushArgument(Pop()));
8408 instr = new(zone()) HCallConstantFunction(getter, 1);
8409 } else {
8410 instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
8411 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008412 } else if (types != NULL && types->length() > 1) {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008413 return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008414 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008415 instr = BuildLoadNamedGeneric(Pop(), name, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008416 }
8417
8418 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008419 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008420
8421 HValue* key = Pop();
8422 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00008423
8424 bool has_side_effects = false;
8425 HValue* load = HandleKeyedElementAccess(
8426 obj, key, NULL, expr, expr->id(), expr->position(),
8427 false, // is_store
8428 &has_side_effects);
8429 if (has_side_effects) {
8430 if (ast_context()->IsEffect()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008431 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008432 } else {
8433 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008434 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008435 Drop(1);
8436 }
8437 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008438 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008439 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008440 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008441 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008442}
8443
8444
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008445void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
8446 Handle<Map> receiver_map) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00008447 if (!holder.is_null()) {
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00008448 Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
8449 AddInstruction(
8450 new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008451 }
8452}
8453
8454
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008455void HOptimizedGraphBuilder::AddCheckConstantFunction(
8456 Handle<JSObject> holder,
8457 HValue* receiver,
8458 Handle<Map> receiver_map) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008459 // Constant functions have the nice property that the map will change if they
8460 // are overwritten. Therefore it is enough to check the map of the holder and
8461 // its prototypes.
8462 AddCheckMapsWithTransitions(receiver, receiver_map);
8463 AddCheckPrototypeMaps(holder, receiver_map);
8464}
8465
8466
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008467class FunctionSorter {
8468 public:
8469 FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
8470 FunctionSorter(int index, int ticks, int ast_length, int src_length)
8471 : index_(index),
8472 ticks_(ticks),
8473 ast_length_(ast_length),
8474 src_length_(src_length) { }
8475
8476 int index() const { return index_; }
8477 int ticks() const { return ticks_; }
8478 int ast_length() const { return ast_length_; }
8479 int src_length() const { return src_length_; }
8480
8481 private:
8482 int index_;
8483 int ticks_;
8484 int ast_length_;
8485 int src_length_;
8486};
8487
8488
danno@chromium.orgca29dd82013-04-26 11:59:48 +00008489inline bool operator<(const FunctionSorter& lhs, const FunctionSorter& rhs) {
8490 int diff = lhs.ticks() - rhs.ticks();
8491 if (diff != 0) return diff > 0;
8492 diff = lhs.ast_length() - rhs.ast_length();
8493 if (diff != 0) return diff < 0;
8494 return lhs.src_length() < rhs.src_length();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008495}
8496
8497
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008498void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
8499 Call* expr,
8500 HValue* receiver,
8501 SmallMapList* types,
8502 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008503 // TODO(ager): We should recognize when the prototype chains for different
8504 // maps are identical. In that case we can avoid repeatedly generating the
8505 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008506 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008507 HBasicBlock* join = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008508 FunctionSorter order[kMaxCallPolymorphism];
8509 int ordered_functions = 0;
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008510
8511 Handle<Map> initial_string_map(
8512 isolate()->native_context()->string_function()->initial_map());
8513 Handle<Map> string_marker_map(
8514 JSObject::cast(initial_string_map->prototype())->map());
8515 Handle<Map> initial_number_map(
8516 isolate()->native_context()->number_function()->initial_map());
8517 Handle<Map> number_marker_map(
8518 JSObject::cast(initial_number_map->prototype())->map());
8519 Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
8520
8521 bool handle_smi = false;
8522
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008523 for (int i = 0;
8524 i < types->length() && ordered_functions < kMaxCallPolymorphism;
8525 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008526 Handle<Map> map = types->at(i);
8527 if (expr->ComputeTarget(map, name)) {
svenpanne@chromium.org876cca82013-03-18 14:43:20 +00008528 if (map.is_identical_to(number_marker_map)) handle_smi = true;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008529 order[ordered_functions++] =
8530 FunctionSorter(i,
8531 expr->target()->shared()->profiler_ticks(),
8532 InliningAstSize(expr->target()),
8533 expr->target()->shared()->SourceSize());
danno@chromium.org2c26cb12012-05-03 09:06:43 +00008534 }
danno@chromium.org2c26cb12012-05-03 09:06:43 +00008535 }
8536
danno@chromium.orgca29dd82013-04-26 11:59:48 +00008537 std::sort(order, order + ordered_functions);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008538
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008539 HBasicBlock* number_block = NULL;
8540
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008541 for (int fn = 0; fn < ordered_functions; ++fn) {
8542 int i = order[fn].index();
8543 Handle<Map> map = types->at(i);
8544 if (fn == 0) {
8545 // Only needed once.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008546 join = graph()->CreateBasicBlock();
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008547 if (handle_smi) {
8548 HBasicBlock* empty_smi_block = graph()->CreateBasicBlock();
8549 HBasicBlock* not_smi_block = graph()->CreateBasicBlock();
8550 number_block = graph()->CreateBasicBlock();
8551 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(receiver);
8552 smicheck->SetSuccessorAt(0, empty_smi_block);
8553 smicheck->SetSuccessorAt(1, not_smi_block);
8554 current_block()->Finish(smicheck);
8555 empty_smi_block->Goto(number_block);
8556 set_current_block(not_smi_block);
8557 } else {
8558 AddInstruction(new(zone()) HCheckNonSmi(receiver));
8559 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008560 }
8561 HBasicBlock* if_true = graph()->CreateBasicBlock();
8562 HBasicBlock* if_false = graph()->CreateBasicBlock();
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008563 HUnaryControlInstruction* compare;
8564
8565 if (handle_smi && map.is_identical_to(number_marker_map)) {
8566 compare = new(zone()) HCompareMap(
8567 receiver, heap_number_map, if_true, if_false);
8568 map = initial_number_map;
8569 expr->set_number_check(
8570 Handle<JSObject>(JSObject::cast(map->prototype())));
8571 } else if (map.is_identical_to(string_marker_map)) {
8572 compare = new(zone()) HIsStringAndBranch(receiver);
8573 compare->SetSuccessorAt(0, if_true);
8574 compare->SetSuccessorAt(1, if_false);
8575 map = initial_string_map;
8576 expr->set_string_check(
8577 Handle<JSObject>(JSObject::cast(map->prototype())));
8578 } else {
8579 compare = new(zone()) HCompareMap(receiver, map, if_true, if_false);
8580 expr->set_map_check();
8581 }
8582
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008583 current_block()->Finish(compare);
8584
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008585 if (expr->check_type() == NUMBER_CHECK) {
8586 if_true->Goto(number_block);
8587 if_true = number_block;
8588 number_block->SetJoinId(expr->id());
8589 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008590 set_current_block(if_true);
ulan@chromium.org6e196bf2013-03-13 09:38:22 +00008591
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008592 expr->ComputeTarget(map, name);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008593 AddCheckPrototypeMaps(expr->holder(), map);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008594 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
8595 Handle<JSFunction> caller = info()->closure();
8596 SmartArrayPointer<char> caller_name =
8597 caller->shared()->DebugName()->ToCString();
8598 PrintF("Trying to inline the polymorphic call to %s from %s\n",
8599 *name->ToCString(),
8600 *caller_name);
8601 }
8602 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
8603 // Trying to inline will signal that we should bailout from the
8604 // entire compilation by setting stack overflow on the visitor.
8605 if (HasStackOverflow()) return;
8606 } else {
8607 HCallConstantFunction* call =
8608 new(zone()) HCallConstantFunction(expr->target(), argument_count);
8609 call->set_position(expr->position());
8610 PreProcessCall(call);
8611 AddInstruction(call);
8612 if (!ast_context()->IsEffect()) Push(call);
8613 }
8614
8615 if (current_block() != NULL) current_block()->Goto(join);
8616 set_current_block(if_false);
8617 }
8618
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008619 // Finish up. Unconditionally deoptimize if we've handled all the maps we
8620 // know about and do not want to handle ones we've never seen. Otherwise
8621 // use a generic IC.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008622 if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008623 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008624 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008625 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008626 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008627 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008628 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00008629
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008630 if (join != NULL) {
8631 AddInstruction(call);
8632 if (!ast_context()->IsEffect()) Push(call);
8633 current_block()->Goto(join);
8634 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008635 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00008636 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008637 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008638
8639 // We assume that control flow is always live after an expression. So
8640 // even without predecessors to the join block, we set it as the exit
8641 // block and continue by adding instructions there.
8642 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008643 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008644 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008645 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008646 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008647 } else {
8648 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008649 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008650}
8651
8652
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008653void HOptimizedGraphBuilder::TraceInline(Handle<JSFunction> target,
8654 Handle<JSFunction> caller,
8655 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008656 if (FLAG_trace_inlining) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00008657 SmartArrayPointer<char> target_name =
8658 target->shared()->DebugName()->ToCString();
8659 SmartArrayPointer<char> caller_name =
8660 caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008661 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008662 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008663 } else {
8664 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008665 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008666 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008667 }
8668}
8669
8670
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008671static const int kNotInlinable = 1000000000;
8672
8673
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008674int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008675 if (!FLAG_use_inlining) return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008676
8677 // Precondition: call is monomorphic and we have found a target with the
8678 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008679 Handle<JSFunction> caller = info()->closure();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008680 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008681
8682 // Do a quick check on source code length to avoid parsing large
8683 // inlining candidates.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00008684 if (target_shared->SourceSize() >
8685 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008686 TraceInline(target, caller, "target text too big");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008687 return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008688 }
8689
8690 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008691 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008692 TraceInline(target, caller, "target not inlineable");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008693 return kNotInlinable;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008694 }
yangguo@chromium.org56454712012-02-16 15:33:53 +00008695 if (target_shared->dont_inline() || target_shared->dont_optimize()) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00008696 TraceInline(target, caller, "target contains unsupported syntax [early]");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008697 return kNotInlinable;
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00008698 }
8699
8700 int nodes_added = target_shared->ast_node_count();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008701 return nodes_added;
8702}
8703
8704
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008705bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
8706 Handle<JSFunction> target,
8707 int arguments_count,
8708 HValue* implicit_return_value,
8709 BailoutId ast_id,
8710 BailoutId return_id,
8711 InliningKind inlining_kind) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00008712 int nodes_added = InliningAstSize(target);
8713 if (nodes_added == kNotInlinable) return false;
8714
8715 Handle<JSFunction> caller = info()->closure();
8716
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00008717 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00008718 TraceInline(target, caller, "target AST is too large [early]");
8719 return false;
8720 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008721
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008722#if !defined(V8_TARGET_ARCH_IA32)
8723 // Target must be able to use caller's context.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008724 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008725 if (target->context() != outer_info->closure()->context() ||
8726 outer_info->scope()->contains_with() ||
8727 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008728 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008729 return false;
8730 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008731#endif
8732
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008733
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008734 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008735 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008736 int current_level = 1;
8737 while (env->outer() != NULL) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008738 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008739 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008740 return false;
8741 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00008742 if (env->outer()->frame_type() == JS_FUNCTION) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008743 current_level++;
8744 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008745 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008746 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008747
8748 // Don't inline recursive functions.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008749 for (FunctionState* state = function_state();
8750 state != NULL;
8751 state = state->outer()) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00008752 if (*state->compilation_info()->closure() == *target) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008753 TraceInline(target, caller, "target is recursive");
8754 return false;
8755 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008756 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008757
8758 // We don't want to add more than a certain number of nodes from inlining.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00008759 if (inlined_count_ > Min(FLAG_max_inlined_nodes_cumulative,
8760 kUnlimitedMaxInlinedNodesCumulative)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008761 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008762 return false;
8763 }
8764
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008765 // Parse and allocate variables.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00008766 CompilationInfo target_info(target, zone());
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00008767 Handle<SharedFunctionInfo> target_shared(target->shared());
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00008768 if (!Parser::Parse(&target_info) || !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008769 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008770 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008771 SetStackOverflow();
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008772 target_shared->DisableOptimization("parse/scope error");
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008773 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008774 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008775 return false;
8776 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008777
8778 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008779 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008780 return false;
8781 }
8782 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008783
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00008784 // The following conditions must be checked again after re-parsing, because
8785 // earlier the information might not have been complete due to lazy parsing.
8786 nodes_added = function->ast_node_count();
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00008787 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00008788 TraceInline(target, caller, "target AST is too large [late]");
8789 return false;
8790 }
8791 AstProperties::Flags* flags(function->flags());
8792 if (flags->Contains(kDontInline) || flags->Contains(kDontOptimize)) {
8793 TraceInline(target, caller, "target contains unsupported syntax [late]");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008794 return false;
8795 }
8796
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008797 // If the function uses the arguments object check that inlining of functions
8798 // with arguments object is enabled and the arguments-variable is
8799 // stack allocated.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00008800 if (function->scope()->arguments() != NULL) {
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008801 if (!FLAG_inline_arguments) {
8802 TraceInline(target, caller, "target uses arguments object");
8803 return false;
8804 }
8805
8806 if (!function->scope()->arguments()->IsStackAllocated()) {
8807 TraceInline(target,
8808 caller,
8809 "target uses non-stackallocated arguments object");
8810 return false;
8811 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008812 }
8813
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00008814 // All declarations must be inlineable.
8815 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
8816 int decl_count = decls->length();
8817 for (int i = 0; i < decl_count; ++i) {
8818 if (!decls->at(i)->IsInlineable()) {
8819 TraceInline(target, caller, "target has non-trivial declaration");
8820 return false;
8821 }
8822 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008823
8824 // Generate the deoptimization data for the unoptimized version of
8825 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008826 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008827 // Note that we compile here using the same AST that we will use for
8828 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008829 target_info.EnableDeoptimizationSupport();
8830 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008831 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008832 return false;
8833 }
hpayer@chromium.org8432c912013-02-28 15:55:26 +00008834 if (target_shared->scope_info() == ScopeInfo::Empty(isolate())) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008835 // The scope info might not have been set if a lazily compiled
8836 // function is inlined before being called for the first time.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008837 Handle<ScopeInfo> target_scope_info =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008838 ScopeInfo::Create(target_info.scope(), zone());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008839 target_shared->set_scope_info(*target_scope_info);
8840 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008841 target_shared->EnableDeoptimizationSupport(*target_info.code());
8842 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
8843 &target_info,
8844 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008845 }
8846
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008847 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00008848 // After this point, we've made a decision to inline this function (so
8849 // TryInline should always return true).
8850
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008851 // Save the pending call context and type feedback oracle. Set up new ones
8852 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008853 ASSERT(target_shared->has_deoptimization_support());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008854 Handle<Code> unoptimized_code(target_shared->code());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008855 TypeFeedbackOracle target_oracle(
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008856 unoptimized_code,
8857 Handle<Context>(target->context()->native_context()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008858 isolate(),
8859 zone());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008860 // The function state is new-allocated because we need to delete it
8861 // in two different places.
ulan@chromium.org967e2702012-02-28 09:49:15 +00008862 FunctionState* target_state = new FunctionState(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008863 this, &target_info, &target_oracle, inlining_kind);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008864
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008865 HConstant* undefined = graph()->GetConstantUndefined();
yangguo@chromium.org003650e2013-01-24 16:31:08 +00008866 bool undefined_receiver = HEnvironment::UseUndefinedReceiver(
8867 target, function, call_kind, inlining_kind);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008868 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00008869 environment()->CopyForInlining(target,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00008870 arguments_count,
lrn@chromium.org1c092762011-05-09 09:42:16 +00008871 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00008872 undefined,
yangguo@chromium.org003650e2013-01-24 16:31:08 +00008873 function_state()->inlining_kind(),
8874 undefined_receiver);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008875#ifdef V8_TARGET_ARCH_IA32
8876 // IA32 only, overwrite the caller's context in the deoptimization
8877 // environment with the correct one.
8878 //
8879 // TODO(kmillikin): implement the same inlining on other platforms so we
8880 // can remove the unsightly ifdefs in this function.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008881 HConstant* context =
8882 new(zone()) HConstant(Handle<Context>(target->context()),
8883 Representation::Tagged());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008884 AddInstruction(context);
8885 inner_env->BindContext(context);
8886#endif
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008887
danno@chromium.orgb2a1c072012-03-23 15:47:56 +00008888 AddSimulate(return_id);
8889 current_block()->UpdateEnvironment(inner_env);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008890 ZoneList<HValue*>* arguments_values = NULL;
8891
8892 // If the function uses arguments copy current arguments values
8893 // to use them for materialization.
8894 if (function->scope()->arguments() != NULL) {
8895 HEnvironment* arguments_env = inner_env->arguments_environment();
8896 int arguments_count = arguments_env->parameter_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008897 arguments_values = new(zone()) ZoneList<HValue*>(arguments_count, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008898 for (int i = 0; i < arguments_count; i++) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008899 arguments_values->Add(arguments_env->Lookup(i), zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008900 }
8901 }
8902
8903 HEnterInlined* enter_inlined =
8904 new(zone()) HEnterInlined(target,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00008905 arguments_count,
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008906 function,
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008907 function_state()->inlining_kind(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008908 function->scope()->arguments(),
yangguo@chromium.org003650e2013-01-24 16:31:08 +00008909 arguments_values,
8910 undefined_receiver);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008911 function_state()->set_entry(enter_inlined);
8912 AddInstruction(enter_inlined);
8913
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008914 // If the function uses arguments object create and bind one.
8915 if (function->scope()->arguments() != NULL) {
8916 ASSERT(function->scope()->arguments()->IsStackAllocated());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008917 inner_env->Bind(function->scope()->arguments(),
8918 graph()->GetArgumentsObject());
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008919 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008920
8921
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00008922 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008923 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008924 if (HasStackOverflow()) {
8925 // Bail out if the inline function did, as we cannot residualize a call
8926 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008927 TraceInline(target, caller, "inline graph construction failed");
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008928 target_shared->DisableOptimization("inlining bailed out");
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008929 inline_bailout_ = true;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008930 delete target_state;
danno@chromium.org160a7b02011-04-18 15:51:38 +00008931 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008932 }
8933
8934 // Update inlined nodes count.
8935 inlined_count_ += nodes_added;
8936
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008937 ASSERT(unoptimized_code->kind() == Code::FUNCTION);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008938 Handle<TypeFeedbackInfo> type_info(
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008939 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00008940 graph()->update_type_change_checksum(type_info->own_type_change_checksum());
8941
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008942 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008943
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008944 if (current_block() != NULL) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008945 FunctionState* state = function_state();
8946 if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
8947 // Falling off the end of an inlined construct call. In a test context the
8948 // return value will always evaluate to true, in a value context the
8949 // return value is the newly allocated receiver.
8950 if (call_context()->IsTest()) {
8951 current_block()->Goto(inlined_test_context()->if_true(), state);
8952 } else if (call_context()->IsEffect()) {
8953 current_block()->Goto(function_return(), state);
8954 } else {
8955 ASSERT(call_context()->IsValue());
8956 current_block()->AddLeaveInlined(implicit_return_value, state);
8957 }
8958 } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
8959 // Falling off the end of an inlined setter call. The returned value is
8960 // never used, the value of an assignment is always the value of the RHS
8961 // of the assignment.
8962 if (call_context()->IsTest()) {
8963 inlined_test_context()->ReturnValue(implicit_return_value);
8964 } else if (call_context()->IsEffect()) {
8965 current_block()->Goto(function_return(), state);
8966 } else {
8967 ASSERT(call_context()->IsValue());
8968 current_block()->AddLeaveInlined(implicit_return_value, state);
8969 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008970 } else {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008971 // Falling off the end of a normal inlined function. This basically means
8972 // returning undefined.
8973 if (call_context()->IsTest()) {
8974 current_block()->Goto(inlined_test_context()->if_false(), state);
8975 } else if (call_context()->IsEffect()) {
8976 current_block()->Goto(function_return(), state);
8977 } else {
8978 ASSERT(call_context()->IsValue());
8979 current_block()->AddLeaveInlined(undefined, state);
8980 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008981 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008982 }
8983
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008984 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008985 if (inlined_test_context() != NULL) {
8986 HBasicBlock* if_true = inlined_test_context()->if_true();
8987 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008988
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008989 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008990 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008991 ClearInlinedTestContext();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008992 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008993
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008994 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008995 if (if_true->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00008996 if_true->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008997 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008998 if_true->Goto(true_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008999 }
9000 if (if_false->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00009001 if_false->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009002 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00009003 if_false->Goto(false_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009004 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009005 set_current_block(NULL);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009006 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009007
danno@chromium.org160a7b02011-04-18 15:51:38 +00009008 } else if (function_return()->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00009009 function_return()->SetJoinId(ast_id);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009010 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00009011 } else {
9012 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009013 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009014 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009015 return true;
9016}
9017
9018
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009019bool HOptimizedGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00009020 // The function call we are inlining is a method call if the call
9021 // is a property call.
9022 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
9023 ? CALL_AS_FUNCTION
9024 : CALL_AS_METHOD;
9025
9026 return TryInline(call_kind,
9027 expr->target(),
yangguo@chromium.org304cc332012-07-24 07:59:48 +00009028 expr->arguments()->length(),
ulan@chromium.org967e2702012-02-28 09:49:15 +00009029 NULL,
9030 expr->id(),
9031 expr->ReturnId(),
9032 drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
9033}
9034
9035
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009036bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr,
9037 HValue* implicit_return_value) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00009038 return TryInline(CALL_AS_FUNCTION,
9039 expr->target(),
yangguo@chromium.org304cc332012-07-24 07:59:48 +00009040 expr->arguments()->length(),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009041 implicit_return_value,
ulan@chromium.org967e2702012-02-28 09:49:15 +00009042 expr->id(),
9043 expr->ReturnId(),
9044 CONSTRUCT_CALL_RETURN);
9045}
9046
9047
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009048bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter,
9049 Property* prop) {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00009050 return TryInline(CALL_AS_METHOD,
9051 getter,
9052 0,
9053 NULL,
9054 prop->id(),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009055 prop->LoadId(),
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +00009056 GETTER_CALL_RETURN);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00009057}
9058
9059
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009060bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
9061 Assignment* assignment,
9062 HValue* implicit_return_value) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009063 return TryInline(CALL_AS_METHOD,
9064 setter,
9065 1,
9066 implicit_return_value,
9067 assignment->id(),
9068 assignment->AssignmentId(),
9069 SETTER_CALL_RETURN);
9070}
9071
9072
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009073bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
9074 Call* expr,
9075 int arguments_count) {
9076 return TryInline(CALL_AS_METHOD,
9077 function,
9078 arguments_count,
9079 NULL,
9080 expr->id(),
9081 expr->ReturnId(),
9082 NORMAL_RETURN);
9083}
9084
9085
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009086bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
9087 bool drop_extra) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009088 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
9089 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
9090 switch (id) {
danno@chromium.org1f34ad32012-11-26 14:53:56 +00009091 case kMathExp:
9092 if (!FLAG_fast_math) break;
9093 // Fall through if FLAG_fast_math.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009094 case kMathRound:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009095 case kMathFloor:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009096 case kMathAbs:
9097 case kMathSqrt:
9098 case kMathLog:
9099 case kMathSin:
9100 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00009101 case kMathTan:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009102 if (expr->arguments()->length() == 1) {
9103 HValue* argument = Pop();
9104 HValue* context = environment()->LookupContext();
9105 Drop(1); // Receiver.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009106 HInstruction* op =
9107 HUnaryMathOperation::New(zone(), context, argument, id);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009108 op->set_position(expr->position());
9109 if (drop_extra) Drop(1); // Optionally drop the function.
9110 ast_context()->ReturnInstruction(op, expr->id());
9111 return true;
9112 }
9113 break;
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009114 case kMathImul:
9115 if (expr->arguments()->length() == 2) {
9116 HValue* right = Pop();
9117 HValue* left = Pop();
9118 Drop(1); // Receiver.
9119 HValue* context = environment()->LookupContext();
9120 HInstruction* op = HMul::NewImul(zone(), context, left, right);
9121 if (drop_extra) Drop(1); // Optionally drop the function.
9122 ast_context()->ReturnInstruction(op, expr->id());
9123 return true;
9124 }
9125 break;
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009126 default:
9127 // Not supported for inlining yet.
9128 break;
9129 }
9130 return false;
9131}
9132
9133
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009134bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
9135 Call* expr,
9136 HValue* receiver,
9137 Handle<Map> receiver_map,
9138 CheckType check_type) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009139 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009140 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009141 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009142 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009143 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
9144 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009145 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009146 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009147 if (argument_count == 2 && check_type == STRING_CHECK) {
9148 HValue* index = Pop();
9149 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009150 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009151 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009152 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009153 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00009154 expr->holder(),
9155 zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009156 HInstruction* char_code =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009157 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009158 if (id == kStringCharCodeAt) {
9159 ast_context()->ReturnInstruction(char_code, expr->id());
9160 return true;
9161 }
9162 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009163 HInstruction* result =
9164 HStringCharFromCode::New(zone(), context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009165 ast_context()->ReturnInstruction(result, expr->id());
9166 return true;
9167 }
9168 break;
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00009169 case kStringFromCharCode:
9170 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
9171 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
9172 HValue* argument = Pop();
9173 HValue* context = environment()->LookupContext();
9174 Drop(1); // Receiver.
9175 HInstruction* result =
9176 HStringCharFromCode::New(zone(), context, argument);
9177 ast_context()->ReturnInstruction(result, expr->id());
9178 return true;
9179 }
9180 break;
danno@chromium.org1f34ad32012-11-26 14:53:56 +00009181 case kMathExp:
9182 if (!FLAG_fast_math) break;
9183 // Fall through if FLAG_fast_math.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009184 case kMathRound:
9185 case kMathFloor:
9186 case kMathAbs:
9187 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009188 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00009189 case kMathSin:
9190 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00009191 case kMathTan:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009192 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009193 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009194 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009195 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009196 Drop(1); // Receiver.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009197 HInstruction* op =
9198 HUnaryMathOperation::New(zone(), context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009199 op->set_position(expr->position());
9200 ast_context()->ReturnInstruction(op, expr->id());
9201 return true;
9202 }
9203 break;
9204 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009205 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009206 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009207 HValue* right = Pop();
9208 HValue* left = Pop();
9209 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009210 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009211 HInstruction* result = NULL;
9212 // Use sqrt() if exponent is 0.5 or -0.5.
9213 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
9214 double exponent = HConstant::cast(right)->DoubleValue();
9215 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009216 result =
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009217 HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009218 } else if (exponent == -0.5) {
9219 HConstant* double_one =
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00009220 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1),
9221 isolate()),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009222 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009223 AddInstruction(double_one);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009224 HInstruction* sqrt =
9225 HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
9226 AddInstruction(sqrt);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009227 // MathPowHalf doesn't have side effects so there's no need for
9228 // an environment simulation here.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009229 ASSERT(!sqrt->HasObservableSideEffects());
9230 result = HDiv::New(zone(), context, double_one, sqrt);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009231 } else if (exponent == 2.0) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009232 result = HMul::New(zone(), context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009233 }
mstarzinger@chromium.orgb228be02013-04-18 14:56:59 +00009234 } else if (right->EqualsInteger32Constant(2)) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009235 result = HMul::New(zone(), context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009236 }
9237
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009238 if (result == NULL) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009239 result = HPower::New(zone(), left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009240 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009241 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009242 return true;
9243 }
9244 break;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00009245 case kMathRandom:
9246 if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009247 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009248 Drop(1); // Receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00009249 HValue* context = environment()->LookupContext();
9250 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
9251 AddInstruction(global_object);
9252 HRandom* result = new(zone()) HRandom(global_object);
9253 ast_context()->ReturnInstruction(result, expr->id());
9254 return true;
9255 }
9256 break;
9257 case kMathMax:
9258 case kMathMin:
9259 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009260 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00009261 HValue* right = Pop();
9262 HValue* left = Pop();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009263 Drop(1); // Receiver.
9264 HValue* context = environment()->LookupContext();
9265 HMathMinMax::Operation op = (id == kMathMin) ? HMathMinMax::kMathMin
9266 : HMathMinMax::kMathMax;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009267 HInstruction* result =
9268 HMathMinMax::New(zone(), context, left, right, op);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009269 ast_context()->ReturnInstruction(result, expr->id());
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00009270 return true;
9271 }
9272 break;
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009273 case kMathImul:
9274 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
9275 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
9276 HValue* right = Pop();
9277 HValue* left = Pop();
9278 Drop(1); // Receiver.
9279 HValue* context = environment()->LookupContext();
9280 HInstruction* result = HMul::NewImul(zone(), context, left, right);
9281 ast_context()->ReturnInstruction(result, expr->id());
9282 return true;
9283 }
9284 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009285 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009286 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009287 break;
9288 }
9289 return false;
9290}
9291
9292
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009293bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009294 Expression* callee = expr->expression();
9295 Property* prop = callee->AsProperty();
9296 ASSERT(prop != NULL);
9297
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00009298 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
9299 return false;
9300 }
9301 Handle<Map> function_map = expr->GetReceiverTypes()->first();
9302 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
9303 !expr->target()->shared()->HasBuiltinFunctionId() ||
9304 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
9305 return false;
9306 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009307
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00009308 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009309
9310 ZoneList<Expression*>* args = expr->arguments();
9311 if (args->length() != 2) return false;
9312
9313 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00009314 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009315 HValue* arg_two_value = environment()->Lookup(arg_two->var());
9316 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
9317
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009318 // Found pattern f.apply(receiver, arguments).
9319 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00009320 if (HasStackOverflow() || current_block() == NULL) return true;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009321 HValue* function = Top();
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009322 AddCheckConstantFunction(expr->holder(), function, function_map);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009323 Drop(1);
9324
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009325 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00009326 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009327 HValue* receiver = Pop();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00009328
9329 if (function_state()->outer() == NULL) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00009330 HInstruction* elements = AddInstruction(
9331 new(zone()) HArgumentsElements(false));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00009332 HInstruction* length =
9333 AddInstruction(new(zone()) HArgumentsLength(elements));
9334 HValue* wrapped_receiver =
9335 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
9336 HInstruction* result =
9337 new(zone()) HApplyArguments(function,
9338 wrapped_receiver,
9339 length,
9340 elements);
9341 result->set_position(expr->position());
9342 ast_context()->ReturnInstruction(result, expr->id());
9343 return true;
9344 } else {
9345 // We are inside inlined function and we know exactly what is inside
yangguo@chromium.org39110192013-01-16 09:55:08 +00009346 // arguments object. But we need to be able to materialize at deopt.
9347 // TODO(mstarzinger): For now we just ensure arguments are pushed
9348 // right after HEnterInlined, but we could be smarter about this.
9349 EnsureArgumentsArePushedForAccess();
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009350 ASSERT_EQ(environment()->arguments_environment()->parameter_count(),
9351 function_state()->entry()->arguments_values()->length());
9352 HEnterInlined* entry = function_state()->entry();
9353 ZoneList<HValue*>* arguments_values = entry->arguments_values();
9354 int arguments_count = arguments_values->length();
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009355 PushAndAdd(new(zone()) HWrapReceiver(receiver, function));
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009356 for (int i = 1; i < arguments_count; i++) {
9357 Push(arguments_values->at(i));
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009358 }
9359
9360 Handle<JSFunction> known_function;
9361 if (function->IsConstant()) {
9362 HConstant* constant_function = HConstant::cast(function);
9363 known_function = Handle<JSFunction>::cast(constant_function->handle());
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009364 int args_count = arguments_count - 1; // Excluding receiver.
9365 if (TryInlineApply(known_function, expr, args_count)) return true;
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009366 }
9367
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009368 Drop(arguments_count - 1);
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009369 PushAndAdd(new(zone()) HPushArgument(Pop()));
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009370 for (int i = 1; i < arguments_count; i++) {
9371 PushAndAdd(new(zone()) HPushArgument(arguments_values->at(i)));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00009372 }
9373
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009374 HValue* context = environment()->LookupContext();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00009375 HInvokeFunction* call = new(zone()) HInvokeFunction(
9376 context,
9377 function,
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00009378 known_function,
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00009379 arguments_count);
9380 Drop(arguments_count);
yangguo@chromium.org154ff992012-03-13 08:09:54 +00009381 call->set_position(expr->position());
9382 ast_context()->ReturnInstruction(call, expr->id());
9383 return true;
9384 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009385}
9386
9387
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009388// Checks if all maps in |types| are from the same family, i.e., are elements
9389// transitions of each other. Returns either NULL if they are not from the same
9390// family, or a Map* indicating the map with the first elements kind of the
9391// family that is in the list.
9392static Map* CheckSameElementsFamily(SmallMapList* types) {
9393 if (types->length() <= 1) return NULL;
9394 // Check if all maps belong to the same transition family.
9395 Map* kinds[kFastElementsKindCount];
9396 Map* first_map = *types->first();
9397 ElementsKind first_kind = first_map->elements_kind();
9398 if (!IsFastElementsKind(first_kind)) return NULL;
9399 int first_index = GetSequenceIndexFromFastElementsKind(first_kind);
9400 int last_index = first_index;
9401
9402 for (int i = 0; i < kFastElementsKindCount; i++) kinds[i] = NULL;
9403
9404 kinds[first_index] = first_map;
9405
9406 for (int i = 1; i < types->length(); ++i) {
9407 Map* map = *types->at(i);
9408 ElementsKind elements_kind = map->elements_kind();
9409 if (!IsFastElementsKind(elements_kind)) return NULL;
9410 int index = GetSequenceIndexFromFastElementsKind(elements_kind);
9411 if (index < first_index) {
9412 first_index = index;
9413 } else if (index > last_index) {
9414 last_index = index;
9415 } else if (kinds[index] != map) {
9416 return NULL;
9417 }
9418 kinds[index] = map;
9419 }
9420
9421 Map* current = kinds[first_index];
9422 for (int i = first_index + 1; i <= last_index; i++) {
9423 Map* next = kinds[i];
9424 if (next != NULL) {
9425 ElementsKind current_kind = next->elements_kind();
9426 if (next != current->LookupElementsTransitionMap(current_kind)) {
9427 return NULL;
9428 }
9429 current = next;
9430 }
9431 }
9432
9433 return kinds[first_index];
9434}
9435
9436
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009437void HOptimizedGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009438 ASSERT(!HasStackOverflow());
9439 ASSERT(current_block() != NULL);
9440 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009441 Expression* callee = expr->expression();
9442 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009443 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009444
9445 Property* prop = callee->AsProperty();
9446 if (prop != NULL) {
9447 if (!prop->key()->IsPropertyName()) {
9448 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009449 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009450
danno@chromium.org160a7b02011-04-18 15:51:38 +00009451 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009452 // Push receiver and key like the non-optimized code generator expects it.
9453 HValue* key = Pop();
9454 HValue* receiver = Pop();
9455 Push(key);
9456 Push(receiver);
9457
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009458 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009459
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009460 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009461 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009462 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009463 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009464 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009465 }
9466
9467 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00009468 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009469
9470 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009471
danno@chromium.org160a7b02011-04-18 15:51:38 +00009472 CHECK_ALIVE(VisitForValue(prop->obj()));
9473 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009474
9475 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00009476 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009477
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009478 bool monomorphic = expr->IsMonomorphic();
9479 Handle<Map> receiver_map;
9480 if (monomorphic) {
9481 receiver_map = (types == NULL || types->is_empty())
9482 ? Handle<Map>::null()
9483 : types->first();
9484 } else {
9485 Map* family_map = CheckSameElementsFamily(types);
9486 if (family_map != NULL) {
9487 receiver_map = Handle<Map>(family_map);
9488 monomorphic = expr->ComputeTarget(receiver_map, name);
9489 }
9490 }
9491
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00009492 HValue* receiver =
9493 environment()->ExpressionStackAt(expr->arguments()->length());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009494 if (monomorphic) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009495 if (TryInlineBuiltinMethodCall(expr,
9496 receiver,
9497 receiver_map,
9498 expr->check_type())) {
9499 if (FLAG_trace_inlining) {
9500 PrintF("Inlining builtin ");
9501 expr->target()->ShortPrint();
9502 PrintF("\n");
9503 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009504 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009505 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009506
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00009507 if (CallStubCompiler::HasCustomCallGenerator(expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009508 expr->check_type() != RECEIVER_MAP_CHECK) {
9509 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009510 // because it is likely to generate better code. Also use the IC
9511 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009512 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009513 call = PreProcessCall(
9514 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009515 } else {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009516 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009517
ulan@chromium.org967e2702012-02-28 09:49:15 +00009518 if (TryInlineCall(expr)) return;
danno@chromium.org160a7b02011-04-18 15:51:38 +00009519 call = PreProcessCall(
9520 new(zone()) HCallConstantFunction(expr->target(),
9521 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009522 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009523 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00009524 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009525 HandlePolymorphicCallNamed(expr, receiver, types, name);
9526 return;
9527
9528 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009529 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009530 call = PreProcessCall(
9531 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009532 }
9533
9534 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009535 expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009536 VariableProxy* proxy = expr->expression()->AsVariableProxy();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009537 bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009538
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009539 if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00009540 return Bailout("possible direct call to eval");
9541 }
9542
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009543 if (global_call) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009544 Variable* var = proxy->var();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009545 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009546 // If there is a global property cell for the name at compile time and
9547 // access check is not enabled we assume that the function will not change
9548 // and generate optimized code for calling the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009549 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009550 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
9551 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009552 !info()->global_object()->IsAccessCheckNeeded()) {
9553 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009554 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009555 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009556 if (known_global_function) {
9557 // Push the global object instead of the global receiver because
9558 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009559 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009560 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009561 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009562 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009563
danno@chromium.org160a7b02011-04-18 15:51:38 +00009564 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009565 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009566 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009567
9568 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009569 HGlobalReceiver* global_receiver =
9570 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009571 // Index of the receiver from the top of the expression stack.
9572 const int receiver_index = argument_count - 1;
9573 AddInstruction(global_receiver);
9574 ASSERT(environment()->ExpressionStackAt(receiver_index)->
9575 IsGlobalObject());
9576 environment()->SetExpressionStackAt(receiver_index, global_receiver);
9577
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009578 if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
9579 if (FLAG_trace_inlining) {
9580 PrintF("Inlining builtin ");
9581 expr->target()->ShortPrint();
9582 PrintF("\n");
9583 }
9584 return;
9585 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00009586 if (TryInlineCall(expr)) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009587
9588 if (expr->target().is_identical_to(info()->closure())) {
9589 graph()->MarkRecursive();
9590 }
9591
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009592 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00009593 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009594 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009595 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009596 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
9597 AddInstruction(receiver);
9598 PushAndAdd(new(zone()) HPushArgument(receiver));
9599 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009600
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009601 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
9602 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009603 }
9604
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009605 } else if (expr->IsMonomorphic()) {
9606 // The function is on the stack in the unoptimized code during
9607 // evaluation of the arguments.
9608 CHECK_ALIVE(VisitForValue(expr->expression()));
9609 HValue* function = Top();
9610 HValue* context = environment()->LookupContext();
9611 HGlobalObject* global = new(zone()) HGlobalObject(context);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009612 AddInstruction(global);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009613 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009614 PushAndAdd(receiver);
9615 CHECK_ALIVE(VisitExpressions(expr->arguments()));
9616 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009617
9618 if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
9619 if (FLAG_trace_inlining) {
9620 PrintF("Inlining builtin ");
9621 expr->target()->ShortPrint();
9622 PrintF("\n");
9623 }
9624 return;
9625 }
9626
ulan@chromium.org967e2702012-02-28 09:49:15 +00009627 if (TryInlineCall(expr, true)) { // Drop function from environment.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009628 return;
9629 } else {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00009630 call = PreProcessCall(
9631 new(zone()) HInvokeFunction(context,
9632 function,
9633 expr->target(),
9634 argument_count));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009635 Drop(1); // The function.
9636 }
9637
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009638 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00009639 CHECK_ALIVE(VisitForValue(expr->expression()));
9640 HValue* function = Top();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009641 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009642 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009643 AddInstruction(global_object);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009644 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009645 AddInstruction(receiver);
9646 PushAndAdd(new(zone()) HPushArgument(receiver));
9647 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009648
danno@chromium.orgc612e022011-11-10 11:38:15 +00009649 call = new(zone()) HCallFunction(context, function, argument_count);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00009650 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009651 }
9652 }
9653
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009654 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009655 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009656}
9657
9658
ulan@chromium.org967e2702012-02-28 09:49:15 +00009659// Checks whether allocation using the given constructor can be inlined.
9660static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
9661 return constructor->has_initial_map() &&
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00009662 constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
9663 constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize;
ulan@chromium.org967e2702012-02-28 09:49:15 +00009664}
9665
9666
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009667void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009668 ASSERT(!HasStackOverflow());
9669 ASSERT(current_block() != NULL);
9670 ASSERT(current_block()->HasPredecessor());
ulan@chromium.org967e2702012-02-28 09:49:15 +00009671 expr->RecordTypeFeedback(oracle());
9672 int argument_count = expr->arguments()->length() + 1; // Plus constructor.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009673 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00009674
ulan@chromium.org967e2702012-02-28 09:49:15 +00009675 if (FLAG_inline_construct &&
9676 expr->IsMonomorphic() &&
9677 IsAllocationInlineable(expr->target())) {
9678 // The constructor function is on the stack in the unoptimized code
9679 // during evaluation of the arguments.
9680 CHECK_ALIVE(VisitForValue(expr->expression()));
9681 HValue* function = Top();
9682 CHECK_ALIVE(VisitExpressions(expr->arguments()));
9683 Handle<JSFunction> constructor = expr->target();
fschneider@chromium.org35814e52012-03-01 15:43:35 +00009684 HValue* check = AddInstruction(
9685 new(zone()) HCheckFunction(function, constructor));
9686
9687 // Force completion of inobject slack tracking before generating
9688 // allocation code to finalize instance size.
9689 if (constructor->shared()->IsInobjectSlackTrackingInProgress()) {
9690 constructor->shared()->CompleteInobjectSlackTracking();
9691 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00009692
9693 // Replace the constructor function with a newly allocated receiver.
9694 HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
9695 // Index of the receiver from the top of the expression stack.
9696 const int receiver_index = argument_count - 1;
9697 AddInstruction(receiver);
9698 ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
9699 environment()->SetExpressionStackAt(receiver_index, receiver);
9700
9701 if (TryInlineConstruct(expr, receiver)) return;
9702
9703 // TODO(mstarzinger): For now we remove the previous HAllocateObject and
9704 // add HPushArgument for the arguments in case inlining failed. What we
9705 // actually should do is emit HInvokeFunction on the constructor instead
9706 // of using HCallNew as a fallback.
9707 receiver->DeleteAndReplaceWith(NULL);
fschneider@chromium.org35814e52012-03-01 15:43:35 +00009708 check->DeleteAndReplaceWith(NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00009709 environment()->SetExpressionStackAt(receiver_index, function);
9710 HInstruction* call = PreProcessCall(
9711 new(zone()) HCallNew(context, function, argument_count));
9712 call->set_position(expr->position());
9713 return ast_context()->ReturnInstruction(call, expr->id());
9714 } else {
9715 // The constructor function is both an operand to the instruction and an
9716 // argument to the construct call.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009717 bool use_call_new_array = FLAG_optimize_constructed_arrays &&
9718 !(expr->target().is_null()) &&
9719 *(expr->target()) == isolate()->global_context()->array_function();
9720
verwaest@chromium.org753aee42012-07-17 16:15:42 +00009721 CHECK_ALIVE(VisitArgument(expr->expression()));
9722 HValue* constructor = HPushArgument::cast(Top())->argument();
ulan@chromium.org967e2702012-02-28 09:49:15 +00009723 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009724 HCallNew* call;
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009725 if (use_call_new_array) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009726 // TODO(mvstanton): It would be better to use the already created global
9727 // property cell that is shared by full code gen. That way, any transition
9728 // information that happened after crankshaft won't be lost. The right
9729 // way to do that is to begin passing the cell to the type feedback oracle
9730 // instead of just the value in the cell. Do this in a follow-up checkin.
ulan@chromium.org57ff8812013-05-10 08:16:55 +00009731 Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId());
9732 ASSERT(feedback->IsSmi());
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009733 Handle<JSGlobalPropertyCell> cell =
9734 isolate()->factory()->NewJSGlobalPropertyCell(feedback);
9735
9736 // TODO(mvstanton): Here we should probably insert code to check if the
9737 // type cell elements kind is different from when we compiled, and deopt
9738 // in that case. Do this in a follow-up checin.
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009739 call = new(zone()) HCallNewArray(context, constructor, argument_count,
9740 cell);
9741 } else {
9742 call = new(zone()) HCallNew(context, constructor, argument_count);
9743 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00009744 Drop(argument_count);
9745 call->set_position(expr->position());
9746 return ast_context()->ReturnInstruction(call, expr->id());
9747 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009748}
9749
9750
9751// Support for generating inlined runtime functions.
9752
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009753// Lookup table for generators for runtime calls that are generated inline.
9754// Elements of the table are member pointers to functions of
9755// HOptimizedGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009756#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009757 &HOptimizedGraphBuilder::Generate##Name,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009758
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009759const HOptimizedGraphBuilder::InlineFunctionGenerator
9760 HOptimizedGraphBuilder::kInlineFunctionGenerators[] = {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009761 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
9762 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
9763};
9764#undef INLINE_FUNCTION_GENERATOR_ADDRESS
9765
9766
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009767void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009768 ASSERT(!HasStackOverflow());
9769 ASSERT(current_block() != NULL);
9770 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009771 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009772 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009773 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009774
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009775 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009776 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009777 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009778 ASSERT(expr->name()->length() > 0);
9779 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009780 // Call to an inline function.
9781 int lookup_index = static_cast<int>(function->function_id) -
9782 static_cast<int>(Runtime::kFirstInlineFunction);
9783 ASSERT(lookup_index >= 0);
9784 ASSERT(static_cast<size_t>(lookup_index) <
9785 ARRAY_SIZE(kInlineFunctionGenerators));
9786 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
9787
9788 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009789 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009790 } else {
9791 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009792 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009793
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009794 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009795 Handle<String> name = expr->name();
9796 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009797 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009798 new(zone()) HCallRuntime(context, name, function, argument_count);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009799 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009800 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009801 }
9802}
9803
9804
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009805void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009806 ASSERT(!HasStackOverflow());
9807 ASSERT(current_block() != NULL);
9808 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009809 switch (expr->op()) {
9810 case Token::DELETE: return VisitDelete(expr);
9811 case Token::VOID: return VisitVoid(expr);
9812 case Token::TYPEOF: return VisitTypeof(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009813 case Token::SUB: return VisitSub(expr);
9814 case Token::BIT_NOT: return VisitBitNot(expr);
9815 case Token::NOT: return VisitNot(expr);
9816 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009817 }
9818}
9819
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009820void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009821 Property* prop = expr->expression()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009822 VariableProxy* proxy = expr->expression()->AsVariableProxy();
9823 if (prop != NULL) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00009824 CHECK_ALIVE(VisitForValue(prop->obj()));
9825 CHECK_ALIVE(VisitForValue(prop->key()));
9826 HValue* key = Pop();
9827 HValue* obj = Pop();
9828 HValue* context = environment()->LookupContext();
9829 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
9830 return ast_context()->ReturnInstruction(instr, expr->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009831 } else if (proxy != NULL) {
9832 Variable* var = proxy->var();
9833 if (var->IsUnallocated()) {
9834 Bailout("delete with global variable");
9835 } else if (var->IsStackAllocated() || var->IsContextSlot()) {
9836 // Result of deleting non-global variables is false. 'this' is not
9837 // really a variable, though we implement it as one. The
9838 // subexpression does not have side effects.
9839 HValue* value = var->is_this()
9840 ? graph()->GetConstantTrue()
9841 : graph()->GetConstantFalse();
9842 return ast_context()->ReturnValue(value);
9843 } else {
9844 Bailout("delete with non-global variable");
9845 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009846 } else {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009847 // Result of deleting non-property, non-variable reference is true.
9848 // Evaluate the subexpression for side effects.
9849 CHECK_ALIVE(VisitForEffect(expr->expression()));
9850 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009851 }
9852}
9853
9854
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009855void HOptimizedGraphBuilder::VisitVoid(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009856 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009857 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009858}
9859
9860
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009861void HOptimizedGraphBuilder::VisitTypeof(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009862 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
9863 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009864 HValue* context = environment()->LookupContext();
9865 HInstruction* instr = new(zone()) HTypeof(context, value);
9866 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009867}
9868
9869
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009870void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009871 CHECK_ALIVE(VisitForValue(expr->expression()));
9872 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009873 HValue* context = environment()->LookupContext();
9874 HInstruction* instr =
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009875 HMul::New(zone(), context, value, graph()->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00009876 TypeInfo info = oracle()->UnaryType(expr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009877 Representation rep = ToRepresentation(info);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009878 if (info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00009879 AddSoftDeoptimize();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009880 info = TypeInfo::Unknown();
9881 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009882 if (instr->IsBinaryOperation()) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +00009883 HBinaryOperation::cast(instr)->set_observed_input_representation(1, rep);
9884 HBinaryOperation::cast(instr)->set_observed_input_representation(2, rep);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009885 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009886 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009887}
9888
9889
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009890void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009891 CHECK_ALIVE(VisitForValue(expr->expression()));
9892 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009893 TypeInfo info = oracle()->UnaryType(expr);
9894 if (info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00009895 AddSoftDeoptimize();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009896 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009897 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009898 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009899}
9900
9901
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009902void HOptimizedGraphBuilder::VisitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009903 if (ast_context()->IsTest()) {
9904 TestContext* context = TestContext::cast(ast_context());
9905 VisitForControl(expr->expression(),
9906 context->if_false(),
9907 context->if_true());
9908 return;
9909 }
9910
9911 if (ast_context()->IsEffect()) {
9912 VisitForEffect(expr->expression());
9913 return;
9914 }
9915
9916 ASSERT(ast_context()->IsValue());
9917 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
9918 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
9919 CHECK_BAILOUT(VisitForControl(expr->expression(),
9920 materialize_false,
9921 materialize_true));
9922
9923 if (materialize_false->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00009924 materialize_false->SetJoinId(expr->MaterializeFalseId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009925 set_current_block(materialize_false);
9926 Push(graph()->GetConstantFalse());
9927 } else {
9928 materialize_false = NULL;
9929 }
9930
9931 if (materialize_true->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00009932 materialize_true->SetJoinId(expr->MaterializeTrueId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009933 set_current_block(materialize_true);
9934 Push(graph()->GetConstantTrue());
9935 } else {
9936 materialize_true = NULL;
9937 }
9938
9939 HBasicBlock* join =
9940 CreateJoin(materialize_false, materialize_true, expr->id());
9941 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009942 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009943}
9944
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009945
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009946HInstruction* HOptimizedGraphBuilder::BuildIncrement(
9947 bool returns_original_input,
9948 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00009949 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00009950 TypeInfo info = oracle()->IncrementType(expr);
9951 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00009952 if (rep.IsTagged()) {
9953 rep = Representation::Integer32();
9954 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00009955
9956 if (returns_original_input) {
9957 // We need an explicit HValue representing ToNumber(input). The
9958 // actual HChange instruction we need is (sometimes) added in a later
9959 // phase, so it is not available now to be used as an input to HAdd and
9960 // as the return value.
9961 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
9962 AddInstruction(number_input);
9963 Push(number_input);
9964 }
9965
9966 // The addition has no side effects, so we do not need
9967 // to simulate the expression stack after this instruction.
9968 // Any later failures deopt to the load of the input or earlier.
9969 HConstant* delta = (expr->op() == Token::INC)
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009970 ? graph()->GetConstant1()
9971 : graph()->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009972 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009973 HInstruction* instr = HAdd::New(zone(), context, Top(), delta);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009974 // We can't insert a simulate here, because it would break deoptimization,
9975 // so the HAdd must not have side effects, so we must freeze its
9976 // representation.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00009977 instr->AssumeRepresentation(rep);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009978 instr->ClearAllSideEffects();
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00009979 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009980 return instr;
9981}
9982
9983
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009984void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009985 ASSERT(!HasStackOverflow());
9986 ASSERT(current_block() != NULL);
9987 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00009988 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009989 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009990 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009991 if (proxy == NULL && prop == NULL) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00009992 return Bailout("invalid lhs in count operation");
9993 }
9994
9995 // Match the full code generator stack by simulating an extra stack
9996 // element for postfix operations in a non-effect context. The return
9997 // value is ToNumber(input).
9998 bool returns_original_input =
9999 expr->is_postfix() && !ast_context()->IsEffect();
10000 HValue* input = NULL; // ToNumber(original_input).
10001 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010002
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010003 if (proxy != NULL) {
10004 Variable* var = proxy->var();
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +000010005 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000010006 return Bailout("unsupported count operation with const");
10007 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010008 // Argument of the count operation is a variable, not a property.
10009 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +000010010 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010011
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010012 after = BuildIncrement(returns_original_input, expr);
10013 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010014 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010015
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010016 switch (var->location()) {
10017 case Variable::UNALLOCATED:
10018 HandleGlobalVariableAssignment(var,
10019 after,
10020 expr->position(),
10021 expr->AssignmentId());
10022 break;
10023
10024 case Variable::PARAMETER:
10025 case Variable::LOCAL:
10026 Bind(var, after);
10027 break;
10028
10029 case Variable::CONTEXT: {
10030 // Bail out if we try to mutate a parameter value in a function
10031 // using the arguments object. We do not (yet) correctly handle the
10032 // arguments property of the function.
10033 if (info()->scope()->arguments() != NULL) {
10034 // Parameters will rewrite to context slots. We have no direct
10035 // way to detect that the variable is a parameter so we use a
10036 // linear search of the parameter list.
10037 int count = info()->scope()->num_parameters();
10038 for (int i = 0; i < count; ++i) {
10039 if (var == info()->scope()->parameter(i)) {
10040 return Bailout("assignment to parameter in arguments object");
10041 }
whesse@chromium.org7b260152011-06-20 15:33:18 +000010042 }
10043 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010044
10045 HValue* context = BuildContextChainWalk(var);
yangguo@chromium.org355cfd12012-08-29 15:32:24 +000010046 HStoreContextSlot::Mode mode = IsLexicalVariableMode(var->mode())
ricow@chromium.org7ad65222011-12-19 12:13:11 +000010047 ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010048 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +000010049 new(zone()) HStoreContextSlot(context, var->index(), mode, after);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010050 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +000010051 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010052 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +000010053 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010054 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +000010055 }
10056
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010057 case Variable::LOOKUP:
10058 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010059 }
10060
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010061 } else {
10062 // Argument of the count operation is a property.
10063 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010064 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010065
10066 if (prop->key()->IsPropertyName()) {
10067 // Named property.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010068 if (returns_original_input) Push(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010069
danno@chromium.org160a7b02011-04-18 15:51:38 +000010070 CHECK_ALIVE(VisitForValue(prop->obj()));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +000010071 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010072
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +000010073 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +000010074 Handle<Map> map;
10075 HInstruction* load;
erik.corry@gmail.com88767242012-08-08 14:43:45 +000010076 bool monomorphic = prop->IsMonomorphic();
10077 if (monomorphic) {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +000010078 map = prop->GetReceiverTypes()->first();
erik.corry@gmail.com88767242012-08-08 14:43:45 +000010079 if (map->is_dictionary_map()) monomorphic = false;
10080 }
10081 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +000010082 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +000010083 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +000010084 if (LookupGetter(map, name, &getter, &holder)) {
10085 load = BuildCallGetter(object, map, getter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +000010086 } else {
10087 load = BuildLoadNamedMonomorphic(object, name, prop, map);
10088 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010089 } else {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +000010090 load = BuildLoadNamedGeneric(object, name, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010091 }
10092 PushAndAdd(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010093 if (load->HasObservableSideEffects()) {
10094 AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
10095 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010096
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010097 after = BuildIncrement(returns_original_input, expr);
10098 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010099
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010100 HInstruction* store;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010101 if (!monomorphic || map->is_observed()) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +000010102 // If we don't know the monomorphic type, do a generic store.
10103 CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, after));
10104 } else {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +000010105 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +000010106 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +000010107 if (LookupSetter(map, name, &setter, &holder)) {
10108 store = BuildCallSetter(object, after, map, setter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +000010109 } else {
10110 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object,
10111 name,
10112 after,
10113 map));
10114 }
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +000010115 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010116 AddInstruction(store);
10117
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010118 // Overwrite the receiver in the bailout environment with the result
10119 // of the operation, and the placeholder with the original value if
10120 // necessary.
10121 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010122 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010123 if (store->HasObservableSideEffects()) {
10124 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
10125 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010126
10127 } else {
10128 // Keyed property.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010129 if (returns_original_input) Push(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010130
danno@chromium.org160a7b02011-04-18 15:51:38 +000010131 CHECK_ALIVE(VisitForValue(prop->obj()));
10132 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010133 HValue* obj = environment()->ExpressionStackAt(1);
10134 HValue* key = environment()->ExpressionStackAt(0);
10135
whesse@chromium.org7b260152011-06-20 15:33:18 +000010136 bool has_side_effects = false;
10137 HValue* load = HandleKeyedElementAccess(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010138 obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
whesse@chromium.org7b260152011-06-20 15:33:18 +000010139 false, // is_store
10140 &has_side_effects);
10141 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010142 if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010143
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010144 after = BuildIncrement(returns_original_input, expr);
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010145 input = environment()->ExpressionStackAt(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010146
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010147 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +000010148 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
10149 RelocInfo::kNoPosition,
10150 true, // is_store
10151 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010152
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010153 // Drop the key and the original value from the bailout environment.
10154 // Overwrite the receiver with the result of the operation, and the
10155 // placeholder with the original value if necessary.
10156 Drop(2);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010157 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010158 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +000010159 ASSERT(has_side_effects); // Stores always have side effects.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010160 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010161 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010162 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +000010163
10164 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010165 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010166}
10167
10168
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010169HInstruction* HOptimizedGraphBuilder::BuildStringCharCodeAt(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010170 HValue* context,
10171 HValue* string,
10172 HValue* index) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010173 if (string->IsConstant() && index->IsConstant()) {
10174 HConstant* c_string = HConstant::cast(string);
10175 HConstant* c_index = HConstant::cast(index);
10176 if (c_string->HasStringValue() && c_index->HasNumberValue()) {
10177 int32_t i = c_index->NumberValueAsInteger32();
10178 Handle<String> s = c_string->StringValue();
10179 if (i < 0 || i >= s->length()) {
10180 return new(zone()) HConstant(OS::nan_value(), Representation::Double());
10181 }
10182 return new(zone()) HConstant(s->Get(i), Representation::Integer32());
10183 }
10184 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010185 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010186 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010187 HInstruction* length = HStringLength::New(zone(), string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000010188 AddInstruction(length);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +000010189 HInstruction* checked_index = AddBoundsCheck(index, length);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010190 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000010191}
10192
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010193// Checks if the given shift amounts have form: (sa) and (32 - sa).
10194static bool ShiftAmountsAllowReplaceByRotate(HValue* sa,
10195 HValue* const32_minus_sa) {
10196 if (!const32_minus_sa->IsSub()) return false;
10197 HSub* sub = HSub::cast(const32_minus_sa);
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +000010198 if (sa != sub->right()) return false;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010199 HValue* const32 = sub->left();
10200 if (!const32->IsConstant() ||
10201 HConstant::cast(const32)->Integer32Value() != 32) {
10202 return false;
10203 }
10204 return (sub->right() == sa);
10205}
10206
10207
10208// Checks if the left and the right are shift instructions with the oposite
10209// directions that can be replaced by one rotate right instruction or not.
10210// Returns the operand and the shift amount for the rotate instruction in the
10211// former case.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010212bool HOptimizedGraphBuilder::MatchRotateRight(HValue* left,
10213 HValue* right,
10214 HValue** operand,
10215 HValue** shift_amount) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010216 HShl* shl;
10217 HShr* shr;
10218 if (left->IsShl() && right->IsShr()) {
10219 shl = HShl::cast(left);
10220 shr = HShr::cast(right);
10221 } else if (left->IsShr() && right->IsShl()) {
10222 shl = HShl::cast(right);
10223 shr = HShr::cast(left);
10224 } else {
10225 return false;
10226 }
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +000010227 if (shl->left() != shr->left()) return false;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010228
10229 if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) &&
10230 !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) {
10231 return false;
10232 }
10233 *operand= shr->left();
10234 *shift_amount = shr->right();
10235 return true;
10236}
10237
10238
10239bool CanBeZero(HValue *right) {
10240 if (right->IsConstant()) {
10241 HConstant* right_const = HConstant::cast(right);
10242 if (right_const->HasInteger32Value() &&
10243 (right_const->Integer32Value() & 0x1f) != 0) {
10244 return false;
10245 }
10246 }
10247 return true;
10248}
10249
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000010250
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010251HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
10252 BinaryOperation* expr,
10253 HValue* left,
10254 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010255 HValue* context = environment()->LookupContext();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010256 TypeInfo left_info, right_info, result_info, combined_info;
10257 oracle()->BinaryType(expr, &left_info, &right_info, &result_info);
10258 Representation left_rep = ToRepresentation(left_info);
10259 Representation right_rep = ToRepresentation(right_info);
10260 Representation result_rep = ToRepresentation(result_info);
10261 if (left_info.IsUninitialized()) {
10262 // Can't have initialized one but not the other.
10263 ASSERT(right_info.IsUninitialized());
yangguo@chromium.org46a2a512013-01-18 16:29:40 +000010264 AddSoftDeoptimize();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010265 left_info = right_info = TypeInfo::Unknown();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010266 }
10267 HInstruction* instr = NULL;
10268 switch (expr->op()) {
10269 case Token::ADD:
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010270 if (left_info.IsString() && right_info.IsString()) {
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010271 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010272 AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010273 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010274 AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010275 instr = HStringAdd::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010276 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010277 instr = HAdd::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010278 }
10279 break;
10280 case Token::SUB:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010281 instr = HSub::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010282 break;
10283 case Token::MUL:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010284 instr = HMul::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010285 break;
10286 case Token::MOD:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010287 instr = HMod::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010288 break;
10289 case Token::DIV:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010290 instr = HDiv::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010291 break;
10292 case Token::BIT_XOR:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010293 case Token::BIT_AND:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010294 instr = HBitwise::New(zone(), expr->op(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010295 break;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010296 case Token::BIT_OR: {
10297 HValue* operand, *shift_amount;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010298 if (left_info.IsInteger32() && right_info.IsInteger32() &&
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010299 MatchRotateRight(left, right, &operand, &shift_amount)) {
10300 instr = new(zone()) HRor(context, operand, shift_amount);
10301 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010302 instr = HBitwise::New(zone(), expr->op(), context, left, right);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010303 }
10304 break;
10305 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010306 case Token::SAR:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010307 instr = HSar::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010308 break;
10309 case Token::SHR:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010310 instr = HShr::New(zone(), context, left, right);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010311 if (FLAG_opt_safe_uint32_operations && instr->IsShr() &&
10312 CanBeZero(right)) {
10313 graph()->RecordUint32Instruction(instr);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +000010314 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010315 break;
10316 case Token::SHL:
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010317 instr = HShl::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010318 break;
10319 default:
10320 UNREACHABLE();
10321 }
10322
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010323 if (instr->IsBinaryOperation()) {
10324 HBinaryOperation* binop = HBinaryOperation::cast(instr);
danno@chromium.orgca29dd82013-04-26 11:59:48 +000010325 binop->set_observed_input_representation(1, left_rep);
10326 binop->set_observed_input_representation(2, right_rep);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010327 binop->initialize_output_representation(result_rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010328 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010329 return instr;
10330}
10331
10332
10333// Check for the form (%_ClassOf(foo) === 'BarClass').
10334static bool IsClassOfTest(CompareOperation* expr) {
10335 if (expr->op() != Token::EQ_STRICT) return false;
10336 CallRuntime* call = expr->left()->AsCallRuntime();
10337 if (call == NULL) return false;
10338 Literal* literal = expr->right()->AsLiteral();
10339 if (literal == NULL) return false;
10340 if (!literal->handle()->IsString()) return false;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +000010341 if (!call->name()->IsOneByteEqualTo(STATIC_ASCII_VECTOR("_ClassOf"))) {
10342 return false;
10343 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010344 ASSERT(call->arguments()->length() == 1);
10345 return true;
10346}
10347
10348
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010349void HOptimizedGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010350 ASSERT(!HasStackOverflow());
10351 ASSERT(current_block() != NULL);
10352 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010353 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +000010354 case Token::COMMA:
10355 return VisitComma(expr);
10356 case Token::OR:
10357 case Token::AND:
10358 return VisitLogicalExpression(expr);
10359 default:
10360 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010361 }
10362}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010363
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010364
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010365void HOptimizedGraphBuilder::VisitComma(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010366 CHECK_ALIVE(VisitForEffect(expr->left()));
10367 // Visit the right subexpression in the same AST context as the entire
10368 // expression.
10369 Visit(expr->right());
10370}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010371
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010372
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010373void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +000010374 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010375 if (ast_context()->IsTest()) {
10376 TestContext* context = TestContext::cast(ast_context());
10377 // Translate left subexpression.
10378 HBasicBlock* eval_right = graph()->CreateBasicBlock();
10379 if (is_logical_and) {
10380 CHECK_BAILOUT(VisitForControl(expr->left(),
10381 eval_right,
10382 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010383 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010384 CHECK_BAILOUT(VisitForControl(expr->left(),
10385 context->if_true(),
10386 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010387 }
10388
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010389 // Translate right subexpression by visiting it in the same AST
10390 // context as the entire expression.
10391 if (eval_right->HasPredecessor()) {
10392 eval_right->SetJoinId(expr->RightId());
10393 set_current_block(eval_right);
10394 Visit(expr->right());
10395 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010396
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010397 } else if (ast_context()->IsValue()) {
10398 CHECK_ALIVE(VisitForValue(expr->left()));
10399 ASSERT(current_block() != NULL);
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010400 HValue* left_value = Top();
10401
10402 if (left_value->IsConstant()) {
10403 HConstant* left_constant = HConstant::cast(left_value);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000010404 if ((is_logical_and && left_constant->BooleanValue()) ||
10405 (!is_logical_and && !left_constant->BooleanValue())) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010406 Drop(1); // left_value.
10407 CHECK_BAILOUT(VisitForValue(expr->right()));
10408 }
10409 return ast_context()->ReturnValue(Pop());
10410 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010411
10412 // We need an extra block to maintain edge-split form.
10413 HBasicBlock* empty_block = graph()->CreateBasicBlock();
10414 HBasicBlock* eval_right = graph()->CreateBasicBlock();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010415 TypeFeedbackId test_id = expr->left()->test_id();
ricow@chromium.org2c99e282011-07-28 09:15:17 +000010416 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010417 HBranch* test = is_logical_and
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010418 ? new(zone()) HBranch(left_value, eval_right, empty_block, expected)
10419 : new(zone()) HBranch(left_value, empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010420 current_block()->Finish(test);
10421
10422 set_current_block(eval_right);
10423 Drop(1); // Value of the left subexpression.
10424 CHECK_BAILOUT(VisitForValue(expr->right()));
10425
10426 HBasicBlock* join_block =
10427 CreateJoin(empty_block, current_block(), expr->id());
10428 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010429 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010430
10431 } else {
10432 ASSERT(ast_context()->IsEffect());
10433 // In an effect context, we don't need the value of the left subexpression,
10434 // only its control flow and side effects. We need an extra block to
10435 // maintain edge-split form.
10436 HBasicBlock* empty_block = graph()->CreateBasicBlock();
10437 HBasicBlock* right_block = graph()->CreateBasicBlock();
10438 if (is_logical_and) {
10439 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
10440 } else {
10441 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
10442 }
10443
10444 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
10445 // actually two empty blocks (one here and one inserted by
10446 // TestContext::BuildBranch, and that they both have an HSimulate though the
10447 // second one is not a merge node, and that we really have no good AST ID to
10448 // put on that first HSimulate.
10449
10450 if (empty_block->HasPredecessor()) {
10451 empty_block->SetJoinId(expr->id());
10452 } else {
10453 empty_block = NULL;
10454 }
10455
10456 if (right_block->HasPredecessor()) {
10457 right_block->SetJoinId(expr->RightId());
10458 set_current_block(right_block);
10459 CHECK_BAILOUT(VisitForEffect(expr->right()));
10460 right_block = current_block();
10461 } else {
10462 right_block = NULL;
10463 }
10464
10465 HBasicBlock* join_block =
10466 CreateJoin(empty_block, right_block, expr->id());
10467 set_current_block(join_block);
10468 // We did not materialize any value in the predecessor environments,
10469 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010470 }
10471}
10472
10473
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010474void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010475 CHECK_ALIVE(VisitForValue(expr->left()));
10476 CHECK_ALIVE(VisitForValue(expr->right()));
10477 HValue* right = Pop();
10478 HValue* left = Pop();
10479 HInstruction* instr = BuildBinaryOperation(expr, left, right);
10480 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010481 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010482}
10483
10484
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010485Representation HOptimizedGraphBuilder::ToRepresentation(TypeInfo info) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010486 if (info.IsUninitialized()) return Representation::None();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010487 if (info.IsSmi()) return Representation::Integer32();
10488 if (info.IsInteger32()) return Representation::Integer32();
10489 if (info.IsDouble()) return Representation::Double();
10490 if (info.IsNumber()) return Representation::Double();
10491 return Representation::Tagged();
10492}
10493
10494
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010495void HOptimizedGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
10496 HTypeof* typeof_expr,
10497 Handle<String> check) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010498 // Note: The HTypeof itself is removed during canonicalization, if possible.
10499 HValue* value = typeof_expr->value();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000010500 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
10501 instr->set_position(expr->position());
10502 return ast_context()->ReturnControl(instr, expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +000010503}
10504
10505
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010506static bool MatchLiteralCompareNil(HValue* left,
10507 Token::Value op,
10508 HValue* right,
10509 Handle<Object> nil,
10510 HValue** expr) {
10511 if (left->IsConstant() &&
10512 HConstant::cast(left)->handle().is_identical_to(nil) &&
10513 Token::IsEqualityOp(op)) {
10514 *expr = right;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000010515 return true;
10516 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000010517 return false;
ager@chromium.org04921a82011-06-27 13:21:41 +000010518}
10519
10520
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010521static bool MatchLiteralCompareTypeof(HValue* left,
10522 Token::Value op,
10523 HValue* right,
10524 HTypeof** typeof_expr,
10525 Handle<String>* check) {
10526 if (left->IsTypeof() &&
10527 Token::IsEqualityOp(op) &&
10528 right->IsConstant() &&
rossberg@chromium.org657d53b2012-07-12 11:06:03 +000010529 HConstant::cast(right)->handle()->IsString()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010530 *typeof_expr = HTypeof::cast(left);
10531 *check = Handle<String>::cast(HConstant::cast(right)->handle());
10532 return true;
10533 }
10534 return false;
10535}
10536
10537
10538static bool IsLiteralCompareTypeof(HValue* left,
10539 Token::Value op,
10540 HValue* right,
10541 HTypeof** typeof_expr,
10542 Handle<String>* check) {
10543 return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) ||
10544 MatchLiteralCompareTypeof(right, op, left, typeof_expr, check);
10545}
10546
10547
10548static bool IsLiteralCompareNil(HValue* left,
10549 Token::Value op,
10550 HValue* right,
10551 Handle<Object> nil,
10552 HValue** expr) {
10553 return MatchLiteralCompareNil(left, op, right, nil, expr) ||
10554 MatchLiteralCompareNil(right, op, left, nil, expr);
10555}
10556
10557
rossberg@chromium.org2c067b12012-03-19 11:01:52 +000010558static bool IsLiteralCompareBool(HValue* left,
10559 Token::Value op,
10560 HValue* right) {
10561 return op == Token::EQ_STRICT &&
10562 ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) ||
10563 (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean()));
10564}
10565
10566
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010567void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010568 ASSERT(!HasStackOverflow());
10569 ASSERT(current_block() != NULL);
10570 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010571 if (IsClassOfTest(expr)) {
10572 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010573 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000010574 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010575 HValue* value = Pop();
10576 Literal* literal = expr->right()->AsLiteral();
10577 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010578 HClassOfTestAndBranch* instr =
10579 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010580 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010581 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010582 }
10583
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010584 TypeInfo left_type, right_type, overall_type_info;
10585 oracle()->CompareType(expr, &left_type, &right_type, &overall_type_info);
10586 Representation combined_rep = ToRepresentation(overall_type_info);
10587 Representation left_rep = ToRepresentation(left_type);
10588 Representation right_rep = ToRepresentation(right_type);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010589 // Check if this expression was ever executed according to type feedback.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010590 // Note that for the special typeof/null/undefined cases we get unknown here.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010591 if (overall_type_info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +000010592 AddSoftDeoptimize();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010593 overall_type_info = left_type = right_type = TypeInfo::Unknown();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010594 }
10595
danno@chromium.org160a7b02011-04-18 15:51:38 +000010596 CHECK_ALIVE(VisitForValue(expr->left()));
10597 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010598
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010599 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010600 HValue* right = Pop();
10601 HValue* left = Pop();
10602 Token::Value op = expr->op();
10603
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010604 HTypeof* typeof_expr = NULL;
10605 Handle<String> check;
10606 if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) {
10607 return HandleLiteralCompareTypeof(expr, typeof_expr, check);
10608 }
10609 HValue* sub_expr = NULL;
ulan@chromium.org750145a2013-03-07 15:14:13 +000010610 Factory* f = isolate()->factory();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010611 if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) {
10612 return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
10613 }
10614 if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) {
10615 return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
10616 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +000010617 if (IsLiteralCompareBool(left, op, right)) {
10618 HCompareObjectEqAndBranch* result =
10619 new(zone()) HCompareObjectEqAndBranch(left, right);
10620 result->set_position(expr->position());
10621 return ast_context()->ReturnControl(result, expr->id());
10622 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010623
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010624 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010625 // Check to see if the rhs of the instanceof is a global function not
10626 // residing in new space. If it is we assume that the function will stay the
10627 // same.
10628 Handle<JSFunction> target = Handle<JSFunction>::null();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010629 VariableProxy* proxy = expr->right()->AsVariableProxy();
10630 bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010631 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +000010632 info()->has_global_object() &&
10633 !info()->global_object()->IsAccessCheckNeeded()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000010634 Handle<String> name = proxy->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +000010635 Handle<GlobalObject> global(info()->global_object());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000010636 LookupResult lookup(isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010637 global->Lookup(*name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +000010638 if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010639 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
10640 // If the function is in new space we assume it's more likely to
10641 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010642 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010643 target = candidate;
10644 }
10645 }
10646 }
10647
10648 // If the target is not null we have found a known global function that is
10649 // assumed to stay the same for this instanceof.
10650 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010651 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
10652 result->set_position(expr->position());
10653 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010654 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010655 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010656 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010657 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010658 result->set_position(expr->position());
10659 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +000010660 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010661 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010662 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010663 result->set_position(expr->position());
10664 return ast_context()->ReturnInstruction(result, expr->id());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010665 } else if (overall_type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010666 switch (op) {
10667 case Token::EQ:
10668 case Token::EQ_STRICT: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +000010669 // Can we get away with map check and not instance type check?
10670 Handle<Map> map = oracle()->GetCompareMap(expr);
10671 if (!map.is_null()) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +000010672 AddCheckMapsWithTransitions(left, map);
10673 AddCheckMapsWithTransitions(right, map);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +000010674 HCompareObjectEqAndBranch* result =
10675 new(zone()) HCompareObjectEqAndBranch(left, right);
10676 result->set_position(expr->position());
10677 return ast_context()->ReturnControl(result, expr->id());
10678 } else {
10679 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010680 AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +000010681 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010682 AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +000010683 HCompareObjectEqAndBranch* result =
10684 new(zone()) HCompareObjectEqAndBranch(left, right);
10685 result->set_position(expr->position());
10686 return ast_context()->ReturnControl(result, expr->id());
10687 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010688 }
10689 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +000010690 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010691 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000010692 } else if (overall_type_info.IsInternalizedString() &&
10693 Token::IsEqualityOp(op)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +000010694 AddInstruction(new(zone()) HCheckNonSmi(left));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000010695 AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone()));
lrn@chromium.orgac2828d2011-06-23 06:29:21 +000010696 AddInstruction(new(zone()) HCheckNonSmi(right));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000010697 AddInstruction(HCheckInstanceType::NewIsInternalizedString(right, zone()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010698 HCompareObjectEqAndBranch* result =
10699 new(zone()) HCompareObjectEqAndBranch(left, right);
10700 result->set_position(expr->position());
10701 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010702 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010703 if (combined_rep.IsTagged() || combined_rep.IsNone()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000010704 HCompareGeneric* result =
10705 new(zone()) HCompareGeneric(context, left, right, op);
danno@chromium.orgca29dd82013-04-26 11:59:48 +000010706 result->set_observed_input_representation(1, left_rep);
10707 result->set_observed_input_representation(2, right_rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010708 result->set_position(expr->position());
10709 return ast_context()->ReturnInstruction(result, expr->id());
10710 } else {
10711 HCompareIDAndBranch* result =
10712 new(zone()) HCompareIDAndBranch(left, right, op);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000010713 result->set_observed_input_representation(left_rep, right_rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010714 result->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010715 return ast_context()->ReturnControl(result, expr->id());
10716 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010717 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010718}
10719
10720
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010721void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
10722 HValue* value,
10723 NilValue nil) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010724 ASSERT(!HasStackOverflow());
10725 ASSERT(current_block() != NULL);
10726 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000010727 EqualityKind kind =
10728 expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
danno@chromium.orgca29dd82013-04-26 11:59:48 +000010729 HIfContinuation continuation;
10730 TypeFeedbackId id = expr->CompareOperationFeedbackId();
10731 CompareNilICStub::Types types;
10732 if (kind == kStrictEquality) {
10733 if (nil == kNullValue) {
10734 types = CompareNilICStub::kCompareAgainstNull;
10735 } else {
10736 types = CompareNilICStub::kCompareAgainstUndefined;
10737 }
10738 } else {
10739 types = static_cast<CompareNilICStub::Types>(
10740 oracle()->CompareNilTypes(id));
10741 if (types == 0) types = CompareNilICStub::kFullCompare;
10742 }
10743 Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id));
10744 BuildCompareNil(value, kind, types, map_handle,
10745 expr->position(), &continuation);
10746 return ast_context()->ReturnContinuation(&continuation, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010747}
10748
10749
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010750HInstruction* HOptimizedGraphBuilder::BuildThisFunction() {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +000010751 // If we share optimized code between different closures, the
10752 // this-function is not a constant, except inside an inlined body.
10753 if (function_state()->outer() != NULL) {
10754 return new(zone()) HConstant(
10755 function_state()->compilation_info()->closure(),
10756 Representation::Tagged());
10757 } else {
10758 return new(zone()) HThisFunction;
10759 }
10760}
10761
10762
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010763HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
10764 HValue* context,
10765 Handle<JSObject> boilerplate_object,
10766 Handle<JSObject> original_boilerplate_object,
10767 int data_size,
10768 int pointer_size,
10769 AllocationSiteMode mode) {
10770 Zone* zone = this->zone();
10771 int total_size = data_size + pointer_size;
10772
10773 NoObservableSideEffectsScope no_effects(this);
10774
10775 HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE;
10776 // TODO(hpayer): add support for old data space
10777 if (FLAG_pretenure_literals &&
10778 isolate()->heap()->ShouldGloballyPretenure() &&
10779 data_size == 0) {
10780 flags = static_cast<HAllocate::Flags>(
10781 flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE);
10782 }
10783
10784 HValue* size_in_bytes =
10785 AddInstruction(new(zone) HConstant(total_size,
10786 Representation::Integer32()));
10787 HInstruction* result =
10788 AddInstruction(new(zone) HAllocate(context,
10789 size_in_bytes,
10790 HType::JSObject(),
10791 flags));
10792 int offset = 0;
10793 BuildEmitDeepCopy(boilerplate_object, original_boilerplate_object, result,
10794 &offset, mode);
10795 return result;
10796}
10797
10798
10799void HOptimizedGraphBuilder::BuildEmitDeepCopy(
10800 Handle<JSObject> boilerplate_object,
10801 Handle<JSObject> original_boilerplate_object,
10802 HInstruction* target,
10803 int* offset,
10804 AllocationSiteMode mode) {
10805 Zone* zone = this->zone();
10806 Factory* factory = isolate()->factory();
10807
10808 HInstruction* original_boilerplate = AddInstruction(new(zone) HConstant(
10809 original_boilerplate_object, Representation::Tagged()));
10810
10811 bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE &&
10812 boilerplate_object->map()->CanTrackAllocationSite();
10813
10814 // Only elements backing stores for non-COW arrays need to be copied.
10815 Handle<FixedArrayBase> elements(boilerplate_object->elements());
10816 Handle<FixedArrayBase> original_elements(
10817 original_boilerplate_object->elements());
10818 ElementsKind kind = boilerplate_object->map()->elements_kind();
10819
10820 // Increase the offset so that subsequent objects end up right after
10821 // this object and its backing store.
10822 int object_offset = *offset;
10823 int object_size = boilerplate_object->map()->instance_size();
10824 int elements_size = (elements->length() > 0 &&
10825 elements->map() != isolate()->heap()->fixed_cow_array_map()) ?
10826 elements->Size() : 0;
10827 int elements_offset = *offset + object_size;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010828 if (create_allocation_site_info) {
10829 elements_offset += AllocationSiteInfo::kSize;
10830 *offset += AllocationSiteInfo::kSize;
10831 }
10832
10833 *offset += object_size + elements_size;
10834
10835 HValue* object_elements = BuildCopyObjectHeader(boilerplate_object, target,
10836 object_offset, elements_offset, elements_size);
10837
10838 // Copy in-object properties.
10839 HValue* object_properties =
10840 AddInstruction(new(zone) HInnerAllocatedObject(target, object_offset));
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010841
10842 Handle<DescriptorArray> descriptors(
10843 boilerplate_object->map()->instance_descriptors());
10844 int limit = boilerplate_object->map()->NumberOfOwnDescriptors();
10845
10846 for (int i = 0; i < limit; i++) {
10847 PropertyDetails details = descriptors->GetDetails(i);
10848 if (details.type() != FIELD) continue;
10849 int index = descriptors->GetFieldIndex(i);
10850 int property_offset = boilerplate_object->GetInObjectPropertyOffset(index);
10851 Handle<Name> name(descriptors->GetKey(i));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010852 Handle<Object> value =
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010853 Handle<Object>(boilerplate_object->InObjectPropertyAt(index),
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010854 isolate());
10855 if (value->IsJSObject()) {
10856 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
10857 Handle<JSObject> original_value_object = Handle<JSObject>::cast(
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010858 Handle<Object>(original_boilerplate_object->InObjectPropertyAt(index),
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010859 isolate()));
10860 HInstruction* value_instruction =
10861 AddInstruction(new(zone) HInnerAllocatedObject(target, *offset));
10862 AddInstruction(new(zone) HStoreNamedField(
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010863 object_properties, name, value_instruction, true,
10864 Representation::Tagged(), property_offset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010865 BuildEmitDeepCopy(value_object, original_value_object, target,
10866 offset, DONT_TRACK_ALLOCATION_SITE);
10867 } else {
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010868 Representation representation = details.representation();
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010869 HInstruction* value_instruction = AddInstruction(new(zone) HConstant(
10870 value, Representation::Tagged()));
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010871 if (representation.IsDouble()) {
10872 HInstruction* double_box =
10873 AddInstruction(new(zone) HInnerAllocatedObject(target, *offset));
10874 BuildStoreMap(double_box, factory->heap_number_map());
10875 AddInstruction(new(zone) HStoreNamedField(
10876 double_box, name, value_instruction, true,
10877 Representation::Double(), HeapNumber::kValueOffset));
10878 value_instruction = double_box;
10879 *offset += HeapNumber::kSize;
10880 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010881 AddInstruction(new(zone) HStoreNamedField(
ulan@chromium.org57ff8812013-05-10 08:16:55 +000010882 object_properties, name, value_instruction, true,
10883 Representation::Tagged(), property_offset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010884 }
10885 }
10886
10887 // Build Allocation Site Info if desired
10888 if (create_allocation_site_info) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +000010889 BuildCreateAllocationSiteInfo(target, JSArray::kSize, original_boilerplate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010890 }
10891
10892 if (object_elements != NULL) {
10893 HInstruction* boilerplate_elements = AddInstruction(new(zone) HConstant(
10894 elements, Representation::Tagged()));
10895
10896 int elements_length = elements->length();
10897 HValue* object_elements_length =
10898 AddInstruction(new(zone) HConstant(
10899 elements_length, Representation::Integer32()));
10900
10901 BuildInitializeElements(object_elements, kind, object_elements_length);
10902
10903 // Copy elements backing store content.
10904 if (elements->IsFixedDoubleArray()) {
10905 for (int i = 0; i < elements_length; i++) {
10906 HValue* key_constant =
10907 AddInstruction(new(zone) HConstant(i, Representation::Integer32()));
10908 HInstruction* value_instruction =
10909 AddInstruction(new(zone) HLoadKeyed(
10910 boilerplate_elements, key_constant, NULL, kind));
10911 AddInstruction(new(zone) HStoreKeyed(
10912 object_elements, key_constant, value_instruction, kind));
10913 }
10914 } else if (elements->IsFixedArray()) {
10915 Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
10916 Handle<FixedArray> original_fast_elements =
10917 Handle<FixedArray>::cast(original_elements);
10918 for (int i = 0; i < elements_length; i++) {
10919 Handle<Object> value(fast_elements->get(i), isolate());
10920 HValue* key_constant =
10921 AddInstruction(new(zone) HConstant(i, Representation::Integer32()));
10922 if (value->IsJSObject()) {
10923 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
10924 Handle<JSObject> original_value_object = Handle<JSObject>::cast(
10925 Handle<Object>(original_fast_elements->get(i), isolate()));
10926 HInstruction* value_instruction =
10927 AddInstruction(new(zone) HInnerAllocatedObject(target, *offset));
10928 AddInstruction(new(zone) HStoreKeyed(
10929 object_elements, key_constant, value_instruction, kind));
10930 BuildEmitDeepCopy(value_object, original_value_object, target,
10931 offset, DONT_TRACK_ALLOCATION_SITE);
10932 } else {
10933 HInstruction* value_instruction =
10934 AddInstruction(new(zone) HLoadKeyed(
10935 boilerplate_elements, key_constant, NULL, kind));
10936 AddInstruction(new(zone) HStoreKeyed(
10937 object_elements, key_constant, value_instruction, kind));
10938 }
10939 }
10940 } else {
10941 UNREACHABLE();
10942 }
10943 }
10944}
10945
10946
10947HValue* HOptimizedGraphBuilder::BuildCopyObjectHeader(
10948 Handle<JSObject> boilerplate_object,
10949 HInstruction* target,
10950 int object_offset,
10951 int elements_offset,
10952 int elements_size) {
10953 ASSERT(boilerplate_object->properties()->length() == 0);
10954 Zone* zone = this->zone();
10955 Factory* factory = isolate()->factory();
10956 HValue* result = NULL;
10957
10958 HValue* object_header =
10959 AddInstruction(new(zone) HInnerAllocatedObject(target, object_offset));
10960 Handle<Map> boilerplate_object_map(boilerplate_object->map());
10961 BuildStoreMap(object_header, boilerplate_object_map);
10962
10963 HInstruction* elements;
10964 if (elements_size == 0) {
10965 Handle<Object> elements_field =
10966 Handle<Object>(boilerplate_object->elements(), isolate());
10967 elements = AddInstruction(new(zone) HConstant(
10968 elements_field, Representation::Tagged()));
10969 } else {
10970 elements = AddInstruction(new(zone) HInnerAllocatedObject(
10971 target, elements_offset));
10972 result = elements;
10973 }
10974 HInstruction* elements_store = AddInstruction(new(zone) HStoreNamedField(
10975 object_header,
10976 factory->elements_field_string(),
10977 elements,
danno@chromium.orgf005df62013-04-30 16:36:45 +000010978 true, Representation::Tagged(), JSObject::kElementsOffset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010979 elements_store->SetGVNFlag(kChangesElementsPointer);
10980
10981 Handle<Object> properties_field =
10982 Handle<Object>(boilerplate_object->properties(), isolate());
10983 ASSERT(*properties_field == isolate()->heap()->empty_fixed_array());
10984 HInstruction* properties = AddInstruction(new(zone) HConstant(
10985 properties_field, Representation::None()));
10986 AddInstruction(new(zone) HStoreNamedField(object_header,
10987 factory->empty_string(),
danno@chromium.orgf005df62013-04-30 16:36:45 +000010988 properties, true,
10989 Representation::Tagged(),
10990 JSObject::kPropertiesOffset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000010991
10992 if (boilerplate_object->IsJSArray()) {
10993 Handle<JSArray> boilerplate_array =
10994 Handle<JSArray>::cast(boilerplate_object);
10995 Handle<Object> length_field =
10996 Handle<Object>(boilerplate_array->length(), isolate());
10997 HInstruction* length = AddInstruction(new(zone) HConstant(
10998 length_field, Representation::None()));
danno@chromium.orgf005df62013-04-30 16:36:45 +000010999 ASSERT(boilerplate_array->length()->IsSmi());
11000 Representation representation =
11001 IsFastElementsKind(boilerplate_array->GetElementsKind())
11002 ? Representation::Smi() : Representation::Tagged();
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000011003 HInstruction* length_store = AddInstruction(new(zone) HStoreNamedField(
11004 object_header,
11005 factory->length_field_string(),
11006 length,
danno@chromium.orgf005df62013-04-30 16:36:45 +000011007 true, representation, JSArray::kLengthOffset));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000011008 length_store->SetGVNFlag(kChangesArrayLengths);
11009 }
11010
11011 return result;
11012}
11013
11014
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011015void HOptimizedGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011016 ASSERT(!HasStackOverflow());
11017 ASSERT(current_block() != NULL);
11018 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +000011019 HInstruction* instr = BuildThisFunction();
11020 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011021}
11022
11023
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011024void HOptimizedGraphBuilder::VisitDeclarations(
11025 ZoneList<Declaration*>* declarations) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011026 ASSERT(globals_.is_empty());
11027 AstVisitor::VisitDeclarations(declarations);
11028 if (!globals_.is_empty()) {
yangguo@chromium.org56454712012-02-16 15:33:53 +000011029 Handle<FixedArray> array =
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011030 isolate()->factory()->NewFixedArray(globals_.length(), TENURED);
11031 for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i));
yangguo@chromium.org56454712012-02-16 15:33:53 +000011032 int flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
11033 DeclareGlobalsNativeFlag::encode(info()->is_native()) |
11034 DeclareGlobalsLanguageMode::encode(info()->language_mode());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011035 HInstruction* result = new(zone()) HDeclareGlobals(
11036 environment()->LookupContext(), array, flags);
yangguo@chromium.org56454712012-02-16 15:33:53 +000011037 AddInstruction(result);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011038 globals_.Clear();
yangguo@chromium.org56454712012-02-16 15:33:53 +000011039 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +000011040}
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000011041
fschneider@chromium.org1805e212011-09-05 10:49:12 +000011042
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011043void HOptimizedGraphBuilder::VisitVariableDeclaration(
11044 VariableDeclaration* declaration) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011045 VariableProxy* proxy = declaration->proxy();
11046 VariableMode mode = declaration->mode();
11047 Variable* variable = proxy->var();
11048 bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
11049 switch (variable->location()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000011050 case Variable::UNALLOCATED:
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011051 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011052 globals_.Add(variable->binding_needs_init()
11053 ? isolate()->factory()->the_hole_value()
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011054 : isolate()->factory()->undefined_value(), zone());
yangguo@chromium.org56454712012-02-16 15:33:53 +000011055 return;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000011056 case Variable::PARAMETER:
11057 case Variable::LOCAL:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011058 if (hole_init) {
11059 HValue* value = graph()->GetConstantHole();
11060 environment()->Bind(variable, value);
11061 }
11062 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000011063 case Variable::CONTEXT:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011064 if (hole_init) {
11065 HValue* value = graph()->GetConstantHole();
11066 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011067 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011068 context, variable->index(), HStoreContextSlot::kNoCheck, value);
11069 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000011070 if (store->HasObservableSideEffects()) {
11071 AddSimulate(proxy->id(), REMOVABLE_SIMULATE);
11072 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +000011073 }
11074 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +000011075 case Variable::LOOKUP:
fschneider@chromium.org1805e212011-09-05 10:49:12 +000011076 return Bailout("unsupported lookup slot in declaration");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000011077 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011078}
11079
11080
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011081void HOptimizedGraphBuilder::VisitFunctionDeclaration(
11082 FunctionDeclaration* declaration) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011083 VariableProxy* proxy = declaration->proxy();
11084 Variable* variable = proxy->var();
11085 switch (variable->location()) {
11086 case Variable::UNALLOCATED: {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011087 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011088 Handle<SharedFunctionInfo> function =
11089 Compiler::BuildFunctionInfo(declaration->fun(), info()->script());
11090 // Check for stack-overflow exception.
11091 if (function.is_null()) return SetStackOverflow();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011092 globals_.Add(function, zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011093 return;
11094 }
11095 case Variable::PARAMETER:
11096 case Variable::LOCAL: {
11097 CHECK_ALIVE(VisitForValue(declaration->fun()));
11098 HValue* value = Pop();
11099 environment()->Bind(variable, value);
11100 break;
11101 }
11102 case Variable::CONTEXT: {
11103 CHECK_ALIVE(VisitForValue(declaration->fun()));
11104 HValue* value = Pop();
11105 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011106 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011107 context, variable->index(), HStoreContextSlot::kNoCheck, value);
11108 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000011109 if (store->HasObservableSideEffects()) {
11110 AddSimulate(proxy->id(), REMOVABLE_SIMULATE);
11111 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011112 break;
11113 }
11114 case Variable::LOOKUP:
11115 return Bailout("unsupported lookup slot in declaration");
11116 }
11117}
11118
11119
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011120void HOptimizedGraphBuilder::VisitModuleDeclaration(
11121 ModuleDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +000011122 UNREACHABLE();
11123}
11124
11125
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011126void HOptimizedGraphBuilder::VisitImportDeclaration(
11127 ImportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +000011128 UNREACHABLE();
11129}
11130
11131
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011132void HOptimizedGraphBuilder::VisitExportDeclaration(
11133 ExportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +000011134 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000011135}
11136
11137
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011138void HOptimizedGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011139 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000011140}
11141
11142
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011143void HOptimizedGraphBuilder::VisitModuleVariable(ModuleVariable* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011144 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000011145}
11146
11147
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011148void HOptimizedGraphBuilder::VisitModulePath(ModulePath* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011149 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000011150}
11151
11152
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011153void HOptimizedGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +000011154 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000011155}
11156
11157
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011158void HOptimizedGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000011159 UNREACHABLE();
11160}
11161
11162
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011163// Generators for inline runtime functions.
11164// Support for types.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011165void HOptimizedGraphBuilder::GenerateIsSmi(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011166 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011167 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011168 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011169 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
11170 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011171}
11172
11173
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011174void HOptimizedGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011175 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011176 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011177 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011178 HHasInstanceTypeAndBranch* result =
11179 new(zone()) HHasInstanceTypeAndBranch(value,
11180 FIRST_SPEC_OBJECT_TYPE,
11181 LAST_SPEC_OBJECT_TYPE);
11182 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011183}
11184
11185
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011186void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011187 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011188 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011189 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011190 HHasInstanceTypeAndBranch* result =
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000011191 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011192 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011193}
11194
11195
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011196void HOptimizedGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011197 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011198 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011199 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011200 HHasCachedArrayIndexAndBranch* result =
11201 new(zone()) HHasCachedArrayIndexAndBranch(value);
11202 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011203}
11204
11205
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011206void HOptimizedGraphBuilder::GenerateIsArray(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011207 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011208 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011209 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011210 HHasInstanceTypeAndBranch* result =
11211 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
11212 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011213}
11214
11215
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011216void HOptimizedGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011217 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011218 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011219 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011220 HHasInstanceTypeAndBranch* result =
11221 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
11222 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011223}
11224
11225
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011226void HOptimizedGraphBuilder::GenerateIsObject(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011227 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011228 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011229 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011230 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
11231 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011232}
11233
11234
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011235void HOptimizedGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011236 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011237}
11238
11239
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011240void HOptimizedGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000011241 ASSERT(call->arguments()->length() == 1);
11242 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11243 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011244 HIsUndetectableAndBranch* result =
11245 new(zone()) HIsUndetectableAndBranch(value);
11246 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011247}
11248
11249
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011250void HOptimizedGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011251 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011252 return Bailout(
11253 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011254}
11255
11256
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +000011257// Support for construct call checks.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011258void HOptimizedGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011259 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011260 if (function_state()->outer() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +000011261 // We are generating graph for inlined function.
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011262 HValue* value = function_state()->inlining_kind() == CONSTRUCT_CALL_RETURN
ulan@chromium.org967e2702012-02-28 09:49:15 +000011263 ? graph()->GetConstantTrue()
11264 : graph()->GetConstantFalse();
11265 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011266 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011267 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
11268 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011269 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011270}
11271
11272
11273// Support for arguments.length and arguments[?].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011274void HOptimizedGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +000011275 // Our implementation of arguments (based on this stack frame or an
11276 // adapter below it) does not work for inlined functions. This runtime
11277 // function is blacklisted by AstNode::IsInlineable.
11278 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011279 ASSERT(call->arguments()->length() == 0);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +000011280 HInstruction* elements = AddInstruction(
11281 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011282 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011283 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011284}
11285
11286
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011287void HOptimizedGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +000011288 // Our implementation of arguments (based on this stack frame or an
11289 // adapter below it) does not work for inlined functions. This runtime
11290 // function is blacklisted by AstNode::IsInlineable.
11291 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011292 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011293 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011294 HValue* index = Pop();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +000011295 HInstruction* elements = AddInstruction(
11296 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011297 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +000011298 HInstruction* checked_index = AddBoundsCheck(index, length);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011299 HAccessArgumentsAt* result =
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +000011300 new(zone()) HAccessArgumentsAt(elements, length, checked_index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011301 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011302}
11303
11304
11305// Support for accessing the class and value fields of an object.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011306void HOptimizedGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011307 // The special form detected by IsClassOfTest is detected before we get here
11308 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +000011309 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011310}
11311
11312
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011313void HOptimizedGraphBuilder::GenerateValueOf(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011314 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011315 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011316 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011317 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011318 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011319}
11320
11321
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011322void HOptimizedGraphBuilder::GenerateDateField(CallRuntime* call) {
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +000011323 ASSERT(call->arguments()->length() == 2);
11324 ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
11325 Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
11326 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11327 HValue* date = Pop();
11328 HDateField* result = new(zone()) HDateField(date, index);
11329 return ast_context()->ReturnInstruction(result, call->id());
11330}
11331
11332
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011333void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +000011334 CallRuntime* call) {
11335 ASSERT(call->arguments()->length() == 3);
11336 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11337 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
11338 CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
11339 HValue* value = Pop();
11340 HValue* index = Pop();
11341 HValue* string = Pop();
11342 HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
11343 String::ONE_BYTE_ENCODING, string, index, value);
11344 return ast_context()->ReturnInstruction(result, call->id());
11345}
11346
11347
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011348void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +000011349 CallRuntime* call) {
11350 ASSERT(call->arguments()->length() == 3);
11351 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11352 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
11353 CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
11354 HValue* value = Pop();
11355 HValue* index = Pop();
11356 HValue* string = Pop();
11357 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011358 HInstruction* char_code = BuildStringCharCodeAt(context, string, index);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +000011359 AddInstruction(char_code);
11360 HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
11361 String::TWO_BYTE_ENCODING, string, index, value);
11362 return ast_context()->ReturnInstruction(result, call->id());
11363}
11364
11365
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011366void HOptimizedGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org2c456792011-11-11 12:00:53 +000011367 ASSERT(call->arguments()->length() == 2);
11368 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11369 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
11370 HValue* value = Pop();
11371 HValue* object = Pop();
11372 // Check if object is a not a smi.
11373 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object);
11374 HBasicBlock* if_smi = graph()->CreateBasicBlock();
11375 HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
11376 HBasicBlock* join = graph()->CreateBasicBlock();
11377 smicheck->SetSuccessorAt(0, if_smi);
11378 smicheck->SetSuccessorAt(1, if_heap_object);
11379 current_block()->Finish(smicheck);
11380 if_smi->Goto(join);
11381
11382 // Check if object is a JSValue.
11383 set_current_block(if_heap_object);
11384 HHasInstanceTypeAndBranch* typecheck =
11385 new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE);
11386 HBasicBlock* if_js_value = graph()->CreateBasicBlock();
11387 HBasicBlock* not_js_value = graph()->CreateBasicBlock();
11388 typecheck->SetSuccessorAt(0, if_js_value);
11389 typecheck->SetSuccessorAt(1, not_js_value);
11390 current_block()->Finish(typecheck);
11391 not_js_value->Goto(join);
11392
11393 // Create in-object property store to kValueOffset.
11394 set_current_block(if_js_value);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000011395 Handle<String> name = isolate()->factory()->undefined_string();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011396 AddInstruction(new(zone()) HStoreNamedField(object,
11397 name,
11398 value,
11399 true, // in-object store.
danno@chromium.orgf005df62013-04-30 16:36:45 +000011400 Representation::Tagged(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011401 JSValue::kValueOffset));
danno@chromium.org2c456792011-11-11 12:00:53 +000011402 if_js_value->Goto(join);
11403 join->SetJoinId(call->id());
11404 set_current_block(join);
11405 return ast_context()->ReturnValue(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011406}
11407
11408
11409// Fast support for charCodeAt(n).
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011410void HOptimizedGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011411 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011412 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11413 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000011414 HValue* index = Pop();
11415 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000011416 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011417 HInstruction* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011418 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011419}
11420
11421
11422// Fast support for string.charAt(n) and string[n].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011423void HOptimizedGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000011424 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011425 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000011426 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000011427 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011428 HInstruction* result = HStringCharFromCode::New(zone(), context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011429 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011430}
11431
11432
11433// Fast support for string.charAt(n) and string[n].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011434void HOptimizedGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000011435 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011436 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11437 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000011438 HValue* index = Pop();
11439 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +000011440 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011441 HInstruction* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000011442 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011443 HInstruction* result = HStringCharFromCode::New(zone(), context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011444 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011445}
11446
11447
11448// Fast support for object equality testing.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011449void HOptimizedGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011450 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011451 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11452 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011453 HValue* right = Pop();
11454 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011455 HCompareObjectEqAndBranch* result =
11456 new(zone()) HCompareObjectEqAndBranch(left, right);
11457 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011458}
11459
11460
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011461void HOptimizedGraphBuilder::GenerateLog(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011462 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011463 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011464}
11465
11466
11467// Fast support for Math.random().
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011468void HOptimizedGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +000011469 HValue* context = environment()->LookupContext();
11470 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
11471 AddInstruction(global_object);
11472 HRandom* result = new(zone()) HRandom(global_object);
11473 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011474}
11475
11476
11477// Fast support for StringAdd.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011478void HOptimizedGraphBuilder::GenerateStringAdd(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011479 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011480 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011481 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011482 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011483 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011484 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011485}
11486
11487
11488// Fast support for SubString.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011489void HOptimizedGraphBuilder::GenerateSubString(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011490 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011491 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011492 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011493 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011494 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011495 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011496}
11497
11498
11499// Fast support for StringCompare.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011500void HOptimizedGraphBuilder::GenerateStringCompare(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011501 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011502 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011503 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011504 HCallStub* result =
11505 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011506 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011507 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011508}
11509
11510
11511// Support for direct calls from JavaScript to native RegExp code.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011512void HOptimizedGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011513 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011514 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011515 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011516 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011517 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011518 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011519}
11520
11521
11522// Construct a RegExp exec result with two in-object properties.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011523void HOptimizedGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011524 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011525 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011526 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011527 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011528 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011529 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011530 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011531}
11532
11533
11534// Support for fast native caches.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011535void HOptimizedGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011536 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011537}
11538
11539
11540// Fast support for number to string.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011541void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011542 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011543 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011544 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011545 HCallStub* result =
11546 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011547 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011548 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011549}
11550
11551
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011552// Fast call for custom callbacks.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011553void HOptimizedGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011554 // 1 ~ The function to call is not itself an argument to the call.
11555 int arg_count = call->arguments()->length() - 1;
11556 ASSERT(arg_count >= 1); // There's always at least a receiver.
11557
11558 for (int i = 0; i < arg_count; ++i) {
11559 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
11560 }
11561 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
danno@chromium.orgc612e022011-11-10 11:38:15 +000011562
danno@chromium.org160a7b02011-04-18 15:51:38 +000011563 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011564 HValue* context = environment()->LookupContext();
danno@chromium.orgc612e022011-11-10 11:38:15 +000011565
11566 // Branch for function proxies, or other non-functions.
11567 HHasInstanceTypeAndBranch* typecheck =
11568 new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
11569 HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
11570 HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
11571 HBasicBlock* join = graph()->CreateBasicBlock();
11572 typecheck->SetSuccessorAt(0, if_jsfunction);
11573 typecheck->SetSuccessorAt(1, if_nonfunction);
11574 current_block()->Finish(typecheck);
11575
11576 set_current_block(if_jsfunction);
11577 HInstruction* invoke_result = AddInstruction(
11578 new(zone()) HInvokeFunction(context, function, arg_count));
danno@chromium.org160a7b02011-04-18 15:51:38 +000011579 Drop(arg_count);
danno@chromium.orgc612e022011-11-10 11:38:15 +000011580 Push(invoke_result);
11581 if_jsfunction->Goto(join);
11582
11583 set_current_block(if_nonfunction);
11584 HInstruction* call_result = AddInstruction(
11585 new(zone()) HCallFunction(context, function, arg_count));
11586 Drop(arg_count);
11587 Push(call_result);
11588 if_nonfunction->Goto(join);
11589
11590 set_current_block(join);
11591 join->SetJoinId(call->id());
11592 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011593}
11594
11595
11596// Fast call to math functions.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011597void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011598 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011599 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11600 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011601 HValue* right = Pop();
11602 HValue* left = Pop();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000011603 HInstruction* result = HPower::New(zone(), left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011604 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011605}
11606
11607
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011608void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011609 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011610 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011611 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011612 HCallStub* result =
11613 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011614 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011615 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011616 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011617}
11618
11619
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011620void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011621 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011622 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011623 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011624 HCallStub* result =
11625 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011626 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011627 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011628 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011629}
11630
11631
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011632void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000011633 ASSERT_EQ(1, call->arguments()->length());
11634 CHECK_ALIVE(VisitArgumentList(call->arguments()));
11635 HValue* context = environment()->LookupContext();
11636 HCallStub* result =
11637 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
11638 result->set_transcendental_type(TranscendentalCache::TAN);
11639 Drop(1);
11640 return ast_context()->ReturnInstruction(result, call->id());
11641}
11642
11643
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011644void HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011645 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000011646 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011647 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011648 HCallStub* result =
11649 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000011650 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011651 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011652 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011653}
11654
11655
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011656void HOptimizedGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000011657 ASSERT(call->arguments()->length() == 1);
11658 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
11659 HValue* value = Pop();
11660 HValue* context = environment()->LookupContext();
11661 HInstruction* result =
11662 HUnaryMathOperation::New(zone(), context, value, kMathSqrt);
11663 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011664}
11665
11666
11667// Check whether two RegExps are equivalent
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011668void HOptimizedGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011669 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011670}
11671
11672
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011673void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000011674 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000011675 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +000011676 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011677 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000011678 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011679}
11680
11681
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011682void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000011683 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011684}
11685
11686
danno@chromium.orgca29dd82013-04-26 11:59:48 +000011687// Support for generators.
11688void HOptimizedGraphBuilder::GenerateGeneratorSend(CallRuntime* call) {
11689 return Bailout("inlined runtime function: GeneratorSend");
11690}
11691
11692
11693void HOptimizedGraphBuilder::GenerateGeneratorThrow(CallRuntime* call) {
11694 return Bailout("inlined runtime function: GeneratorThrow");
11695}
11696
11697
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011698#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +000011699#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011700
11701
11702HEnvironment::HEnvironment(HEnvironment* outer,
11703 Scope* scope,
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011704 Handle<JSFunction> closure,
11705 Zone* zone)
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011706 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011707 values_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000011708 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011709 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011710 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011711 local_count_(0),
11712 outer_(outer),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000011713 entry_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011714 pop_count_(0),
11715 push_count_(0),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011716 ast_id_(BailoutId::None()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011717 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011718 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
11719}
11720
11721
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000011722HEnvironment::HEnvironment(Zone* zone, int parameter_count)
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011723 : values_(0, zone),
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011724 frame_type_(STUB),
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000011725 parameter_count_(parameter_count),
11726 specials_count_(1),
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011727 local_count_(0),
11728 outer_(NULL),
11729 entry_(NULL),
11730 pop_count_(0),
11731 push_count_(0),
11732 ast_id_(BailoutId::None()),
11733 zone_(zone) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000011734 Initialize(parameter_count, 0, 0);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000011735}
11736
11737
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011738HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone)
11739 : values_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000011740 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011741 parameter_count_(0),
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000011742 specials_count_(0),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011743 local_count_(0),
11744 outer_(NULL),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000011745 entry_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011746 pop_count_(0),
11747 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011748 ast_id_(other->ast_id()),
11749 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011750 Initialize(other);
11751}
11752
11753
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011754HEnvironment::HEnvironment(HEnvironment* outer,
11755 Handle<JSFunction> closure,
ulan@chromium.org967e2702012-02-28 09:49:15 +000011756 FrameType frame_type,
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011757 int arguments,
11758 Zone* zone)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011759 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011760 values_(arguments, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000011761 frame_type_(frame_type),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011762 parameter_count_(arguments),
11763 local_count_(0),
11764 outer_(outer),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000011765 entry_(NULL),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011766 pop_count_(0),
11767 push_count_(0),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011768 ast_id_(BailoutId::None()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011769 zone_(zone) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011770}
11771
11772
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011773void HEnvironment::Initialize(int parameter_count,
11774 int local_count,
11775 int stack_height) {
11776 parameter_count_ = parameter_count;
11777 local_count_ = local_count;
11778
11779 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011780 int total = parameter_count + specials_count_ + local_count + stack_height;
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011781 values_.Initialize(total + 4, zone());
11782 for (int i = 0; i < total; ++i) values_.Add(NULL, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011783}
11784
11785
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011786void HEnvironment::Initialize(const HEnvironment* other) {
11787 closure_ = other->closure();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011788 values_.AddAll(other->values_, zone());
jkummerow@chromium.org59297c72013-01-09 16:32:23 +000011789 assigned_variables_.Union(other->assigned_variables_, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +000011790 frame_type_ = other->frame_type_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011791 parameter_count_ = other->parameter_count_;
11792 local_count_ = other->local_count_;
11793 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
ulan@chromium.org56c14af2012-09-20 12:51:09 +000011794 entry_ = other->entry_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011795 pop_count_ = other->pop_count_;
11796 push_count_ = other->push_count_;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000011797 specials_count_ = other->specials_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011798 ast_id_ = other->ast_id_;
11799}
11800
11801
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011802void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
11803 ASSERT(!block->IsLoopHeader());
11804 ASSERT(values_.length() == other->values_.length());
11805
11806 int length = values_.length();
11807 for (int i = 0; i < length; ++i) {
11808 HValue* value = values_[i];
11809 if (value != NULL && value->IsPhi() && value->block() == block) {
11810 // There is already a phi for the i'th value.
11811 HPhi* phi = HPhi::cast(value);
11812 // Assert index is correct and that we haven't missed an incoming edge.
11813 ASSERT(phi->merged_index() == i);
11814 ASSERT(phi->OperandCount() == block->predecessors()->length());
11815 phi->AddInput(other->values_[i]);
11816 } else if (values_[i] != other->values_[i]) {
11817 // There is a fresh value on the incoming edge, a phi is needed.
11818 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011819 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011820 HValue* old_value = values_[i];
11821 for (int j = 0; j < block->predecessors()->length(); j++) {
11822 phi->AddInput(old_value);
11823 }
11824 phi->AddInput(other->values_[i]);
11825 this->values_[i] = phi;
11826 block->AddPhi(phi);
11827 }
11828 }
11829}
11830
11831
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011832void HEnvironment::Bind(int index, HValue* value) {
11833 ASSERT(value != NULL);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +000011834 assigned_variables_.Add(index, zone());
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011835 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011836}
11837
11838
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011839bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011840 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011841}
11842
11843
11844bool HEnvironment::ExpressionStackIsEmpty() const {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +000011845 ASSERT(length() >= first_expression_index());
11846 return length() == first_expression_index();
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011847}
11848
11849
11850void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
11851 int count = index_from_top + 1;
11852 int index = values_.length() - count;
11853 ASSERT(HasExpressionAt(index));
11854 // The push count must include at least the element in question or else
11855 // the new value will not be included in this environment's history.
11856 if (push_count_ < count) {
11857 // This is the same effect as popping then re-pushing 'count' elements.
11858 pop_count_ += (count - push_count_);
11859 push_count_ = count;
11860 }
11861 values_[index] = value;
11862}
11863
11864
11865void HEnvironment::Drop(int count) {
11866 for (int i = 0; i < count; ++i) {
11867 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011868 }
11869}
11870
11871
11872HEnvironment* HEnvironment::Copy() const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011873 return new(zone()) HEnvironment(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011874}
11875
11876
11877HEnvironment* HEnvironment::CopyWithoutHistory() const {
11878 HEnvironment* result = Copy();
11879 result->ClearHistory();
11880 return result;
11881}
11882
11883
11884HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
11885 HEnvironment* new_env = Copy();
11886 for (int i = 0; i < values_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011887 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011888 phi->AddInput(values_[i]);
11889 new_env->values_[i] = phi;
11890 loop_header->AddPhi(phi);
11891 }
11892 new_env->ClearHistory();
11893 return new_env;
11894}
11895
11896
ulan@chromium.org967e2702012-02-28 09:49:15 +000011897HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
11898 Handle<JSFunction> target,
11899 FrameType frame_type,
11900 int arguments) const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000011901 HEnvironment* new_env =
11902 new(zone()) HEnvironment(outer, target, frame_type,
11903 arguments + 1, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +000011904 for (int i = 0; i <= arguments; ++i) { // Include receiver.
11905 new_env->Push(ExpressionStackAt(arguments - i));
11906 }
11907 new_env->ClearHistory();
11908 return new_env;
11909}
11910
11911
danno@chromium.org40cb8782011-05-25 07:58:50 +000011912HEnvironment* HEnvironment::CopyForInlining(
11913 Handle<JSFunction> target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011914 int arguments,
danno@chromium.org40cb8782011-05-25 07:58:50 +000011915 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +000011916 HConstant* undefined,
yangguo@chromium.org003650e2013-01-24 16:31:08 +000011917 InliningKind inlining_kind,
11918 bool undefined_receiver) const {
ulan@chromium.org967e2702012-02-28 09:49:15 +000011919 ASSERT(frame_type() == JS_FUNCTION);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011920
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011921 // Outer environment is a copy of this one without the arguments.
11922 int arity = function->scope()->num_parameters();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011923
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011924 HEnvironment* outer = Copy();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011925 outer->Drop(arguments + 1); // Including receiver.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011926 outer->ClearHistory();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011927
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011928 if (inlining_kind == CONSTRUCT_CALL_RETURN) {
ulan@chromium.org967e2702012-02-28 09:49:15 +000011929 // Create artificial constructor stub environment. The receiver should
11930 // actually be the constructor function, but we pass the newly allocated
11931 // object instead, DoComputeConstructStubFrame() relies on that.
11932 outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +000011933 } else if (inlining_kind == GETTER_CALL_RETURN) {
11934 // We need an additional StackFrame::INTERNAL frame for restoring the
11935 // correct context.
11936 outer = CreateStubEnvironment(outer, target, JS_GETTER, arguments);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011937 } else if (inlining_kind == SETTER_CALL_RETURN) {
11938 // We need an additional StackFrame::INTERNAL frame for temporarily saving
11939 // the argument of the setter, see StoreStubCompiler::CompileStoreViaSetter.
11940 outer = CreateStubEnvironment(outer, target, JS_SETTER, arguments);
ulan@chromium.org967e2702012-02-28 09:49:15 +000011941 }
11942
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011943 if (arity != arguments) {
11944 // Create artificial arguments adaptation environment.
ulan@chromium.org967e2702012-02-28 09:49:15 +000011945 outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011946 }
11947
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000011948 HEnvironment* inner =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +000011949 new(zone()) HEnvironment(outer, function->scope(), target, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011950 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +000011951 for (int i = 0; i <= arity; ++i) { // Include receiver.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011952 HValue* push = (i <= arguments) ?
11953 ExpressionStackAt(arguments - i) : undefined;
vegorov@chromium.org3cf47312011-06-29 13:20:01 +000011954 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011955 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000011956 // If the function we are inlining is a strict mode function or a
11957 // builtin function, pass undefined as the receiver for function
11958 // calls (instead of the global receiver).
yangguo@chromium.org003650e2013-01-24 16:31:08 +000011959 if (undefined_receiver) {
danno@chromium.org40cb8782011-05-25 07:58:50 +000011960 inner->SetValueAt(0, undefined);
11961 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011962 inner->SetValueAt(arity + 1, LookupContext());
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011963 for (int i = arity + 2; i < inner->length(); ++i) {
11964 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011965 }
11966
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000011967 inner->set_ast_id(BailoutId::FunctionEntry());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011968 return inner;
11969}
11970
11971
11972void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +000011973 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011974 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011975 if (i == parameter_count()) stream->Add("specials\n");
11976 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
11977 if (i == parameter_count() + specials_count() + local_count()) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011978 stream->Add("expressions\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +000011979 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011980 HValue* val = values_.at(i);
11981 stream->Add("%d: ", i);
11982 if (val != NULL) {
11983 val->PrintNameTo(stream);
11984 } else {
11985 stream->Add("NULL");
11986 }
11987 stream->Add("\n");
11988 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000011989 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000011990}
11991
11992
11993void HEnvironment::PrintToStd() {
11994 HeapStringAllocator string_allocator;
11995 StringStream trace(&string_allocator);
11996 PrintTo(&trace);
11997 PrintF("%s", *trace.ToCString());
11998}
11999
12000
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000012001void HTracer::TraceCompilation(CompilationInfo* info) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012002 Tag tag(this, "compilation");
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000012003 if (info->IsOptimizing()) {
12004 Handle<String> name = info->function()->debug_name();
12005 PrintStringProperty("name", *name->ToCString());
12006 PrintStringProperty("method", *name->ToCString());
12007 } else {
12008 CodeStub::Major major_key = info->code_stub()->MajorKey();
12009 PrintStringProperty("name", CodeStub::MajorName(major_key, false));
12010 PrintStringProperty("method", "stub");
12011 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012012 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
12013}
12014
12015
jkummerow@chromium.org28583c92012-07-16 11:31:55 +000012016void HTracer::TraceLithium(const char* name, LChunk* chunk) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000012017 ASSERT(!FLAG_parallel_recompilation);
ulan@chromium.org32d7dba2013-04-24 10:59:06 +000012018 ALLOW_HANDLE_DEREF(chunk->isolate(), "debug output");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012019 Trace(name, chunk->graph(), chunk);
12020}
12021
12022
12023void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +000012024 ASSERT(!FLAG_parallel_recompilation);
ulan@chromium.org32d7dba2013-04-24 10:59:06 +000012025 ALLOW_HANDLE_DEREF(graph->isolate(), "debug output");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012026 Trace(name, graph, NULL);
12027}
12028
12029
jkummerow@chromium.org28583c92012-07-16 11:31:55 +000012030void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012031 Tag tag(this, "cfg");
12032 PrintStringProperty("name", name);
12033 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
12034 for (int i = 0; i < blocks->length(); i++) {
12035 HBasicBlock* current = blocks->at(i);
12036 Tag block_tag(this, "block");
12037 PrintBlockProperty("name", current->block_id());
12038 PrintIntProperty("from_bci", -1);
12039 PrintIntProperty("to_bci", -1);
12040
12041 if (!current->predecessors()->is_empty()) {
12042 PrintIndent();
12043 trace_.Add("predecessors");
12044 for (int j = 0; j < current->predecessors()->length(); ++j) {
12045 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
12046 }
12047 trace_.Add("\n");
12048 } else {
12049 PrintEmptyProperty("predecessors");
12050 }
12051
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000012052 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012053 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000012054 } else {
12055 PrintIndent();
12056 trace_.Add("successors");
12057 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
12058 trace_.Add(" \"B%d\"", it.Current()->block_id());
12059 }
12060 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012061 }
12062
12063 PrintEmptyProperty("xhandlers");
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000012064 const char* flags = current->IsLoopSuccessorDominator()
12065 ? "dom-loop-succ"
12066 : "";
12067 PrintStringProperty("flags", flags);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012068
12069 if (current->dominator() != NULL) {
12070 PrintBlockProperty("dominator", current->dominator()->block_id());
12071 }
12072
kmillikin@chromium.org83e16822011-09-13 08:21:47 +000012073 PrintIntProperty("loop_depth", current->LoopNestingDepth());
12074
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012075 if (chunk != NULL) {
12076 int first_index = current->first_instruction_index();
12077 int last_index = current->last_instruction_index();
12078 PrintIntProperty(
12079 "first_lir_id",
12080 LifetimePosition::FromInstructionIndex(first_index).Value());
12081 PrintIntProperty(
12082 "last_lir_id",
12083 LifetimePosition::FromInstructionIndex(last_index).Value());
12084 }
12085
12086 {
12087 Tag states_tag(this, "states");
12088 Tag locals_tag(this, "locals");
12089 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000012090 PrintIntProperty("size", current->phis()->length());
12091 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012092 for (int j = 0; j < total; ++j) {
12093 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +000012094 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012095 trace_.Add("%d ", phi->merged_index());
12096 phi->PrintNameTo(&trace_);
12097 trace_.Add(" ");
12098 phi->PrintTo(&trace_);
12099 trace_.Add("\n");
12100 }
12101 }
12102
12103 {
12104 Tag HIR_tag(this, "HIR");
12105 HInstruction* instruction = current->first();
12106 while (instruction != NULL) {
12107 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +000012108 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000012109 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012110 trace_.Add("%d %d ", bci, uses);
12111 instruction->PrintNameTo(&trace_);
12112 trace_.Add(" ");
12113 instruction->PrintTo(&trace_);
12114 trace_.Add(" <|@\n");
12115 instruction = instruction->next();
12116 }
12117 }
12118
12119
12120 if (chunk != NULL) {
12121 Tag LIR_tag(this, "LIR");
12122 int first_index = current->first_instruction_index();
12123 int last_index = current->last_instruction_index();
12124 if (first_index != -1 && last_index != -1) {
12125 const ZoneList<LInstruction*>* instructions = chunk->instructions();
12126 for (int i = first_index; i <= last_index; ++i) {
12127 LInstruction* linstr = instructions->at(i);
12128 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000012129 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012130 trace_.Add("%d ",
12131 LifetimePosition::FromInstructionIndex(i).Value());
12132 linstr->PrintTo(&trace_);
12133 trace_.Add(" <|@\n");
12134 }
12135 }
12136 }
12137 }
12138 }
12139}
12140
12141
12142void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
12143 Tag tag(this, "intervals");
12144 PrintStringProperty("name", name);
12145
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000012146 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012147 for (int i = 0; i < fixed_d->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000012148 TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012149 }
12150
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000012151 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012152 for (int i = 0; i < fixed->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000012153 TraceLiveRange(fixed->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012154 }
12155
12156 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
12157 for (int i = 0; i < live_ranges->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000012158 TraceLiveRange(live_ranges->at(i), "object", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012159 }
12160}
12161
12162
mmassi@chromium.org7028c052012-06-13 11:51:58 +000012163void HTracer::TraceLiveRange(LiveRange* range, const char* type,
12164 Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012165 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000012166 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012167 trace_.Add("%d %s", range->id(), type);
12168 if (range->HasRegisterAssigned()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000012169 LOperand* op = range->CreateAssignedOperand(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012170 int assigned_reg = op->index();
12171 if (op->IsDoubleRegister()) {
12172 trace_.Add(" \"%s\"",
12173 DoubleRegister::AllocationIndexToString(assigned_reg));
12174 } else {
12175 ASSERT(op->IsRegister());
12176 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
12177 }
12178 } else if (range->IsSpilled()) {
12179 LOperand* op = range->TopLevel()->GetSpillOperand();
12180 if (op->IsDoubleStackSlot()) {
12181 trace_.Add(" \"double_stack:%d\"", op->index());
12182 } else {
12183 ASSERT(op->IsStackSlot());
12184 trace_.Add(" \"stack:%d\"", op->index());
12185 }
12186 }
12187 int parent_index = -1;
12188 if (range->IsChild()) {
12189 parent_index = range->parent()->id();
12190 } else {
12191 parent_index = range->id();
12192 }
12193 LOperand* op = range->FirstHint();
12194 int hint_index = -1;
danno@chromium.orgfa458e42012-02-01 10:48:36 +000012195 if (op != NULL && op->IsUnallocated()) {
12196 hint_index = LUnallocated::cast(op)->virtual_register();
12197 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012198 trace_.Add(" %d %d", parent_index, hint_index);
12199 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000012200 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012201 trace_.Add(" [%d, %d[",
12202 cur_interval->start().Value(),
12203 cur_interval->end().Value());
12204 cur_interval = cur_interval->next();
12205 }
12206
12207 UsePosition* current_pos = range->first_pos();
12208 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000012209 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012210 trace_.Add(" %d M", current_pos->pos().Value());
12211 }
12212 current_pos = current_pos->next();
12213 }
12214
12215 trace_.Add(" \"\"\n");
12216 }
12217}
12218
12219
12220void HTracer::FlushToFile() {
ulan@chromium.org750145a2013-03-07 15:14:13 +000012221 AppendChars(filename_.start(), *trace_.ToCString(), trace_.length(), false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012222 trace_.Reset();
12223}
12224
12225
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000012226void HStatistics::Initialize(CompilationInfo* info) {
yangguo@chromium.org003650e2013-01-24 16:31:08 +000012227 if (info->shared_info().is_null()) return;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000012228 source_size_ += info->shared_info()->SourceSize();
12229}
12230
12231
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012232void HStatistics::Print() {
12233 PrintF("Timing results:\n");
12234 int64_t sum = 0;
12235 for (int i = 0; i < timing_.length(); ++i) {
12236 sum += timing_[i];
12237 }
12238
12239 for (int i = 0; i < names_.length(); ++i) {
12240 PrintF("%30s", names_[i]);
12241 double ms = static_cast<double>(timing_[i]) / 1000;
12242 double percent = static_cast<double>(timing_[i]) * 100 / sum;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012243 PrintF(" - %8.3f ms / %4.1f %% ", ms, percent);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012244
12245 unsigned size = sizes_[i];
12246 double size_percent = static_cast<double>(size) * 100 / total_size_;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012247 PrintF(" %9u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012248 }
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012249
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012250 PrintF("----------------------------------------"
12251 "---------------------------------------\n");
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012252 int64_t total = create_graph_ + optimize_graph_ + generate_code_;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012253 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012254 "Create graph",
12255 static_cast<double>(create_graph_) / 1000,
12256 static_cast<double>(create_graph_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012257 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012258 "Optimize graph",
12259 static_cast<double>(optimize_graph_) / 1000,
12260 static_cast<double>(optimize_graph_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012261 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012262 "Generate and install code",
12263 static_cast<double>(generate_code_) / 1000,
12264 static_cast<double>(generate_code_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012265 PrintF("----------------------------------------"
12266 "---------------------------------------\n");
12267 PrintF("%30s - %8.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012268 "Total",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012269 static_cast<double>(total) / 1000,
12270 static_cast<double>(total) / full_code_gen_);
12271
12272 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
12273 double normalized_time = source_size_in_kb > 0
12274 ? (static_cast<double>(total) / 1000) / source_size_in_kb
12275 : 0;
12276 double normalized_size_in_kb = source_size_in_kb > 0
12277 ? total_size_ / 1024 / source_size_in_kb
12278 : 0;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012279 PrintF("%30s - %8.3f ms %7.3f kB allocated\n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000012280 "Average per kB source",
12281 normalized_time, normalized_size_in_kb);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012282}
12283
12284
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012285void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012286 if (name == HPhase::kFullCodeGen) {
12287 full_code_gen_ += ticks;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012288 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012289 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012290 for (int i = 0; i < names_.length(); ++i) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000012291 if (strcmp(names_[i], name) == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012292 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012293 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012294 return;
12295 }
12296 }
12297 names_.Add(name);
12298 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012299 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012300 }
12301}
12302
12303
12304const char* const HPhase::kFullCodeGen = "Full code generator";
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012305
ulan@chromium.org750145a2013-03-07 15:14:13 +000012306
12307HPhase::HPhase(const char* name, Isolate* isolate) {
12308 Init(isolate, name, NULL, NULL, NULL);
12309}
12310
12311
12312HPhase::HPhase(const char* name, HGraph* graph) {
12313 Init(graph->isolate(), name, graph, NULL, NULL);
12314}
12315
12316
12317HPhase::HPhase(const char* name, LChunk* chunk) {
12318 Init(chunk->isolate(), name, NULL, chunk, NULL);
12319}
12320
12321
12322HPhase::HPhase(const char* name, LAllocator* allocator) {
12323 Init(allocator->isolate(), name, NULL, NULL, allocator);
12324}
12325
12326
12327void HPhase::Init(Isolate* isolate,
12328 const char* name,
12329 HGraph* graph,
12330 LChunk* chunk,
12331 LAllocator* allocator) {
12332 isolate_ = isolate;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012333 name_ = name;
12334 graph_ = graph;
12335 chunk_ = chunk;
12336 allocator_ = allocator;
12337 if (allocator != NULL && chunk_ == NULL) {
12338 chunk_ = allocator->chunk();
12339 }
ulan@chromium.org750145a2013-03-07 15:14:13 +000012340 if (FLAG_hydrogen_stats) {
12341 start_ticks_ = OS::Ticks();
12342 start_allocation_size_ = Zone::allocation_size_;
12343 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012344}
12345
12346
ulan@chromium.org750145a2013-03-07 15:14:13 +000012347HPhase::~HPhase() {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000012348 if (FLAG_hydrogen_stats) {
ulan@chromium.org750145a2013-03-07 15:14:13 +000012349 int64_t ticks = OS::Ticks() - start_ticks_;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000012350 unsigned size = Zone::allocation_size_ - start_allocation_size_;
ulan@chromium.org750145a2013-03-07 15:14:13 +000012351 isolate_->GetHStatistics()->SaveTiming(name_, ticks, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012352 }
12353
fschneider@chromium.org35814e52012-03-01 15:43:35 +000012354 // Produce trace output if flag is set so that the first letter of the
12355 // phase name matches the command line parameter FLAG_trace_phase.
12356 if (FLAG_trace_hydrogen &&
12357 OS::StrChr(const_cast<char*>(FLAG_trace_phase), name_[0]) != NULL) {
ulan@chromium.org750145a2013-03-07 15:14:13 +000012358 if (graph_ != NULL) {
12359 isolate_->GetHTracer()->TraceHydrogen(name_, graph_);
12360 }
12361 if (chunk_ != NULL) {
12362 isolate_->GetHTracer()->TraceLithium(name_, chunk_);
12363 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012364 if (allocator_ != NULL) {
ulan@chromium.org750145a2013-03-07 15:14:13 +000012365 isolate_->GetHTracer()->TraceLiveRanges(name_, allocator_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012366 }
12367 }
12368
12369#ifdef DEBUG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000012370 if (graph_ != NULL) graph_->Verify(false); // No full verify.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000012371 if (allocator_ != NULL) allocator_->Verify();
12372#endif
12373}
12374
12375} } // namespace v8::internal