blob: 2754fa32e01d764418471c10231c7db9e9697c64 [file] [log] [blame]
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001// Copyright 2012 the V8 project authors. All rights reserved.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000028#include "v8.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000029#include "hydrogen.h"
30
31#include "codegen.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000032#include "full-codegen.h"
33#include "hashmap.h"
34#include "lithium-allocator.h"
35#include "parser.h"
ricow@chromium.org4f693d62011-07-04 14:01:31 +000036#include "scopeinfo.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000037#include "scopes.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000038#include "stub-cache.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000039
40#if V8_TARGET_ARCH_IA32
41#include "ia32/lithium-codegen-ia32.h"
42#elif V8_TARGET_ARCH_X64
43#include "x64/lithium-codegen-x64.h"
44#elif V8_TARGET_ARCH_ARM
45#include "arm/lithium-codegen-arm.h"
lrn@chromium.org7516f052011-03-30 08:52:27 +000046#elif V8_TARGET_ARCH_MIPS
47#include "mips/lithium-codegen-mips.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000048#else
49#error Unsupported target architecture.
50#endif
51
52namespace v8 {
53namespace internal {
54
55HBasicBlock::HBasicBlock(HGraph* graph)
56 : block_id_(graph->GetNextBlockID()),
57 graph_(graph),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000058 phis_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000059 first_(NULL),
60 last_(NULL),
61 end_(NULL),
62 loop_information_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000063 predecessors_(2, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000064 dominator_(NULL),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000065 dominated_blocks_(4, graph->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000066 last_environment_(NULL),
67 argument_count_(-1),
68 first_instruction_index_(-1),
69 last_instruction_index_(-1),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000070 deleted_phis_(4, graph->zone()),
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000071 parent_loop_header_(NULL),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000072 is_inline_return_target_(false),
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000073 is_deoptimizing_(false),
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +000074 dominates_loop_successors_(false),
75 is_osr_entry_(false) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000076
77
78void HBasicBlock::AttachLoopInformation() {
79 ASSERT(!IsLoopHeader());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000080 loop_information_ = new(zone()) HLoopInformation(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000081}
82
83
84void HBasicBlock::DetachLoopInformation() {
85 ASSERT(IsLoopHeader());
86 loop_information_ = NULL;
87}
88
89
90void HBasicBlock::AddPhi(HPhi* phi) {
91 ASSERT(!IsStartBlock());
mmassi@chromium.org7028c052012-06-13 11:51:58 +000092 phis_.Add(phi, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000093 phi->SetBlock(this);
94}
95
96
97void HBasicBlock::RemovePhi(HPhi* phi) {
98 ASSERT(phi->block() == this);
99 ASSERT(phis_.Contains(phi));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000100 ASSERT(phi->HasNoUses() || !phi->is_live());
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000101 phi->Kill();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000102 phis_.RemoveElement(phi);
103 phi->SetBlock(NULL);
104}
105
106
107void HBasicBlock::AddInstruction(HInstruction* instr) {
108 ASSERT(!IsStartBlock() || !IsFinished());
109 ASSERT(!instr->IsLinked());
110 ASSERT(!IsFinished());
111 if (first_ == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000112 HBlockEntry* entry = new(zone()) HBlockEntry();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000113 entry->InitializeAsFirst(this);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000114 first_ = last_ = entry;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000115 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000116 instr->InsertAfter(last_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000117}
118
119
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000120HDeoptimize* HBasicBlock::CreateDeoptimize(
121 HDeoptimize::UseEnvironment has_uses) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000122 ASSERT(HasEnvironment());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000123 if (has_uses == HDeoptimize::kNoUses)
124 return new(zone()) HDeoptimize(0, zone());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000125
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000126 HEnvironment* environment = last_environment();
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000127 HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000128 for (int i = 0; i < environment->length(); i++) {
129 HValue* val = environment->values()->at(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000130 instr->AddEnvironmentValue(val, zone());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000131 }
132
133 return instr;
134}
135
136
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000137HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
138 RemovableSimulate removable) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000139 ASSERT(HasEnvironment());
140 HEnvironment* environment = last_environment();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000141 ASSERT(ast_id.IsNone() ||
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000142 ast_id == BailoutId::StubEntry() ||
ager@chromium.org04921a82011-06-27 13:21:41 +0000143 environment->closure()->shared()->VerifyBailoutId(ast_id));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000144
145 int push_count = environment->push_count();
146 int pop_count = environment->pop_count();
147
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000148 HSimulate* instr =
149 new(zone()) HSimulate(ast_id, pop_count, zone(), removable);
150 // Order of pushed values: newest (top of stack) first. This allows
151 // HSimulate::MergeInto() to easily append additional pushed values
152 // that are older (from further down the stack).
153 for (int i = 0; i < push_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000154 instr->AddPushedValue(environment->ExpressionStackAt(i));
155 }
jkummerow@chromium.org59297c72013-01-09 16:32:23 +0000156 for (GrowableBitVector::Iterator it(environment->assigned_variables(),
157 zone());
158 !it.Done();
159 it.Advance()) {
160 int index = it.Current();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000161 instr->AddAssignedValue(index, environment->Lookup(index));
162 }
163 environment->ClearHistory();
164 return instr;
165}
166
167
168void HBasicBlock::Finish(HControlInstruction* end) {
169 ASSERT(!IsFinished());
170 AddInstruction(end);
171 end_ = end;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000172 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
173 it.Current()->RegisterPredecessor(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000174 }
175}
176
177
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000178void HBasicBlock::Goto(HBasicBlock* block, FunctionState* state) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000179 bool drop_extra = state != NULL &&
180 state->inlining_kind() == DROP_EXTRA_ON_RETURN;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000181
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000182 if (block->IsInlineReturnTarget()) {
verwaest@chromium.org33e09c82012-10-10 17:07:22 +0000183 AddInstruction(new(zone()) HLeaveInlined());
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000184 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000185 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000186
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000187 AddSimulate(BailoutId::None());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000188 HGoto* instr = new(zone()) HGoto(block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000189 Finish(instr);
190}
191
192
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000193void HBasicBlock::AddLeaveInlined(HValue* return_value,
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000194 FunctionState* state) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000195 HBasicBlock* target = state->function_return();
196 bool drop_extra = state->inlining_kind() == DROP_EXTRA_ON_RETURN;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000197
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000198 ASSERT(target->IsInlineReturnTarget());
199 ASSERT(return_value != NULL);
verwaest@chromium.org33e09c82012-10-10 17:07:22 +0000200 AddInstruction(new(zone()) HLeaveInlined());
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000201 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000202 last_environment()->Push(return_value);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000203 AddSimulate(BailoutId::None());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000204 HGoto* instr = new(zone()) HGoto(target);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000205 Finish(instr);
206}
207
208
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000209void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
210 ASSERT(!HasEnvironment());
211 ASSERT(first() == NULL);
212 UpdateEnvironment(env);
213}
214
215
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000216void HBasicBlock::SetJoinId(BailoutId ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000217 int length = predecessors_.length();
218 ASSERT(length > 0);
219 for (int i = 0; i < length; i++) {
220 HBasicBlock* predecessor = predecessors_[i];
221 ASSERT(predecessor->end()->IsGoto());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000222 HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000223 // We only need to verify the ID once.
224 ASSERT(i != 0 ||
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000225 (predecessor->last_environment()->closure().is_null() ||
226 predecessor->last_environment()->closure()->shared()
227 ->VerifyBailoutId(ast_id)));
ager@chromium.org04921a82011-06-27 13:21:41 +0000228 simulate->set_ast_id(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000229 }
230}
231
232
233bool HBasicBlock::Dominates(HBasicBlock* other) const {
234 HBasicBlock* current = other->dominator();
235 while (current != NULL) {
236 if (current == this) return true;
237 current = current->dominator();
238 }
239 return false;
240}
241
242
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000243int HBasicBlock::LoopNestingDepth() const {
244 const HBasicBlock* current = this;
245 int result = (current->IsLoopHeader()) ? 1 : 0;
246 while (current->parent_loop_header() != NULL) {
247 current = current->parent_loop_header();
248 result++;
249 }
250 return result;
251}
252
253
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000254void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
255 ASSERT(IsLoopHeader());
256
257 SetJoinId(stmt->EntryId());
258 if (predecessors()->length() == 1) {
259 // This is a degenerated loop.
260 DetachLoopInformation();
261 return;
262 }
263
264 // Only the first entry into the loop is from outside the loop. All other
265 // entries must be back edges.
266 for (int i = 1; i < predecessors()->length(); ++i) {
267 loop_information()->RegisterBackEdge(predecessors()->at(i));
268 }
269}
270
271
272void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
danno@chromium.org160a7b02011-04-18 15:51:38 +0000273 if (HasPredecessor()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000274 // Only loop header blocks can have a predecessor added after
275 // instructions have been added to the block (they have phis for all
276 // values in the environment, these phis may be eliminated later).
277 ASSERT(IsLoopHeader() || first_ == NULL);
278 HEnvironment* incoming_env = pred->last_environment();
279 if (IsLoopHeader()) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000280 ASSERT(phis()->length() == incoming_env->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000281 for (int i = 0; i < phis_.length(); ++i) {
282 phis_[i]->AddInput(incoming_env->values()->at(i));
283 }
284 } else {
285 last_environment()->AddIncomingEdge(this, pred->last_environment());
286 }
287 } else if (!HasEnvironment() && !IsFinished()) {
288 ASSERT(!IsLoopHeader());
289 SetInitialEnvironment(pred->last_environment()->Copy());
290 }
291
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000292 predecessors_.Add(pred, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000293}
294
295
296void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
297 ASSERT(!dominated_blocks_.Contains(block));
298 // Keep the list of dominated blocks sorted such that if there is two
299 // succeeding block in this list, the predecessor is before the successor.
300 int index = 0;
301 while (index < dominated_blocks_.length() &&
302 dominated_blocks_[index]->block_id() < block->block_id()) {
303 ++index;
304 }
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000305 dominated_blocks_.InsertAt(index, block, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000306}
307
308
309void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
310 if (dominator_ == NULL) {
311 dominator_ = other;
312 other->AddDominatedBlock(this);
313 } else if (other->dominator() != NULL) {
314 HBasicBlock* first = dominator_;
315 HBasicBlock* second = other;
316
317 while (first != second) {
318 if (first->block_id() > second->block_id()) {
319 first = first->dominator();
320 } else {
321 second = second->dominator();
322 }
323 ASSERT(first != NULL && second != NULL);
324 }
325
326 if (dominator_ != first) {
327 ASSERT(dominator_->dominated_blocks_.Contains(this));
328 dominator_->dominated_blocks_.RemoveElement(this);
329 dominator_ = first;
330 first->AddDominatedBlock(this);
331 }
332 }
333}
334
335
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000336void HBasicBlock::AssignLoopSuccessorDominators() {
337 // Mark blocks that dominate all subsequent reachable blocks inside their
338 // loop. Exploit the fact that blocks are sorted in reverse post order. When
339 // the loop is visited in increasing block id order, if the number of
340 // non-loop-exiting successor edges at the dominator_candidate block doesn't
341 // exceed the number of previously encountered predecessor edges, there is no
342 // path from the loop header to any block with higher id that doesn't go
343 // through the dominator_candidate block. In this case, the
344 // dominator_candidate block is guaranteed to dominate all blocks reachable
345 // from it with higher ids.
346 HBasicBlock* last = loop_information()->GetLastBackEdge();
347 int outstanding_successors = 1; // one edge from the pre-header
348 // Header always dominates everything.
349 MarkAsLoopSuccessorDominator();
350 for (int j = block_id(); j <= last->block_id(); ++j) {
351 HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
352 for (HPredecessorIterator it(dominator_candidate); !it.Done();
353 it.Advance()) {
354 HBasicBlock* predecessor = it.Current();
355 // Don't count back edges.
356 if (predecessor->block_id() < dominator_candidate->block_id()) {
357 outstanding_successors--;
358 }
359 }
360
361 // If more successors than predecessors have been seen in the loop up to
362 // now, it's not possible to guarantee that the current block dominates
363 // all of the blocks with higher IDs. In this case, assume conservatively
364 // that those paths through loop that don't go through the current block
365 // contain all of the loop's dependencies. Also be careful to record
366 // dominator information about the current loop that's being processed,
367 // and not nested loops, which will be processed when
368 // AssignLoopSuccessorDominators gets called on their header.
369 ASSERT(outstanding_successors >= 0);
370 HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header();
371 if (outstanding_successors == 0 &&
372 (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) {
373 dominator_candidate->MarkAsLoopSuccessorDominator();
374 }
375 HControlInstruction* end = dominator_candidate->end();
376 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
377 HBasicBlock* successor = it.Current();
378 // Only count successors that remain inside the loop and don't loop back
379 // to a loop header.
380 if (successor->block_id() > dominator_candidate->block_id() &&
381 successor->block_id() <= last->block_id()) {
382 // Backwards edges must land on loop headers.
383 ASSERT(successor->block_id() > dominator_candidate->block_id() ||
384 successor->IsLoopHeader());
385 outstanding_successors++;
386 }
387 }
388 }
389}
390
391
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000392int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
393 for (int i = 0; i < predecessors_.length(); ++i) {
394 if (predecessors_[i] == predecessor) return i;
395 }
396 UNREACHABLE();
397 return -1;
398}
399
400
401#ifdef DEBUG
402void HBasicBlock::Verify() {
403 // Check that every block is finished.
404 ASSERT(IsFinished());
405 ASSERT(block_id() >= 0);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000406
407 // Check that the incoming edges are in edge split form.
408 if (predecessors_.length() > 1) {
409 for (int i = 0; i < predecessors_.length(); ++i) {
410 ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
411 }
412 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000413}
414#endif
415
416
417void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000418 this->back_edges_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000419 AddBlock(block);
420}
421
422
423HBasicBlock* HLoopInformation::GetLastBackEdge() const {
424 int max_id = -1;
425 HBasicBlock* result = NULL;
426 for (int i = 0; i < back_edges_.length(); ++i) {
427 HBasicBlock* cur = back_edges_[i];
428 if (cur->block_id() > max_id) {
429 max_id = cur->block_id();
430 result = cur;
431 }
432 }
433 return result;
434}
435
436
437void HLoopInformation::AddBlock(HBasicBlock* block) {
438 if (block == loop_header()) return;
439 if (block->parent_loop_header() == loop_header()) return;
440 if (block->parent_loop_header() != NULL) {
441 AddBlock(block->parent_loop_header());
442 } else {
443 block->set_parent_loop_header(loop_header());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000444 blocks_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000445 for (int i = 0; i < block->predecessors()->length(); ++i) {
446 AddBlock(block->predecessors()->at(i));
447 }
448 }
449}
450
451
452#ifdef DEBUG
453
454// Checks reachability of the blocks in this graph and stores a bit in
455// the BitVector "reachable()" for every block that can be reached
456// from the start block of the graph. If "dont_visit" is non-null, the given
457// block is treated as if it would not be part of the graph. "visited_count()"
458// returns the number of reachable blocks.
459class ReachabilityAnalyzer BASE_EMBEDDED {
460 public:
461 ReachabilityAnalyzer(HBasicBlock* entry_block,
462 int block_count,
463 HBasicBlock* dont_visit)
464 : visited_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000465 stack_(16, entry_block->zone()),
466 reachable_(block_count, entry_block->zone()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000467 dont_visit_(dont_visit) {
468 PushBlock(entry_block);
469 Analyze();
470 }
471
472 int visited_count() const { return visited_count_; }
473 const BitVector* reachable() const { return &reachable_; }
474
475 private:
476 void PushBlock(HBasicBlock* block) {
477 if (block != NULL && block != dont_visit_ &&
478 !reachable_.Contains(block->block_id())) {
479 reachable_.Add(block->block_id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000480 stack_.Add(block, block->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000481 visited_count_++;
482 }
483 }
484
485 void Analyze() {
486 while (!stack_.is_empty()) {
487 HControlInstruction* end = stack_.RemoveLast()->end();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000488 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
489 PushBlock(it.Current());
490 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000491 }
492 }
493
494 int visited_count_;
495 ZoneList<HBasicBlock*> stack_;
496 BitVector reachable_;
497 HBasicBlock* dont_visit_;
498};
499
500
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000501void HGraph::Verify(bool do_full_verify) const {
yangguo@chromium.org003650e2013-01-24 16:31:08 +0000502 // Allow dereferencing for debug mode verification.
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000503 AllowHandleDereference allow_handle_deref(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000504 for (int i = 0; i < blocks_.length(); i++) {
505 HBasicBlock* block = blocks_.at(i);
506
507 block->Verify();
508
509 // Check that every block contains at least one node and that only the last
510 // node is a control instruction.
511 HInstruction* current = block->first();
512 ASSERT(current != NULL && current->IsBlockEntry());
513 while (current != NULL) {
514 ASSERT((current->next() == NULL) == current->IsControlInstruction());
515 ASSERT(current->block() == block);
516 current->Verify();
517 current = current->next();
518 }
519
520 // Check that successors are correctly set.
521 HBasicBlock* first = block->end()->FirstSuccessor();
522 HBasicBlock* second = block->end()->SecondSuccessor();
523 ASSERT(second == NULL || first != NULL);
524
525 // Check that the predecessor array is correct.
526 if (first != NULL) {
527 ASSERT(first->predecessors()->Contains(block));
528 if (second != NULL) {
529 ASSERT(second->predecessors()->Contains(block));
530 }
531 }
532
533 // Check that phis have correct arguments.
534 for (int j = 0; j < block->phis()->length(); j++) {
535 HPhi* phi = block->phis()->at(j);
536 phi->Verify();
537 }
538
539 // Check that all join blocks have predecessors that end with an
540 // unconditional goto and agree on their environment node id.
541 if (block->predecessors()->length() >= 2) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000542 BailoutId id =
543 block->predecessors()->first()->last_environment()->ast_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000544 for (int k = 0; k < block->predecessors()->length(); k++) {
545 HBasicBlock* predecessor = block->predecessors()->at(k);
546 ASSERT(predecessor->end()->IsGoto());
547 ASSERT(predecessor->last_environment()->ast_id() == id);
548 }
549 }
550 }
551
552 // Check special property of first block to have no predecessors.
553 ASSERT(blocks_.at(0)->predecessors()->is_empty());
554
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000555 if (do_full_verify) {
556 // Check that the graph is fully connected.
557 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
558 ASSERT(analyzer.visited_count() == blocks_.length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000559
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000560 // Check that entry block dominator is NULL.
561 ASSERT(entry_block_->dominator() == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000562
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000563 // Check dominators.
564 for (int i = 0; i < blocks_.length(); ++i) {
565 HBasicBlock* block = blocks_.at(i);
566 if (block->dominator() == NULL) {
567 // Only start block may have no dominator assigned to.
568 ASSERT(i == 0);
569 } else {
570 // Assert that block is unreachable if dominator must not be visited.
571 ReachabilityAnalyzer dominator_analyzer(entry_block_,
572 blocks_.length(),
573 block->dominator());
574 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
575 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000576 }
577 }
578}
579
580#endif
581
582
583HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000584 Handle<Object> value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000585 if (!pointer->is_set()) {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000586 HConstant* constant = new(zone()) HConstant(value,
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000587 Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000588 constant->InsertAfter(GetConstantUndefined());
589 pointer->set(constant);
590 }
591 return pointer->get();
592}
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.org94b0d6f2013-02-04 13:33:20 +0000607HConstant* HGraph::GetConstant0() {
608 return GetConstantInt32(&constant_0_, 0);
609}
610
611
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000612HConstant* HGraph::GetConstant1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000613 return GetConstantInt32(&constant_1_, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000614}
615
616
617HConstant* HGraph::GetConstantMinus1() {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +0000618 return GetConstantInt32(&constant_minus1_, -1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000619}
620
621
622HConstant* HGraph::GetConstantTrue() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000623 return GetConstant(&constant_true_, isolate()->factory()->true_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000624}
625
626
627HConstant* HGraph::GetConstantFalse() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000628 return GetConstant(&constant_false_, isolate()->factory()->false_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000629}
630
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000631
632HConstant* HGraph::GetConstantHole() {
svenpanne@chromium.org619781a2012-07-05 08:22:44 +0000633 return GetConstant(&constant_hole_, isolate()->factory()->the_hole_value());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000634}
635
636
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +0000637HGraphBuilder::CheckBuilder::CheckBuilder(HGraphBuilder* builder, BailoutId id)
638 : builder_(builder),
639 finished_(false),
640 id_(id) {
641 HEnvironment* env = builder->environment();
642 failure_block_ = builder->CreateBasicBlock(env->Copy());
643 merge_block_ = builder->CreateBasicBlock(env->Copy());
644}
645
646
647void HGraphBuilder::CheckBuilder::CheckNotUndefined(HValue* value) {
648 HEnvironment* env = builder_->environment();
649 HIsNilAndBranch* compare =
650 new(zone()) HIsNilAndBranch(value, kStrictEquality, kUndefinedValue);
651 HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy());
652 HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy());
653 compare->SetSuccessorAt(0, failure_block);
654 compare->SetSuccessorAt(1, success_block);
655 failure_block->Goto(failure_block_);
656 builder_->current_block()->Finish(compare);
657 builder_->set_current_block(success_block);
658}
659
660
661void HGraphBuilder::CheckBuilder::CheckIntegerEq(HValue* left, HValue* right) {
662 HEnvironment* env = builder_->environment();
663 HCompareIDAndBranch* compare =
664 new(zone()) HCompareIDAndBranch(left, right, Token::EQ);
665 compare->AssumeRepresentation(Representation::Integer32());
666 HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy());
667 HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy());
668 compare->SetSuccessorAt(0, success_block);
669 compare->SetSuccessorAt(1, failure_block);
670 failure_block->Goto(failure_block_);
671 builder_->current_block()->Finish(compare);
672 builder_->set_current_block(success_block);
673}
674
675
676void HGraphBuilder::CheckBuilder::End() {
677 ASSERT(!finished_);
678 builder_->current_block()->Goto(merge_block_);
679 failure_block_->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
680 failure_block_->SetJoinId(id_);
681 builder_->set_current_block(merge_block_);
682 merge_block_->SetJoinId(id_);
683 finished_ = true;
684}
685
686
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000687HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, BailoutId id)
688 : builder_(builder),
689 finished_(false),
690 id_(id) {
691 HEnvironment* env = builder->environment();
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +0000692 first_true_block_ = builder->CreateBasicBlock(env->Copy());
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000693 last_true_block_ = NULL;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +0000694 first_false_block_ = builder->CreateBasicBlock(env->Copy());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000695}
696
697
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000698HInstruction* HGraphBuilder::IfBuilder::BeginTrue(
699 HValue* left,
700 HValue* right,
701 Token::Value token,
702 Representation input_representation) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000703 HCompareIDAndBranch* compare =
704 new(zone()) HCompareIDAndBranch(left, right, token);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000705 compare->set_observed_input_representation(input_representation,
706 input_representation);
707 compare->ChangeRepresentation(input_representation);
708 compare->SetSuccessorAt(0, first_true_block_);
709 compare->SetSuccessorAt(1, first_false_block_);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000710 builder_->current_block()->Finish(compare);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000711 builder_->set_current_block(first_true_block_);
712 return compare;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000713}
714
715
716void HGraphBuilder::IfBuilder::BeginFalse() {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000717 last_true_block_ = builder_->current_block();
718 ASSERT(!last_true_block_->IsFinished());
719 builder_->set_current_block(first_false_block_);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000720}
721
722
723void HGraphBuilder::IfBuilder::End() {
724 ASSERT(!finished_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000725 ASSERT(!last_true_block_->IsFinished());
726 HBasicBlock* last_false_block = builder_->current_block();
727 ASSERT(!last_false_block->IsFinished());
728 HEnvironment* merge_env =
729 last_true_block_->last_environment()->Copy();
730 merge_block_ = builder_->CreateBasicBlock(merge_env);
731 last_true_block_->Goto(merge_block_);
732 last_false_block->Goto(merge_block_);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000733 merge_block_->SetJoinId(id_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000734 builder_->set_current_block(merge_block_);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000735 finished_ = true;
736}
737
738
739HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder,
740 HValue* context,
741 LoopBuilder::Direction direction,
742 BailoutId id)
743 : builder_(builder),
744 context_(context),
745 direction_(direction),
746 id_(id),
747 finished_(false) {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000748 header_block_ = builder->CreateLoopHeaderBlock();
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000749 body_block_ = NULL;
750 exit_block_ = NULL;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000751}
752
753
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000754HValue* HGraphBuilder::LoopBuilder::BeginBody(
755 HValue* initial,
756 HValue* terminating,
757 Token::Value token,
758 Representation input_representation) {
759 HEnvironment* env = builder_->environment();
760 phi_ = new(zone()) HPhi(env->values()->length(), zone());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000761 header_block_->AddPhi(phi_);
762 phi_->AddInput(initial);
763 phi_->ChangeRepresentation(Representation::Integer32());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000764 env->Push(initial);
765 builder_->current_block()->Goto(header_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000766
767 HEnvironment* body_env = env->Copy();
768 HEnvironment* exit_env = env->Copy();
769 body_block_ = builder_->CreateBasicBlock(body_env);
770 exit_block_ = builder_->CreateBasicBlock(exit_env);
771 // Remove the phi from the expression stack
772 body_env->Pop();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000773
774 builder_->set_current_block(header_block_);
775 HCompareIDAndBranch* compare =
776 new(zone()) HCompareIDAndBranch(phi_, terminating, token);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000777 compare->set_observed_input_representation(input_representation,
778 input_representation);
779 compare->ChangeRepresentation(input_representation);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000780 compare->SetSuccessorAt(0, body_block_);
781 compare->SetSuccessorAt(1, exit_block_);
782 builder_->current_block()->Finish(compare);
783
784 builder_->set_current_block(body_block_);
785 if (direction_ == kPreIncrement || direction_ == kPreDecrement) {
786 HValue* one = builder_->graph()->GetConstant1();
787 if (direction_ == kPreIncrement) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000788 increment_ = HAdd::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000789 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000790 increment_ = HSub::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000791 }
792 increment_->ClearFlag(HValue::kCanOverflow);
793 increment_->ChangeRepresentation(Representation::Integer32());
794 builder_->AddInstruction(increment_);
795 return increment_;
796 } else {
797 return phi_;
798 }
799}
800
801
802void HGraphBuilder::LoopBuilder::EndBody() {
803 ASSERT(!finished_);
804
805 if (direction_ == kPostIncrement || direction_ == kPostDecrement) {
806 HValue* one = builder_->graph()->GetConstant1();
807 if (direction_ == kPostIncrement) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000808 increment_ = HAdd::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000809 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000810 increment_ = HSub::New(zone(), context_, phi_, one);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000811 }
812 increment_->ClearFlag(HValue::kCanOverflow);
813 increment_->ChangeRepresentation(Representation::Integer32());
814 builder_->AddInstruction(increment_);
815 }
816
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000817 // Push the new increment value on the expression stack to merge into the phi.
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000818 builder_->environment()->Push(increment_);
819 builder_->current_block()->Goto(header_block_);
820 header_block_->loop_information()->RegisterBackEdge(body_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000821 header_block_->SetJoinId(id_);
822
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000823 builder_->set_current_block(exit_block_);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000824 // Pop the phi from the expression stack
825 builder_->environment()->Pop();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000826 finished_ = true;
827}
828
829
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000830HGraph* HGraphBuilder::CreateGraph() {
831 graph_ = new(zone()) HGraph(info_);
832 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info_);
833 HPhase phase("H_Block building");
834 set_current_block(graph()->entry_block());
835 if (!BuildGraph()) return NULL;
836 return graph_;
837}
838
839
840HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
841 ASSERT(current_block() != NULL);
842 current_block()->AddInstruction(instr);
843 return instr;
844}
845
846
847void HGraphBuilder::AddSimulate(BailoutId id,
848 RemovableSimulate removable) {
849 ASSERT(current_block() != NULL);
850 current_block()->AddSimulate(id, removable);
851}
852
853
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000854HBoundsCheck* HGraphBuilder::AddBoundsCheck(HValue* index,
855 HValue* length,
856 BoundsCheckKeyMode key_mode,
857 Representation r) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000858 if (!index->type().IsSmi()) {
859 index = new(graph()->zone()) HCheckSmiOrInt32(index);
860 AddInstruction(HCheckSmiOrInt32::cast(index));
861 }
862 if (!length->type().IsSmi()) {
863 length = new(graph()->zone()) HCheckSmiOrInt32(length);
864 AddInstruction(HCheckSmiOrInt32::cast(length));
865 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000866 HBoundsCheck* result = new(graph()->zone()) HBoundsCheck(
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000867 index, length, key_mode, r);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000868 AddInstruction(result);
869 return result;
870}
871
872
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000873HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
874 HBasicBlock* b = graph()->CreateBasicBlock();
875 b->SetInitialEnvironment(env);
876 return b;
877}
878
879
880HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
881 HBasicBlock* header = graph()->CreateBasicBlock();
882 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
883 header->SetInitialEnvironment(entry_env);
884 header->AttachLoopInformation();
885 return header;
886}
887
888
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000889HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
890 HValue* external_elements,
891 HValue* checked_key,
892 HValue* val,
893 HValue* dependency,
894 ElementsKind elements_kind,
895 bool is_store) {
896 Zone* zone = this->zone();
897 if (is_store) {
898 ASSERT(val != NULL);
899 switch (elements_kind) {
900 case EXTERNAL_PIXEL_ELEMENTS: {
901 val = AddInstruction(new(zone) HClampToUint8(val));
902 break;
903 }
904 case EXTERNAL_BYTE_ELEMENTS:
905 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
906 case EXTERNAL_SHORT_ELEMENTS:
907 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
908 case EXTERNAL_INT_ELEMENTS:
909 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
910 break;
911 }
912 case EXTERNAL_FLOAT_ELEMENTS:
913 case EXTERNAL_DOUBLE_ELEMENTS:
914 break;
915 case FAST_SMI_ELEMENTS:
916 case FAST_ELEMENTS:
917 case FAST_DOUBLE_ELEMENTS:
918 case FAST_HOLEY_SMI_ELEMENTS:
919 case FAST_HOLEY_ELEMENTS:
920 case FAST_HOLEY_DOUBLE_ELEMENTS:
921 case DICTIONARY_ELEMENTS:
922 case NON_STRICT_ARGUMENTS_ELEMENTS:
923 UNREACHABLE();
924 break;
925 }
926 return new(zone) HStoreKeyed(external_elements, checked_key,
927 val, elements_kind);
928 } else {
929 ASSERT(val == NULL);
930 HLoadKeyed* load =
931 new(zone) HLoadKeyed(
932 external_elements, checked_key, dependency, elements_kind);
933 if (FLAG_opt_safe_uint32_operations &&
934 elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
935 graph()->RecordUint32Instruction(load);
936 }
937 return load;
938 }
939}
940
941
942HInstruction* HGraphBuilder::BuildFastElementAccess(
943 HValue* elements,
944 HValue* checked_key,
945 HValue* val,
946 HValue* load_dependency,
947 ElementsKind elements_kind,
948 bool is_store) {
949 Zone* zone = this->zone();
950 if (is_store) {
951 ASSERT(val != NULL);
952 switch (elements_kind) {
953 case FAST_SMI_ELEMENTS:
954 case FAST_HOLEY_SMI_ELEMENTS:
955 // Smi-only arrays need a smi check.
956 AddInstruction(new(zone) HCheckSmi(val));
957 // Fall through.
958 case FAST_ELEMENTS:
959 case FAST_HOLEY_ELEMENTS:
960 case FAST_DOUBLE_ELEMENTS:
961 case FAST_HOLEY_DOUBLE_ELEMENTS:
962 return new(zone) HStoreKeyed(elements, checked_key, val, elements_kind);
963 default:
964 UNREACHABLE();
965 return NULL;
966 }
967 }
968 // It's an element load (!is_store).
969 return new(zone) HLoadKeyed(elements,
970 checked_key,
971 load_dependency,
972 elements_kind);
973}
974
975
976HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
977 HValue* object,
978 HValue* key,
979 HValue* val,
980 HCheckMaps* mapcheck,
981 bool is_js_array,
982 ElementsKind elements_kind,
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +0000983 bool is_store,
984 Representation checked_index_representation) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000985 Zone* zone = this->zone();
986 // No GVNFlag is necessary for ElementsKind if there is an explicit dependency
987 // on a HElementsTransition instruction. The flag can also be removed if the
988 // map to check has FAST_HOLEY_ELEMENTS, since there can be no further
989 // ElementsKind transitions. Finally, the dependency can be removed for stores
990 // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
991 // generated store code.
992 if ((elements_kind == FAST_HOLEY_ELEMENTS) ||
993 (elements_kind == FAST_ELEMENTS && is_store)) {
994 if (mapcheck != NULL) {
995 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
996 }
997 }
998 bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
999 bool fast_elements = IsFastObjectElementsKind(elements_kind);
1000 HInstruction* elements =
1001 AddInstruction(new(zone) HLoadElements(object, mapcheck));
1002 if (is_store && (fast_elements || fast_smi_only_elements)) {
1003 HCheckMaps* check_cow_map = new(zone) HCheckMaps(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001004 elements, graph()->isolate()->factory()->fixed_array_map(), zone);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001005 check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
1006 AddInstruction(check_cow_map);
1007 }
1008 HInstruction* length = NULL;
1009 HInstruction* checked_key = NULL;
1010 if (IsExternalArrayElementsKind(elements_kind)) {
1011 length = AddInstruction(new(zone) HFixedArrayBaseLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001012 checked_key = AddBoundsCheck(
1013 key, length, ALLOW_SMI_KEY, checked_index_representation);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001014 HLoadExternalArrayPointer* external_elements =
1015 new(zone) HLoadExternalArrayPointer(elements);
1016 AddInstruction(external_elements);
1017 return BuildExternalArrayElementAccess(
1018 external_elements, checked_key, val, mapcheck,
1019 elements_kind, is_store);
1020 }
1021 ASSERT(fast_smi_only_elements ||
1022 fast_elements ||
1023 IsFastDoubleElementsKind(elements_kind));
1024 if (is_js_array) {
1025 length = AddInstruction(new(zone) HJSArrayLength(object, mapcheck,
1026 HType::Smi()));
1027 } else {
1028 length = AddInstruction(new(zone) HFixedArrayBaseLength(elements));
1029 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001030 checked_key = AddBoundsCheck(
1031 key, length, ALLOW_SMI_KEY, checked_index_representation);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001032 return BuildFastElementAccess(elements, checked_key, val, mapcheck,
1033 elements_kind, is_store);
1034}
1035
1036
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001037HValue* HGraphBuilder::BuildAllocateElements(HContext* context,
1038 ElementsKind kind,
1039 HValue* capacity) {
1040 Zone* zone = this->zone();
1041
1042 int elements_size = IsFastDoubleElementsKind(kind)
1043 ? kDoubleSize : kPointerSize;
1044 HConstant* elements_size_value =
1045 new(zone) HConstant(elements_size, Representation::Integer32());
1046 AddInstruction(elements_size_value);
1047 HValue* mul = AddInstruction(
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001048 HMul::New(zone, context, capacity, elements_size_value));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001049 mul->ChangeRepresentation(Representation::Integer32());
1050 mul->ClearFlag(HValue::kCanOverflow);
1051
1052 HConstant* header_size =
1053 new(zone) HConstant(FixedArray::kHeaderSize, Representation::Integer32());
1054 AddInstruction(header_size);
1055 HValue* total_size = AddInstruction(
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001056 HAdd::New(zone, context, mul, header_size));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001057 total_size->ChangeRepresentation(Representation::Integer32());
1058 total_size->ClearFlag(HValue::kCanOverflow);
1059
1060 HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE;
1061 if (IsFastDoubleElementsKind(kind)) {
1062 flags = static_cast<HAllocate::Flags>(
1063 flags | HAllocate::ALLOCATE_DOUBLE_ALIGNED);
1064 }
1065
1066 HValue* elements =
1067 AddInstruction(new(zone) HAllocate(context, total_size,
1068 HType::JSArray(), flags));
1069 Isolate* isolate = graph()->isolate();
1070
1071 Factory* factory = isolate->factory();
1072 Handle<Map> map = IsFastDoubleElementsKind(kind)
1073 ? factory->fixed_double_array_map()
1074 : factory->fixed_array_map();
1075 BuildStoreMap(elements, map, BailoutId::StubEntry());
1076
1077 Handle<String> fixed_array_length_field_name =
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001078 isolate->factory()->length_field_string();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001079 HInstruction* store_length =
1080 new(zone) HStoreNamedField(elements, fixed_array_length_field_name,
1081 capacity, true, FixedArray::kLengthOffset);
1082 AddInstruction(store_length);
1083 AddSimulate(BailoutId::StubEntry(), FIXED_SIMULATE);
1084
1085 return elements;
1086}
1087
1088
1089HInstruction* HGraphBuilder::BuildStoreMap(HValue* object,
1090 HValue* map,
1091 BailoutId id) {
1092 Zone* zone = this->zone();
1093 Isolate* isolate = graph()->isolate();
1094 Factory* factory = isolate->factory();
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001095 Handle<String> map_field_name = factory->map_field_string();
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001096 HInstruction* store_map =
1097 new(zone) HStoreNamedField(object, map_field_name, map,
1098 true, JSObject::kMapOffset);
1099 store_map->SetGVNFlag(kChangesMaps);
1100 AddInstruction(store_map);
1101 AddSimulate(id, FIXED_SIMULATE);
1102 return store_map;
1103}
1104
1105
1106HInstruction* HGraphBuilder::BuildStoreMap(HValue* object,
1107 Handle<Map> map,
1108 BailoutId id) {
1109 Zone* zone = this->zone();
1110 HValue* map_constant =
1111 AddInstruction(new(zone) HConstant(map, Representation::Tagged()));
1112 return BuildStoreMap(object, map_constant, id);
1113}
1114
1115
1116void HGraphBuilder::BuildCopyElements(HContext* context,
1117 HValue* from_elements,
1118 ElementsKind from_elements_kind,
1119 HValue* to_elements,
1120 ElementsKind to_elements_kind,
1121 HValue* length) {
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001122 LoopBuilder builder(this, context, LoopBuilder::kPostIncrement,
1123 BailoutId::StubEntry());
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00001124
1125 HValue* key = builder.BeginBody(graph()->GetConstant0(),
1126 length, Token::LT);
1127
1128 HValue* element =
1129 AddInstruction(new(zone()) HLoadKeyed(from_elements, key, NULL,
1130 from_elements_kind,
1131 ALLOW_RETURN_HOLE));
1132
1133 AddInstruction(new(zone()) HStoreKeyed(to_elements, key, element,
1134 to_elements_kind));
1135 AddSimulate(BailoutId::StubEntry(), REMOVABLE_SIMULATE);
1136
1137 builder.EndBody();
1138}
1139
1140
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001141HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info,
1142 TypeFeedbackOracle* oracle)
1143 : HGraphBuilder(info),
1144 function_state_(NULL),
ulan@chromium.org967e2702012-02-28 09:49:15 +00001145 initial_function_state_(this, info, oracle, NORMAL_RETURN),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001146 ast_context_(NULL),
1147 break_scope_(NULL),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001148 inlined_count_(0),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001149 globals_(10, info->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001150 inline_bailout_(false) {
1151 // This is not initialized in the initializer list because the
1152 // constructor for the initial state relies on function_state_ == NULL
1153 // to know it's the initial state.
1154 function_state_= &initial_function_state_;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001155 InitializeAstVisitor();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001156}
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001157
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001158
1159HBasicBlock* HOptimizedGraphBuilder::CreateJoin(HBasicBlock* first,
1160 HBasicBlock* second,
1161 BailoutId join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001162 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001163 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001164 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001165 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001166 } else {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001167 HBasicBlock* join_block = graph()->CreateBasicBlock();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001168 first->Goto(join_block);
1169 second->Goto(join_block);
1170 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001171 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001172 }
1173}
1174
1175
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001176HBasicBlock* HOptimizedGraphBuilder::JoinContinue(IterationStatement* statement,
1177 HBasicBlock* exit_block,
1178 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001179 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001180 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001181 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001182 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001183 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001184 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001185}
1186
1187
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001188HBasicBlock* HOptimizedGraphBuilder::CreateLoop(IterationStatement* statement,
1189 HBasicBlock* loop_entry,
1190 HBasicBlock* body_exit,
1191 HBasicBlock* loop_successor,
1192 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +00001193 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001194 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001195 if (break_block != NULL) {
1196 if (loop_successor != NULL) loop_successor->Goto(break_block);
1197 break_block->SetJoinId(statement->ExitId());
1198 return break_block;
1199 }
1200 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001201}
1202
1203
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00001204void HBasicBlock::FinishExit(HControlInstruction* instruction) {
1205 Finish(instruction);
1206 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001207}
1208
1209
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001210HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001211 : isolate_(info->isolate()),
1212 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001213 entry_block_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001214 blocks_(8, info->zone()),
1215 values_(16, info->zone()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001216 phi_list_(NULL),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001217 uint32_instructions_(NULL),
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001218 info_(info),
1219 zone_(info->zone()),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001220 is_recursive_(false),
1221 use_optimistic_licm_(false),
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001222 has_soft_deoptimize_(false),
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001223 type_change_checksum_(0) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001224 if (info->IsStub()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001225 HydrogenCodeStub* stub = info->code_stub();
1226 int param_count =
1227 stub->GetInterfaceDescriptor(isolate_)->register_param_count_;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001228 start_environment_ =
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001229 new(zone_) HEnvironment(zone_, param_count);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001230 } else {
1231 start_environment_ =
1232 new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
1233 }
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00001234 start_environment_->set_ast_id(BailoutId::FunctionEntry());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001235 entry_block_ = CreateBasicBlock();
1236 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001237}
1238
1239
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001240HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001241 HBasicBlock* result = new(zone()) HBasicBlock(this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001242 blocks_.Add(result, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001243 return result;
1244}
1245
1246
1247void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001248 if (!FLAG_use_canonicalizing) return;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001249 HPhase phase("H_Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001250 for (int i = 0; i < blocks()->length(); ++i) {
1251 HInstruction* instr = blocks()->at(i)->first();
1252 while (instr != NULL) {
1253 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001254 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001255 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001256 }
1257 }
1258}
1259
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001260// Block ordering was implemented with two mutually recursive methods,
1261// HGraph::Postorder and HGraph::PostorderLoopBlocks.
1262// The recursion could lead to stack overflow so the algorithm has been
1263// implemented iteratively.
1264// At a high level the algorithm looks like this:
1265//
1266// Postorder(block, loop_header) : {
1267// if (block has already been visited or is of another loop) return;
1268// mark block as visited;
1269// if (block is a loop header) {
1270// VisitLoopMembers(block, loop_header);
1271// VisitSuccessorsOfLoopHeader(block);
1272// } else {
1273// VisitSuccessors(block)
1274// }
1275// put block in result list;
1276// }
1277//
1278// VisitLoopMembers(block, outer_loop_header) {
1279// foreach (block b in block loop members) {
1280// VisitSuccessorsOfLoopMember(b, outer_loop_header);
1281// if (b is loop header) VisitLoopMembers(b);
1282// }
1283// }
1284//
1285// VisitSuccessorsOfLoopMember(block, outer_loop_header) {
1286// foreach (block b in block successors) Postorder(b, outer_loop_header)
1287// }
1288//
1289// VisitSuccessorsOfLoopHeader(block) {
1290// foreach (block b in block successors) Postorder(b, block)
1291// }
1292//
1293// VisitSuccessors(block, loop_header) {
1294// foreach (block b in block successors) Postorder(b, loop_header)
1295// }
1296//
1297// The ordering is started calling Postorder(entry, NULL).
1298//
1299// Each instance of PostorderProcessor represents the "stack frame" of the
1300// recursion, and particularly keeps the state of the loop (iteration) of the
1301// "Visit..." function it represents.
1302// To recycle memory we keep all the frames in a double linked list but
1303// this means that we cannot use constructors to initialize the frames.
1304//
1305class PostorderProcessor : public ZoneObject {
1306 public:
1307 // Back link (towards the stack bottom).
1308 PostorderProcessor* parent() {return father_; }
1309 // Forward link (towards the stack top).
1310 PostorderProcessor* child() {return child_; }
1311 HBasicBlock* block() { return block_; }
1312 HLoopInformation* loop() { return loop_; }
1313 HBasicBlock* loop_header() { return loop_header_; }
1314
1315 static PostorderProcessor* CreateEntryProcessor(Zone* zone,
1316 HBasicBlock* block,
1317 BitVector* visited) {
1318 PostorderProcessor* result = new(zone) PostorderProcessor(NULL);
1319 return result->SetupSuccessors(zone, block, NULL, visited);
1320 }
1321
1322 PostorderProcessor* PerformStep(Zone* zone,
1323 BitVector* visited,
1324 ZoneList<HBasicBlock*>* order) {
1325 PostorderProcessor* next =
1326 PerformNonBacktrackingStep(zone, visited, order);
1327 if (next != NULL) {
1328 return next;
1329 } else {
1330 return Backtrack(zone, visited, order);
1331 }
1332 }
1333
1334 private:
1335 explicit PostorderProcessor(PostorderProcessor* father)
1336 : father_(father), child_(NULL), successor_iterator(NULL) { }
1337
1338 // Each enum value states the cycle whose state is kept by this instance.
1339 enum LoopKind {
1340 NONE,
1341 SUCCESSORS,
1342 SUCCESSORS_OF_LOOP_HEADER,
1343 LOOP_MEMBERS,
1344 SUCCESSORS_OF_LOOP_MEMBER
1345 };
1346
1347 // Each "Setup..." method is like a constructor for a cycle state.
1348 PostorderProcessor* SetupSuccessors(Zone* zone,
1349 HBasicBlock* block,
1350 HBasicBlock* loop_header,
1351 BitVector* visited) {
1352 if (block == NULL || visited->Contains(block->block_id()) ||
1353 block->parent_loop_header() != loop_header) {
1354 kind_ = NONE;
1355 block_ = NULL;
1356 loop_ = NULL;
1357 loop_header_ = NULL;
1358 return this;
1359 } else {
1360 block_ = block;
1361 loop_ = NULL;
1362 visited->Add(block->block_id());
1363
1364 if (block->IsLoopHeader()) {
1365 kind_ = SUCCESSORS_OF_LOOP_HEADER;
1366 loop_header_ = block;
1367 InitializeSuccessors();
1368 PostorderProcessor* result = Push(zone);
1369 return result->SetupLoopMembers(zone, block, block->loop_information(),
1370 loop_header);
1371 } else {
1372 ASSERT(block->IsFinished());
1373 kind_ = SUCCESSORS;
1374 loop_header_ = loop_header;
1375 InitializeSuccessors();
1376 return this;
1377 }
1378 }
1379 }
1380
1381 PostorderProcessor* SetupLoopMembers(Zone* zone,
1382 HBasicBlock* block,
1383 HLoopInformation* loop,
1384 HBasicBlock* loop_header) {
1385 kind_ = LOOP_MEMBERS;
1386 block_ = block;
1387 loop_ = loop;
1388 loop_header_ = loop_header;
1389 InitializeLoopMembers();
1390 return this;
1391 }
1392
1393 PostorderProcessor* SetupSuccessorsOfLoopMember(
1394 HBasicBlock* block,
1395 HLoopInformation* loop,
1396 HBasicBlock* loop_header) {
1397 kind_ = SUCCESSORS_OF_LOOP_MEMBER;
1398 block_ = block;
1399 loop_ = loop;
1400 loop_header_ = loop_header;
1401 InitializeSuccessors();
1402 return this;
1403 }
1404
1405 // This method "allocates" a new stack frame.
1406 PostorderProcessor* Push(Zone* zone) {
1407 if (child_ == NULL) {
1408 child_ = new(zone) PostorderProcessor(this);
1409 }
1410 return child_;
1411 }
1412
1413 void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) {
1414 ASSERT(block_->end()->FirstSuccessor() == NULL ||
1415 order->Contains(block_->end()->FirstSuccessor()) ||
1416 block_->end()->FirstSuccessor()->IsLoopHeader());
1417 ASSERT(block_->end()->SecondSuccessor() == NULL ||
1418 order->Contains(block_->end()->SecondSuccessor()) ||
1419 block_->end()->SecondSuccessor()->IsLoopHeader());
1420 order->Add(block_, zone);
1421 }
1422
1423 // This method is the basic block to walk up the stack.
1424 PostorderProcessor* Pop(Zone* zone,
1425 BitVector* visited,
1426 ZoneList<HBasicBlock*>* order) {
1427 switch (kind_) {
1428 case SUCCESSORS:
1429 case SUCCESSORS_OF_LOOP_HEADER:
1430 ClosePostorder(order, zone);
1431 return father_;
1432 case LOOP_MEMBERS:
1433 return father_;
1434 case SUCCESSORS_OF_LOOP_MEMBER:
1435 if (block()->IsLoopHeader() && block() != loop_->loop_header()) {
1436 // In this case we need to perform a LOOP_MEMBERS cycle so we
1437 // initialize it and return this instead of father.
1438 return SetupLoopMembers(zone, block(),
1439 block()->loop_information(), loop_header_);
1440 } else {
1441 return father_;
1442 }
1443 case NONE:
1444 return father_;
1445 }
1446 UNREACHABLE();
1447 return NULL;
1448 }
1449
1450 // Walks up the stack.
1451 PostorderProcessor* Backtrack(Zone* zone,
1452 BitVector* visited,
1453 ZoneList<HBasicBlock*>* order) {
1454 PostorderProcessor* parent = Pop(zone, visited, order);
1455 while (parent != NULL) {
1456 PostorderProcessor* next =
1457 parent->PerformNonBacktrackingStep(zone, visited, order);
1458 if (next != NULL) {
1459 return next;
1460 } else {
1461 parent = parent->Pop(zone, visited, order);
1462 }
1463 }
1464 return NULL;
1465 }
1466
1467 PostorderProcessor* PerformNonBacktrackingStep(
1468 Zone* zone,
1469 BitVector* visited,
1470 ZoneList<HBasicBlock*>* order) {
1471 HBasicBlock* next_block;
1472 switch (kind_) {
1473 case SUCCESSORS:
1474 next_block = AdvanceSuccessors();
1475 if (next_block != NULL) {
1476 PostorderProcessor* result = Push(zone);
1477 return result->SetupSuccessors(zone, next_block,
1478 loop_header_, visited);
1479 }
1480 break;
1481 case SUCCESSORS_OF_LOOP_HEADER:
1482 next_block = AdvanceSuccessors();
1483 if (next_block != NULL) {
1484 PostorderProcessor* result = Push(zone);
1485 return result->SetupSuccessors(zone, next_block,
1486 block(), visited);
1487 }
1488 break;
1489 case LOOP_MEMBERS:
1490 next_block = AdvanceLoopMembers();
1491 if (next_block != NULL) {
1492 PostorderProcessor* result = Push(zone);
1493 return result->SetupSuccessorsOfLoopMember(next_block,
1494 loop_, loop_header_);
1495 }
1496 break;
1497 case SUCCESSORS_OF_LOOP_MEMBER:
1498 next_block = AdvanceSuccessors();
1499 if (next_block != NULL) {
1500 PostorderProcessor* result = Push(zone);
1501 return result->SetupSuccessors(zone, next_block,
1502 loop_header_, visited);
1503 }
1504 break;
1505 case NONE:
1506 return NULL;
1507 }
1508 return NULL;
1509 }
1510
1511 // The following two methods implement a "foreach b in successors" cycle.
1512 void InitializeSuccessors() {
1513 loop_index = 0;
1514 loop_length = 0;
1515 successor_iterator = HSuccessorIterator(block_->end());
1516 }
1517
1518 HBasicBlock* AdvanceSuccessors() {
1519 if (!successor_iterator.Done()) {
1520 HBasicBlock* result = successor_iterator.Current();
1521 successor_iterator.Advance();
1522 return result;
1523 }
1524 return NULL;
1525 }
1526
1527 // The following two methods implement a "foreach b in loop members" cycle.
1528 void InitializeLoopMembers() {
1529 loop_index = 0;
1530 loop_length = loop_->blocks()->length();
1531 }
1532
1533 HBasicBlock* AdvanceLoopMembers() {
1534 if (loop_index < loop_length) {
1535 HBasicBlock* result = loop_->blocks()->at(loop_index);
1536 loop_index++;
1537 return result;
1538 } else {
1539 return NULL;
1540 }
1541 }
1542
1543 LoopKind kind_;
1544 PostorderProcessor* father_;
1545 PostorderProcessor* child_;
1546 HLoopInformation* loop_;
1547 HBasicBlock* block_;
1548 HBasicBlock* loop_header_;
1549 int loop_index;
1550 int loop_length;
1551 HSuccessorIterator successor_iterator;
1552};
1553
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001554
1555void HGraph::OrderBlocks() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001556 HPhase phase("H_Block ordering");
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001557 BitVector visited(blocks_.length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001558
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001559 ZoneList<HBasicBlock*> reverse_result(8, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001560 HBasicBlock* start = blocks_[0];
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001561 PostorderProcessor* postorder =
1562 PostorderProcessor::CreateEntryProcessor(zone(), start, &visited);
1563 while (postorder != NULL) {
1564 postorder = postorder->PerformStep(zone(), &visited, &reverse_result);
1565 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001566 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001567 int index = 0;
1568 for (int i = reverse_result.length() - 1; i >= 0; --i) {
1569 HBasicBlock* b = reverse_result[i];
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001570 blocks_.Add(b, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001571 b->set_block_id(index++);
1572 }
1573}
1574
1575
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001576void HGraph::AssignDominators() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001577 HPhase phase("H_Assign dominators", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001578 for (int i = 0; i < blocks_.length(); ++i) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001579 HBasicBlock* block = blocks_[i];
1580 if (block->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001581 // Only the first predecessor of a loop header is from outside the loop.
1582 // All others are back edges, and thus cannot dominate the loop header.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001583 block->AssignCommonDominator(block->predecessors()->first());
1584 block->AssignLoopSuccessorDominators();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001585 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00001586 for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001587 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
1588 }
1589 }
1590 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001591}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001592
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001593
ricow@chromium.org2c99e282011-07-28 09:15:17 +00001594// Mark all blocks that are dominated by an unconditional soft deoptimize to
1595// prevent code motion across those blocks.
1596void HGraph::PropagateDeoptimizingMark() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001597 HPhase phase("H_Propagate deoptimizing mark", this);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001598 // Skip this phase if there is nothing to be done anyway.
1599 if (!has_soft_deoptimize()) return;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001600 MarkAsDeoptimizingRecursively(entry_block());
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001601 NullifyUnreachableInstructions();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001602}
1603
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001604
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001605void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
1606 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1607 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1608 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
1609 MarkAsDeoptimizingRecursively(dominated);
1610 }
1611}
1612
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001613
1614void HGraph::NullifyUnreachableInstructions() {
yangguo@chromium.org28381b42013-01-21 14:39:38 +00001615 if (!FLAG_unreachable_code_elimination) return;
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00001616 int block_count = blocks_.length();
1617 for (int i = 0; i < block_count; ++i) {
1618 HBasicBlock* block = blocks_.at(i);
1619 bool nullify = false;
1620 const ZoneList<HBasicBlock*>* predecessors = block->predecessors();
1621 int predecessors_length = predecessors->length();
1622 bool all_predecessors_deoptimizing = (predecessors_length > 0);
1623 for (int j = 0; j < predecessors_length; ++j) {
1624 if (!predecessors->at(j)->IsDeoptimizing()) {
1625 all_predecessors_deoptimizing = false;
1626 break;
1627 }
1628 }
1629 if (all_predecessors_deoptimizing) nullify = true;
1630 for (HInstruction* instr = block->first(); instr != NULL;
1631 instr = instr->next()) {
1632 // Leave the basic structure of the graph intact.
1633 if (instr->IsBlockEntry()) continue;
1634 if (instr->IsControlInstruction()) continue;
1635 if (instr->IsSimulate()) continue;
1636 if (instr->IsEnterInlined()) continue;
1637 if (instr->IsLeaveInlined()) continue;
1638 if (nullify) {
1639 HInstruction* last_dummy = NULL;
1640 for (int j = 0; j < instr->OperandCount(); ++j) {
1641 HValue* operand = instr->OperandAt(j);
1642 // Insert an HDummyUse for each operand, unless the operand
1643 // is an HDummyUse itself. If it's even from the same block,
1644 // remember it as a potential replacement for the instruction.
1645 if (operand->IsDummyUse()) {
1646 if (operand->block() == instr->block() &&
1647 last_dummy == NULL) {
1648 last_dummy = HInstruction::cast(operand);
1649 }
1650 continue;
1651 }
1652 if (operand->IsControlInstruction()) {
1653 // Inserting a dummy use for a value that's not defined anywhere
1654 // will fail. Some instructions define fake inputs on such
1655 // values as control flow dependencies.
1656 continue;
1657 }
1658 HDummyUse* dummy = new(zone()) HDummyUse(operand);
1659 dummy->InsertBefore(instr);
1660 last_dummy = dummy;
1661 }
1662 if (last_dummy == NULL) last_dummy = GetConstant1();
1663 instr->DeleteAndReplaceWith(last_dummy);
1664 continue;
1665 }
1666 if (instr->IsSoftDeoptimize()) {
1667 ASSERT(block->IsDeoptimizing());
1668 nullify = true;
1669 }
1670 }
1671 }
1672}
1673
1674
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001675// Replace all phis consisting of a single non-loop operand plus any number of
1676// loop operands by that single non-loop operand.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001677void HGraph::EliminateRedundantPhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001678 HPhase phase("H_Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001679
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001680 // We do a simple fixed point iteration without any work list, because
1681 // machine-generated JavaScript can lead to a very dense Hydrogen graph with
1682 // an enormous work list and will consequently result in OOM. Experiments
1683 // showed that this simple algorithm is good enough, and even e.g. tracking
1684 // the set or range of blocks to consider is not a real improvement.
1685 bool need_another_iteration;
1686 ZoneList<HPhi*> redundant_phis(blocks_.length(), zone());
1687 do {
1688 need_another_iteration = false;
1689 for (int i = 0; i < blocks_.length(); ++i) {
1690 HBasicBlock* block = blocks_[i];
1691 for (int j = 0; j < block->phis()->length(); j++) {
1692 HPhi* phi = block->phis()->at(j);
1693 HValue* replacement = phi->GetRedundantReplacement();
1694 if (replacement != NULL) {
1695 // Remember phi to avoid concurrent modification of the block's phis.
1696 redundant_phis.Add(phi, zone());
1697 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1698 HValue* value = it.value();
1699 value->SetOperandAt(it.index(), replacement);
1700 need_another_iteration |= value->IsPhi();
1701 }
1702 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001703 }
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001704 for (int i = 0; i < redundant_phis.length(); i++) {
1705 block->RemovePhi(redundant_phis[i]);
1706 }
1707 redundant_phis.Clear();
1708 }
1709 } while (need_another_iteration);
1710
1711#if DEBUG
1712 // Make sure that we *really* removed all redundant phis.
1713 for (int i = 0; i < blocks_.length(); ++i) {
1714 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
1715 ASSERT(blocks_[i]->phis()->at(j)->GetRedundantReplacement() == NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001716 }
1717 }
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001718#endif
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001719}
1720
1721
1722void HGraph::EliminateUnreachablePhis() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001723 HPhase phase("H_Unreachable phi elimination", this);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001724
1725 // Initialize worklist.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001726 ZoneList<HPhi*> phi_list(blocks_.length(), zone());
1727 ZoneList<HPhi*> worklist(blocks_.length(), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001728 for (int i = 0; i < blocks_.length(); ++i) {
1729 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
1730 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001731 phi_list.Add(phi, zone());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001732 // We can't eliminate phis in the receiver position in the environment
1733 // because in case of throwing an error we need this value to
1734 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001735 if (phi->HasRealUses() || phi->IsReceiver()) {
1736 phi->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001737 worklist.Add(phi, zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001738 }
1739 }
1740 }
1741
1742 // Iteratively mark live phis.
1743 while (!worklist.is_empty()) {
1744 HPhi* phi = worklist.RemoveLast();
1745 for (int i = 0; i < phi->OperandCount(); i++) {
1746 HValue* operand = phi->OperandAt(i);
1747 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
1748 HPhi::cast(operand)->set_is_live(true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001749 worklist.Add(HPhi::cast(operand), zone());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001750 }
1751 }
1752 }
1753
1754 // Remove unreachable phis.
1755 for (int i = 0; i < phi_list.length(); i++) {
1756 HPhi* phi = phi_list[i];
1757 if (!phi->is_live()) {
1758 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001759 block->RemovePhi(phi);
1760 block->RecordDeletedPhi(phi->merged_index());
1761 }
1762 }
1763}
1764
1765
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001766bool HGraph::CheckArgumentsPhiUses() {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00001767 int block_count = blocks_.length();
1768 for (int i = 0; i < block_count; ++i) {
1769 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1770 HPhi* phi = blocks_[i]->phis()->at(j);
1771 // We don't support phi uses of arguments for now.
1772 if (phi->CheckFlag(HValue::kIsArguments)) return false;
1773 }
1774 }
1775 return true;
1776}
1777
1778
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001779bool HGraph::CheckConstPhiUses() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001780 int block_count = blocks_.length();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001781 for (int i = 0; i < block_count; ++i) {
1782 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1783 HPhi* phi = blocks_[i]->phis()->at(j);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001784 // Check for the hole value (from an uninitialized const).
1785 for (int k = 0; k < phi->OperandCount(); k++) {
1786 if (phi->OperandAt(k) == GetConstantHole()) return false;
1787 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001788 }
1789 }
1790 return true;
1791}
1792
1793
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001794void HGraph::CollectPhis() {
1795 int block_count = blocks_.length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001796 phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001797 for (int i = 0; i < block_count; ++i) {
1798 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
1799 HPhi* phi = blocks_[i]->phis()->at(j);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001800 phi_list_->Add(phi, zone());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001801 }
1802 }
1803}
1804
1805
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001806void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001807 BitVector in_worklist(GetMaximumValueID(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001808 for (int i = 0; i < worklist->length(); ++i) {
1809 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
1810 in_worklist.Add(worklist->at(i)->id());
1811 }
1812
1813 while (!worklist->is_empty()) {
1814 HValue* current = worklist->RemoveLast();
1815 in_worklist.Remove(current->id());
1816 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001817 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
1818 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001819 if (!in_worklist.Contains(use->id())) {
1820 in_worklist.Add(use->id());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001821 worklist->Add(use, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001822 }
1823 }
1824 }
1825 }
1826}
1827
1828
1829class HRangeAnalysis BASE_EMBEDDED {
1830 public:
ulan@chromium.org812308e2012-02-29 15:58:45 +00001831 explicit HRangeAnalysis(HGraph* graph) :
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001832 graph_(graph), zone_(graph->zone()), changed_ranges_(16, zone_) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001833
1834 void Analyze();
1835
1836 private:
1837 void TraceRange(const char* msg, ...);
1838 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001839 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
1840 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001841 void InferRange(HValue* value);
1842 void RollBackTo(int index);
1843 void AddRange(HValue* value, Range* range);
1844
1845 HGraph* graph_;
ulan@chromium.org812308e2012-02-29 15:58:45 +00001846 Zone* zone_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001847 ZoneList<HValue*> changed_ranges_;
1848};
1849
1850
1851void HRangeAnalysis::TraceRange(const char* msg, ...) {
1852 if (FLAG_trace_range) {
1853 va_list arguments;
1854 va_start(arguments, msg);
1855 OS::VPrint(msg, arguments);
1856 va_end(arguments);
1857 }
1858}
1859
1860
1861void HRangeAnalysis::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00001862 HPhase phase("H_Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001863 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001864}
1865
1866
1867void HRangeAnalysis::Analyze(HBasicBlock* block) {
1868 TraceRange("Analyzing block B%d\n", block->block_id());
1869
1870 int last_changed_range = changed_ranges_.length() - 1;
1871
1872 // Infer range based on control flow.
1873 if (block->predecessors()->length() == 1) {
1874 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001875 if (pred->end()->IsCompareIDAndBranch()) {
1876 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001877 }
1878 }
1879
1880 // Process phi instructions.
1881 for (int i = 0; i < block->phis()->length(); ++i) {
1882 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001883 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001884 }
1885
1886 // Go through all instructions of the current block.
1887 HInstruction* instr = block->first();
1888 while (instr != block->end()) {
1889 InferRange(instr);
1890 instr = instr->next();
1891 }
1892
1893 // Continue analysis in all dominated blocks.
1894 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
1895 Analyze(block->dominated_blocks()->at(i));
1896 }
1897
1898 RollBackTo(last_changed_range);
1899}
1900
1901
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001902void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
1903 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001904 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001905 if (test->representation().IsInteger32()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001906 Token::Value op = test->token();
1907 if (test->SecondSuccessor() == dest) {
1908 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001909 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001910 Token::Value inverted_op = Token::ReverseCompareOp(op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001911 UpdateControlFlowRange(op, test->left(), test->right());
1912 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001913 }
1914}
1915
1916
1917// We know that value [op] other. Use this information to update the range on
1918// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001919void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
1920 HValue* value,
1921 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001922 Range temp_range;
1923 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001924 Range* new_range = NULL;
1925
1926 TraceRange("Control flow range infer %d %s %d\n",
1927 value->id(),
1928 Token::Name(op),
1929 other->id());
1930
1931 if (op == Token::EQ || op == Token::EQ_STRICT) {
1932 // The same range has to apply for value.
ulan@chromium.org812308e2012-02-29 15:58:45 +00001933 new_range = range->Copy(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001934 } else if (op == Token::LT || op == Token::LTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001935 new_range = range->CopyClearLower(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001936 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001937 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001938 }
1939 } else if (op == Token::GT || op == Token::GTE) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001940 new_range = range->CopyClearUpper(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001941 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001942 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001943 }
1944 }
1945
1946 if (new_range != NULL && !new_range->IsMostGeneric()) {
1947 AddRange(value, new_range);
1948 }
1949}
1950
1951
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001952void HRangeAnalysis::InferRange(HValue* value) {
1953 ASSERT(!value->HasRange());
1954 if (!value->representation().IsNone()) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00001955 value->ComputeInitialRange(zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001956 Range* range = value->range();
1957 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1958 value->id(),
1959 value->Mnemonic(),
1960 range->lower(),
1961 range->upper());
1962 }
1963}
1964
1965
1966void HRangeAnalysis::RollBackTo(int index) {
1967 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1968 changed_ranges_[i]->RemoveLastAddedRange();
1969 }
1970 changed_ranges_.Rewind(index + 1);
1971}
1972
1973
1974void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1975 Range* original_range = value->range();
ulan@chromium.org812308e2012-02-29 15:58:45 +00001976 value->AddNewRange(range, zone_);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001977 changed_ranges_.Add(value, zone_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001978 Range* new_range = value->range();
1979 TraceRange("Updated range of %d set to [%d,%d]\n",
1980 value->id(),
1981 new_range->lower(),
1982 new_range->upper());
1983 if (original_range != NULL) {
1984 TraceRange("Original range was [%d,%d]\n",
1985 original_range->lower(),
1986 original_range->upper());
1987 }
1988 TraceRange("New information was [%d,%d]\n",
1989 range->lower(),
1990 range->upper());
1991}
1992
1993
1994void TraceGVN(const char* msg, ...) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00001995 va_list arguments;
1996 va_start(arguments, msg);
1997 OS::VPrint(msg, arguments);
1998 va_end(arguments);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001999}
2000
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002001// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when
2002// --trace-gvn is off.
2003#define TRACE_GVN_1(msg, a1) \
2004 if (FLAG_trace_gvn) { \
2005 TraceGVN(msg, a1); \
2006 }
2007
2008#define TRACE_GVN_2(msg, a1, a2) \
2009 if (FLAG_trace_gvn) { \
2010 TraceGVN(msg, a1, a2); \
2011 }
2012
2013#define TRACE_GVN_3(msg, a1, a2, a3) \
2014 if (FLAG_trace_gvn) { \
2015 TraceGVN(msg, a1, a2, a3); \
2016 }
2017
2018#define TRACE_GVN_4(msg, a1, a2, a3, a4) \
2019 if (FLAG_trace_gvn) { \
2020 TraceGVN(msg, a1, a2, a3, a4); \
2021 }
2022
2023#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \
2024 if (FLAG_trace_gvn) { \
2025 TraceGVN(msg, a1, a2, a3, a4, a5); \
2026 }
2027
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002028
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002029HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002030 : array_size_(other->array_size_),
2031 lists_size_(other->lists_size_),
2032 count_(other->count_),
2033 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002034 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
2035 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002036 free_list_head_(other->free_list_head_) {
2037 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
2038 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
2039}
2040
2041
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002042void HValueMap::Kill(GVNFlagSet flags) {
2043 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags);
2044 if (!present_flags_.ContainsAnyOf(depends_flags)) return;
2045 present_flags_.RemoveAll();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002046 for (int i = 0; i < array_size_; ++i) {
2047 HValue* value = array_[i].value;
2048 if (value != NULL) {
2049 // Clear list of collisions first, so we know if it becomes empty.
2050 int kept = kNil; // List of kept elements.
2051 int next;
2052 for (int current = array_[i].next; current != kNil; current = next) {
2053 next = lists_[current].next;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002054 HValue* value = lists_[current].value;
2055 if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002056 // Drop it.
2057 count_--;
2058 lists_[current].next = free_list_head_;
2059 free_list_head_ = current;
2060 } else {
2061 // Keep it.
2062 lists_[current].next = kept;
2063 kept = current;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002064 present_flags_.Add(value->gvn_flags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002065 }
2066 }
2067 array_[i].next = kept;
2068
2069 // Now possibly drop directly indexed element.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002070 value = array_[i].value;
2071 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002072 count_--;
2073 int head = array_[i].next;
2074 if (head == kNil) {
2075 array_[i].value = NULL;
2076 } else {
2077 array_[i].value = lists_[head].value;
2078 array_[i].next = lists_[head].next;
2079 lists_[head].next = free_list_head_;
2080 free_list_head_ = head;
2081 }
2082 } else {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002083 present_flags_.Add(value->gvn_flags()); // Keep it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002084 }
2085 }
2086 }
2087}
2088
2089
2090HValue* HValueMap::Lookup(HValue* value) const {
2091 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
2092 uint32_t pos = Bound(hash);
2093 if (array_[pos].value != NULL) {
2094 if (array_[pos].value->Equals(value)) return array_[pos].value;
2095 int next = array_[pos].next;
2096 while (next != kNil) {
2097 if (lists_[next].value->Equals(value)) return lists_[next].value;
2098 next = lists_[next].next;
2099 }
2100 }
2101 return NULL;
2102}
2103
2104
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002105void HValueMap::Resize(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002106 ASSERT(new_size > count_);
2107 // Hashing the values into the new array has no more collisions than in the
2108 // old hash map, so we can use the existing lists_ array, if we are careful.
2109
2110 // Make sure we have at least one free element.
2111 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002112 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002113 }
2114
2115 HValueMapListElement* new_array =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002116 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002117 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
2118
2119 HValueMapListElement* old_array = array_;
2120 int old_size = array_size_;
2121
2122 int old_count = count_;
2123 count_ = 0;
2124 // Do not modify present_flags_. It is currently correct.
2125 array_size_ = new_size;
2126 array_ = new_array;
2127
2128 if (old_array != NULL) {
2129 // Iterate over all the elements in lists, rehashing them.
2130 for (int i = 0; i < old_size; ++i) {
2131 if (old_array[i].value != NULL) {
2132 int current = old_array[i].next;
2133 while (current != kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002134 Insert(lists_[current].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002135 int next = lists_[current].next;
2136 lists_[current].next = free_list_head_;
2137 free_list_head_ = current;
2138 current = next;
2139 }
2140 // Rehash the directly stored value.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002141 Insert(old_array[i].value, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002142 }
2143 }
2144 }
2145 USE(old_count);
2146 ASSERT(count_ == old_count);
2147}
2148
2149
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002150void HValueMap::ResizeLists(int new_size, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002151 ASSERT(new_size > lists_size_);
2152
2153 HValueMapListElement* new_lists =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002154 zone->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002155 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
2156
2157 HValueMapListElement* old_lists = lists_;
2158 int old_size = lists_size_;
2159
2160 lists_size_ = new_size;
2161 lists_ = new_lists;
2162
2163 if (old_lists != NULL) {
2164 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
2165 }
2166 for (int i = old_size; i < lists_size_; ++i) {
2167 lists_[i].next = free_list_head_;
2168 free_list_head_ = i;
2169 }
2170}
2171
2172
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002173void HValueMap::Insert(HValue* value, Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002174 ASSERT(value != NULL);
2175 // Resizing when half of the hashtable is filled up.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002176 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002177 ASSERT(count_ < array_size_);
2178 count_++;
2179 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
2180 if (array_[pos].value == NULL) {
2181 array_[pos].value = value;
2182 array_[pos].next = kNil;
2183 } else {
2184 if (free_list_head_ == kNil) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002185 ResizeLists(lists_size_ << 1, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002186 }
2187 int new_element_pos = free_list_head_;
2188 ASSERT(new_element_pos != kNil);
2189 free_list_head_ = lists_[free_list_head_].next;
2190 lists_[new_element_pos].value = value;
2191 lists_[new_element_pos].next = array_[pos].next;
2192 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
2193 array_[pos].next = new_element_pos;
2194 }
2195}
2196
2197
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002198HSideEffectMap::HSideEffectMap() : count_(0) {
2199 memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize);
2200}
2201
2202
2203HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002204 *this = *other; // Calls operator=.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002205}
2206
2207
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002208HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002209 if (this != &other) {
2210 memcpy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize);
2211 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002212 return *this;
2213}
2214
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002215void HSideEffectMap::Kill(GVNFlagSet flags) {
2216 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
2217 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
2218 if (flags.Contains(changes_flag)) {
2219 if (data_[i] != NULL) count_--;
2220 data_[i] = NULL;
2221 }
2222 }
2223}
2224
2225
2226void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) {
2227 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
2228 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
2229 if (flags.Contains(changes_flag)) {
2230 if (data_[i] == NULL) count_++;
2231 data_[i] = instr;
2232 }
2233 }
2234}
2235
2236
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002237class HStackCheckEliminator BASE_EMBEDDED {
2238 public:
2239 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
2240
2241 void Process();
2242
2243 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002244 HGraph* graph_;
2245};
2246
2247
2248void HStackCheckEliminator::Process() {
2249 // For each loop block walk the dominator tree from the backwards branch to
2250 // the loop header. If a call instruction is encountered the backwards branch
2251 // is dominated by a call and the stack check in the backwards branch can be
2252 // removed.
2253 for (int i = 0; i < graph_->blocks()->length(); i++) {
2254 HBasicBlock* block = graph_->blocks()->at(i);
2255 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002256 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
2257 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00002258 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002259 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00002260 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002261 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00002262 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00002263 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002264 }
2265 instr = instr->next();
2266 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002267
2268 // Done when the loop header is processed.
2269 if (dominator == block) break;
2270
2271 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002272 dominator = dominator->dominator();
2273 }
2274 }
2275 }
2276}
2277
2278
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002279// Simple sparse set with O(1) add, contains, and clear.
2280class SparseSet {
2281 public:
2282 SparseSet(Zone* zone, int capacity)
2283 : capacity_(capacity),
2284 length_(0),
2285 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002286 sparse_(zone->NewArray<int>(capacity)) {
2287#ifndef NVALGRIND
2288 // Initialize the sparse array to make valgrind happy.
2289 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
2290#endif
2291 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002292
2293 bool Contains(int n) const {
2294 ASSERT(0 <= n && n < capacity_);
2295 int d = sparse_[n];
2296 return 0 <= d && d < length_ && dense_[d] == n;
2297 }
2298
2299 bool Add(int n) {
2300 if (Contains(n)) return false;
2301 dense_[length_] = n;
2302 sparse_[n] = length_;
2303 ++length_;
2304 return true;
2305 }
2306
2307 void Clear() { length_ = 0; }
2308
2309 private:
2310 int capacity_;
2311 int length_;
2312 int* dense_;
2313 int* sparse_;
2314
2315 DISALLOW_COPY_AND_ASSIGN(SparseSet);
2316};
2317
2318
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002319class HGlobalValueNumberer BASE_EMBEDDED {
2320 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002321 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002322 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002323 info_(info),
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002324 removed_side_effects_(false),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002325 block_side_effects_(graph->blocks()->length(), graph->zone()),
2326 loop_side_effects_(graph->blocks()->length(), graph->zone()),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002327 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
yangguo@chromium.org304cc332012-07-24 07:59:48 +00002328#ifdef DEBUG
2329 ASSERT(info->isolate()->optimizing_compiler_thread()->IsOptimizerThread() ||
2330 !info->isolate()->heap()->IsAllocationAllowed());
2331#endif
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002332 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
2333 graph_->zone());
2334 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(),
2335 graph_->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002336 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002337
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002338 // Returns true if values with side effects are removed.
2339 bool Analyze();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002340
2341 private:
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002342 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
2343 HBasicBlock* dominator,
2344 HBasicBlock* dominated);
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002345 void AnalyzeGraph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002346 void ComputeBlockSideEffects();
2347 void LoopInvariantCodeMotion();
2348 void ProcessLoopBlock(HBasicBlock* block,
2349 HBasicBlock* before_loop,
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002350 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002351 GVNFlagSet* accumulated_first_time_depends,
2352 GVNFlagSet* accumulated_first_time_changes);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002353 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002354 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
2355
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002356 HGraph* graph() { return graph_; }
2357 CompilationInfo* info() { return info_; }
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002358 Zone* zone() const { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002359
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002360 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002361 CompilationInfo* info_;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002362 bool removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002363
2364 // A map of block IDs to their side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002365 ZoneList<GVNFlagSet> block_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002366
2367 // A map of loop header block IDs to their loop's side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002368 ZoneList<GVNFlagSet> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002369
2370 // Used when collecting side effects on paths from dominator to
2371 // dominated.
2372 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002373};
2374
2375
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002376bool HGlobalValueNumberer::Analyze() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002377 removed_side_effects_ = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002378 ComputeBlockSideEffects();
2379 if (FLAG_loop_invariant_code_motion) {
2380 LoopInvariantCodeMotion();
2381 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002382 AnalyzeGraph();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002383 return removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002384}
2385
2386
2387void HGlobalValueNumberer::ComputeBlockSideEffects() {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002388 // The Analyze phase of GVN can be called multiple times. Clear loop side
2389 // effects before computing them to erase the contents from previous Analyze
2390 // passes.
2391 for (int i = 0; i < loop_side_effects_.length(); ++i) {
2392 loop_side_effects_[i].RemoveAll();
2393 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002394 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
2395 // Compute side effects for the block.
2396 HBasicBlock* block = graph_->blocks()->at(i);
2397 HInstruction* instr = block->first();
2398 int id = block->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002399 GVNFlagSet side_effects;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002400 while (instr != NULL) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002401 side_effects.Add(instr->ChangesFlags());
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00002402 if (instr->IsSoftDeoptimize()) {
2403 block_side_effects_[id].RemoveAll();
2404 side_effects.RemoveAll();
2405 break;
2406 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002407 instr = instr->next();
2408 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002409 block_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002410
2411 // Loop headers are part of their loop.
2412 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002413 loop_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002414 }
2415
2416 // Propagate loop side effects upwards.
2417 if (block->HasParentLoopHeader()) {
2418 int header_id = block->parent_loop_header()->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002419 loop_side_effects_[header_id].Add(block->IsLoopHeader()
2420 ? loop_side_effects_[id]
2421 : side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002422 }
2423 }
2424}
2425
2426
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002427SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) {
2428 char underlying_buffer[kLastFlag * 128];
2429 Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer));
2430#if DEBUG
2431 int offset = 0;
2432 const char* separator = "";
2433 const char* comma = ", ";
2434 buffer[0] = 0;
2435 uint32_t set_depends_on = 0;
2436 uint32_t set_changes = 0;
2437 for (int bit = 0; bit < kLastFlag; ++bit) {
2438 if ((flags.ToIntegral() & (1 << bit)) != 0) {
2439 if (bit % 2 == 0) {
2440 set_changes++;
2441 } else {
2442 set_depends_on++;
2443 }
2444 }
2445 }
2446 bool positive_changes = set_changes < (kLastFlag / 2);
2447 bool positive_depends_on = set_depends_on < (kLastFlag / 2);
2448 if (set_changes > 0) {
2449 if (positive_changes) {
2450 offset += OS::SNPrintF(buffer + offset, "changes [");
2451 } else {
2452 offset += OS::SNPrintF(buffer + offset, "changes all except [");
2453 }
2454 for (int bit = 0; bit < kLastFlag; ++bit) {
2455 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) {
2456 switch (static_cast<GVNFlag>(bit)) {
2457#define DECLARE_FLAG(type) \
2458 case kChanges##type: \
2459 offset += OS::SNPrintF(buffer + offset, separator); \
2460 offset += OS::SNPrintF(buffer + offset, #type); \
2461 separator = comma; \
2462 break;
2463GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
2464GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
2465#undef DECLARE_FLAG
2466 default:
2467 break;
2468 }
2469 }
2470 }
2471 offset += OS::SNPrintF(buffer + offset, "]");
2472 }
2473 if (set_depends_on > 0) {
2474 separator = "";
2475 if (set_changes > 0) {
2476 offset += OS::SNPrintF(buffer + offset, ", ");
2477 }
2478 if (positive_depends_on) {
2479 offset += OS::SNPrintF(buffer + offset, "depends on [");
2480 } else {
2481 offset += OS::SNPrintF(buffer + offset, "depends on all except [");
2482 }
2483 for (int bit = 0; bit < kLastFlag; ++bit) {
2484 if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) {
2485 switch (static_cast<GVNFlag>(bit)) {
2486#define DECLARE_FLAG(type) \
2487 case kDependsOn##type: \
2488 offset += OS::SNPrintF(buffer + offset, separator); \
2489 offset += OS::SNPrintF(buffer + offset, #type); \
2490 separator = comma; \
2491 break;
2492GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
2493GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
2494#undef DECLARE_FLAG
2495 default:
2496 break;
2497 }
2498 }
2499 }
2500 offset += OS::SNPrintF(buffer + offset, "]");
2501 }
2502#else
2503 OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral());
2504#endif
2505 size_t string_len = strlen(underlying_buffer) + 1;
2506 ASSERT(string_len <= sizeof(underlying_buffer));
2507 char* result = new char[strlen(underlying_buffer) + 1];
2508 memcpy(result, underlying_buffer, string_len);
2509 return SmartArrayPointer<char>(result);
2510}
2511
2512
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002513void HGlobalValueNumberer::LoopInvariantCodeMotion() {
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002514 TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n",
2515 graph_->use_optimistic_licm() ? "yes" : "no");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002516 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
2517 HBasicBlock* block = graph_->blocks()->at(i);
2518 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002519 GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002520 TRACE_GVN_2("Try loop invariant motion for block B%d %s\n",
2521 block->block_id(),
2522 *GetGVNFlagsString(side_effects));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002523
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002524 GVNFlagSet accumulated_first_time_depends;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002525 GVNFlagSet accumulated_first_time_changes;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002526 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
2527 for (int j = block->block_id(); j <= last->block_id(); ++j) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002528 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002529 &accumulated_first_time_depends,
2530 &accumulated_first_time_changes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002531 }
2532 }
2533 }
2534}
2535
2536
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002537void HGlobalValueNumberer::ProcessLoopBlock(
2538 HBasicBlock* block,
2539 HBasicBlock* loop_header,
2540 GVNFlagSet loop_kills,
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002541 GVNFlagSet* first_time_depends,
2542 GVNFlagSet* first_time_changes) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002543 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002544 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002545 TRACE_GVN_2("Loop invariant motion for B%d %s\n",
2546 block->block_id(),
2547 *GetGVNFlagsString(depends_flags));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002548 HInstruction* instr = block->first();
2549 while (instr != NULL) {
2550 HInstruction* next = instr->next();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002551 bool hoisted = false;
2552 if (instr->CheckFlag(HValue::kUseGVN)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002553 TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n",
2554 instr->id(),
2555 instr->Mnemonic(),
2556 *GetGVNFlagsString(instr->gvn_flags()),
2557 *GetGVNFlagsString(loop_kills));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002558 bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002559 if (can_hoist && !graph()->use_optimistic_licm()) {
2560 can_hoist = block->IsLoopSuccessorDominator();
2561 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002562
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002563 if (can_hoist) {
2564 bool inputs_loop_invariant = true;
2565 for (int i = 0; i < instr->OperandCount(); ++i) {
2566 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
2567 inputs_loop_invariant = false;
2568 }
2569 }
2570
2571 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002572 TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id());
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002573 // Move the instruction out of the loop.
2574 instr->Unlink();
2575 instr->InsertBefore(pre_header->end());
2576 if (instr->HasSideEffects()) removed_side_effects_ = true;
2577 hoisted = true;
2578 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002579 }
2580 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002581 if (!hoisted) {
2582 // If an instruction is not hoisted, we have to account for its side
2583 // effects when hoisting later HTransitionElementsKind instructions.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002584 GVNFlagSet previous_depends = *first_time_depends;
2585 GVNFlagSet previous_changes = *first_time_changes;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00002586 first_time_depends->Add(instr->DependsOnFlags());
2587 first_time_changes->Add(instr->ChangesFlags());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002588 if (!(previous_depends == *first_time_depends)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002589 TRACE_GVN_1("Updated first-time accumulated %s\n",
2590 *GetGVNFlagsString(*first_time_depends));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002591 }
2592 if (!(previous_changes == *first_time_changes)) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002593 TRACE_GVN_1("Updated first-time accumulated %s\n",
2594 *GetGVNFlagsString(*first_time_changes));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002595 }
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00002596 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002597 instr = next;
2598 }
2599}
2600
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00002601
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002602bool HGlobalValueNumberer::AllowCodeMotion() {
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00002603 return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002604}
2605
2606
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002607bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
2608 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002609 // If we've disabled code motion or we're in a block that unconditionally
2610 // deoptimizes, don't move any instructions.
2611 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002612}
2613
2614
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002615GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002616 HBasicBlock* dominator, HBasicBlock* dominated) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002617 GVNFlagSet side_effects;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002618 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
2619 HBasicBlock* block = dominated->predecessors()->at(i);
2620 if (dominator->block_id() < block->block_id() &&
2621 block->block_id() < dominated->block_id() &&
2622 visited_on_paths_.Add(block->block_id())) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002623 side_effects.Add(block_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002624 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002625 side_effects.Add(loop_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002626 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00002627 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
2628 dominator, block));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00002629 }
2630 }
2631 return side_effects;
2632}
2633
2634
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002635// Each instance of this class is like a "stack frame" for the recursive
2636// traversal of the dominator tree done during GVN (the stack is handled
2637// as a double linked list).
2638// We reuse frames when possible so the list length is limited by the depth
2639// of the dominator tree but this forces us to initialize each frame calling
2640// an explicit "Initialize" method instead of a using constructor.
2641class GvnBasicBlockState: public ZoneObject {
2642 public:
2643 static GvnBasicBlockState* CreateEntry(Zone* zone,
2644 HBasicBlock* entry_block,
2645 HValueMap* entry_map) {
2646 return new(zone)
2647 GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002648 }
2649
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002650 HBasicBlock* block() { return block_; }
2651 HValueMap* map() { return map_; }
2652 HSideEffectMap* dominators() { return &dominators_; }
2653
2654 GvnBasicBlockState* next_in_dominator_tree_traversal(
2655 Zone* zone,
2656 HBasicBlock** dominator) {
2657 // This assignment needs to happen before calling next_dominated() because
2658 // that call can reuse "this" if we are at the last dominated block.
2659 *dominator = block();
2660 GvnBasicBlockState* result = next_dominated(zone);
2661 if (result == NULL) {
2662 GvnBasicBlockState* dominator_state = pop();
2663 if (dominator_state != NULL) {
2664 // This branch is guaranteed not to return NULL because pop() never
2665 // returns a state where "is_done() == true".
2666 *dominator = dominator_state->block();
2667 result = dominator_state->next_dominated(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002668 } else {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002669 // Unnecessary (we are returning NULL) but done for cleanness.
2670 *dominator = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002671 }
2672 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002673 return result;
2674 }
2675
2676 private:
2677 void Initialize(HBasicBlock* block,
2678 HValueMap* map,
2679 HSideEffectMap* dominators,
2680 bool copy_map,
2681 Zone* zone) {
2682 block_ = block;
2683 map_ = copy_map ? map->Copy(zone) : map;
2684 dominated_index_ = -1;
2685 length_ = block->dominated_blocks()->length();
2686 if (dominators != NULL) {
2687 dominators_ = *dominators;
2688 }
2689 }
2690 bool is_done() { return dominated_index_ >= length_; }
2691
2692 GvnBasicBlockState(GvnBasicBlockState* previous,
2693 HBasicBlock* block,
2694 HValueMap* map,
2695 HSideEffectMap* dominators,
2696 Zone* zone)
2697 : previous_(previous), next_(NULL) {
2698 Initialize(block, map, dominators, true, zone);
2699 }
2700
2701 GvnBasicBlockState* next_dominated(Zone* zone) {
2702 dominated_index_++;
2703 if (dominated_index_ == length_ - 1) {
2704 // No need to copy the map for the last child in the dominator tree.
2705 Initialize(block_->dominated_blocks()->at(dominated_index_),
2706 map(),
2707 dominators(),
2708 false,
2709 zone);
2710 return this;
2711 } else if (dominated_index_ < length_) {
2712 return push(zone,
2713 block_->dominated_blocks()->at(dominated_index_),
2714 dominators());
2715 } else {
2716 return NULL;
2717 }
2718 }
2719
2720 GvnBasicBlockState* push(Zone* zone,
2721 HBasicBlock* block,
2722 HSideEffectMap* dominators) {
2723 if (next_ == NULL) {
2724 next_ =
2725 new(zone) GvnBasicBlockState(this, block, map(), dominators, zone);
2726 } else {
2727 next_->Initialize(block, map(), dominators, true, zone);
2728 }
2729 return next_;
2730 }
2731 GvnBasicBlockState* pop() {
2732 GvnBasicBlockState* result = previous_;
2733 while (result != NULL && result->is_done()) {
2734 TRACE_GVN_2("Backtracking from block B%d to block b%d\n",
2735 block()->block_id(),
2736 previous_->block()->block_id())
2737 result = result->previous_;
2738 }
2739 return result;
2740 }
2741
2742 GvnBasicBlockState* previous_;
2743 GvnBasicBlockState* next_;
2744 HBasicBlock* block_;
2745 HValueMap* map_;
2746 HSideEffectMap dominators_;
2747 int dominated_index_;
2748 int length_;
2749};
2750
2751// This is a recursive traversal of the dominator tree but it has been turned
2752// into a loop to avoid stack overflows.
2753// The logical "stack frames" of the recursion are kept in a list of
2754// GvnBasicBlockState instances.
2755void HGlobalValueNumberer::AnalyzeGraph() {
2756 HBasicBlock* entry_block = graph_->entry_block();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002757 HValueMap* entry_map = new(zone()) HValueMap(zone());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002758 GvnBasicBlockState* current =
2759 GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map);
2760
2761 while (current != NULL) {
2762 HBasicBlock* block = current->block();
2763 HValueMap* map = current->map();
2764 HSideEffectMap* dominators = current->dominators();
2765
2766 TRACE_GVN_2("Analyzing block B%d%s\n",
2767 block->block_id(),
2768 block->IsLoopHeader() ? " (loop header)" : "");
2769
2770 // If this is a loop header kill everything killed by the loop.
2771 if (block->IsLoopHeader()) {
2772 map->Kill(loop_side_effects_[block->block_id()]);
2773 }
2774
2775 // Go through all instructions of the current block.
2776 HInstruction* instr = block->first();
2777 while (instr != NULL) {
2778 HInstruction* next = instr->next();
2779 GVNFlagSet flags = instr->ChangesFlags();
2780 if (!flags.IsEmpty()) {
2781 // Clear all instructions in the map that are affected by side effects.
2782 // Store instruction as the dominating one for tracked side effects.
2783 map->Kill(flags);
2784 dominators->Store(flags, instr);
2785 TRACE_GVN_2("Instruction %d %s\n", instr->id(),
2786 *GetGVNFlagsString(flags));
2787 }
2788 if (instr->CheckFlag(HValue::kUseGVN)) {
2789 ASSERT(!instr->HasObservableSideEffects());
2790 HValue* other = map->Lookup(instr);
2791 if (other != NULL) {
2792 ASSERT(instr->Equals(other) && other->Equals(instr));
2793 TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n",
danno@chromium.org1044a4d2012-04-30 12:34:39 +00002794 instr->id(),
2795 instr->Mnemonic(),
2796 other->id(),
2797 other->Mnemonic());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002798 if (instr->HasSideEffects()) removed_side_effects_ = true;
2799 instr->DeleteAndReplaceWith(other);
2800 } else {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002801 map->Add(instr, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002802 }
2803 }
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002804 if (instr->IsLinked() &&
2805 instr->CheckFlag(HValue::kTrackSideEffectDominators)) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002806 for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
2807 HValue* other = dominators->at(i);
2808 GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
2809 GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i);
2810 if (instr->DependsOnFlags().Contains(depends_on_flag) &&
2811 (other != NULL)) {
2812 TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n",
2813 i,
2814 instr->id(),
2815 instr->Mnemonic(),
2816 other->id(),
2817 other->Mnemonic());
2818 instr->SetSideEffectDominator(changes_flag, other);
2819 }
2820 }
2821 }
2822 instr = next;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002823 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002824
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002825 HBasicBlock* dominator_block;
2826 GvnBasicBlockState* next =
2827 current->next_in_dominator_tree_traversal(zone(), &dominator_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002828
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002829 if (next != NULL) {
2830 HBasicBlock* dominated = next->block();
2831 HValueMap* successor_map = next->map();
2832 HSideEffectMap* successor_dominators = next->dominators();
2833
2834 // Kill everything killed on any path between this block and the
2835 // dominated block. We don't have to traverse these paths if the
2836 // value map and the dominators list is already empty. If the range
2837 // of block ids (block_id, dominated_id) is empty there are no such
2838 // paths.
2839 if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) &&
2840 dominator_block->block_id() + 1 < dominated->block_id()) {
2841 visited_on_paths_.Clear();
2842 GVNFlagSet side_effects_on_all_paths =
2843 CollectSideEffectsOnPathsToDominatedBlock(dominator_block,
2844 dominated);
2845 successor_map->Kill(side_effects_on_all_paths);
2846 successor_dominators->Kill(side_effects_on_all_paths);
2847 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002848 }
rossberg@chromium.org400388e2012-06-06 09:29:22 +00002849 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002850 }
2851}
2852
2853
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002854void HInferRepresentation::AddToWorklist(HValue* current) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002855 if (current->representation().IsTagged()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002856 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
2857 if (in_worklist_.Contains(current->id())) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002858 worklist_.Add(current, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002859 in_worklist_.Add(current->id());
2860}
2861
2862
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002863void HInferRepresentation::Analyze() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002864 HPhase phase("H_Infer representations", graph_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002865
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002866 // (1) Initialize bit vectors and count real uses. Each phi gets a
2867 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002868 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002869 int phi_count = phi_list->length();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002870 ZoneList<BitVector*> connected_phis(phi_count, graph_->zone());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002871 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002872 phi_list->at(i)->InitRealUses(i);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00002873 BitVector* connected_set = new(zone()) BitVector(phi_count, graph_->zone());
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00002874 connected_set->Add(i);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002875 connected_phis.Add(connected_set, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002876 }
2877
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002878 // (2) Do a fixed point iteration to find the set of connected phis. A
2879 // phi is connected to another phi if its value is used either directly or
2880 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002881 bool change = true;
2882 while (change) {
2883 change = false;
ricow@chromium.org55ee8072011-09-08 16:33:10 +00002884 // We normally have far more "forward edges" than "backward edges",
2885 // so we terminate faster when we walk backwards.
2886 for (int i = phi_count - 1; i >= 0; --i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002887 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002888 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
2889 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002890 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002891 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002892 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
2893 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002894 }
2895 }
2896 }
2897 }
2898
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002899 // (3a) Use the phi reachability information from step 2 to
2900 // push information about values which can't be converted to integer
2901 // without deoptimization through the phi use-def chains, avoiding
2902 // unnecessary deoptimizations later.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002903 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002904 HPhi* phi = phi_list->at(i);
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002905 bool cti = phi->AllOperandsConvertibleToInteger();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002906 if (cti) continue;
2907
2908 for (BitVector::Iterator it(connected_phis.at(i));
2909 !it.Done();
2910 it.Advance()) {
2911 HPhi* phi = phi_list->at(it.Current());
2912 phi->set_is_convertible_to_integer(false);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002913 }
2914 }
2915
2916 // (3b) Use the phi reachability information from step 2 to
2917 // sum up the non-phi use counts of all connected phis.
2918 for (int i = 0; i < phi_count; ++i) {
2919 HPhi* phi = phi_list->at(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002920 for (BitVector::Iterator it(connected_phis.at(i));
2921 !it.Done();
2922 it.Advance()) {
2923 int index = it.Current();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002924 HPhi* it_use = phi_list->at(index);
2925 if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002926 }
2927 }
2928
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002929 // Initialize work list
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002930 for (int i = 0; i < graph_->blocks()->length(); ++i) {
2931 HBasicBlock* block = graph_->blocks()->at(i);
2932 const ZoneList<HPhi*>* phis = block->phis();
2933 for (int j = 0; j < phis->length(); ++j) {
2934 AddToWorklist(phis->at(j));
2935 }
2936
2937 HInstruction* current = block->first();
2938 while (current != NULL) {
2939 AddToWorklist(current);
2940 current = current->next();
2941 }
2942 }
2943
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002944 // Do a fixed point iteration, trying to improve representations
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002945 while (!worklist_.is_empty()) {
2946 HValue* current = worklist_.RemoveLast();
2947 in_worklist_.Remove(current->id());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002948 current->InferRepresentation(this);
2949 }
2950
2951 // Lastly: any instruction that we don't have representation information
2952 // for defaults to Tagged.
2953 for (int i = 0; i < graph_->blocks()->length(); ++i) {
2954 HBasicBlock* block = graph_->blocks()->at(i);
2955 const ZoneList<HPhi*>* phis = block->phis();
2956 for (int j = 0; j < phis->length(); ++j) {
2957 HPhi* phi = phis->at(j);
2958 if (phi->representation().IsNone()) {
2959 phi->ChangeRepresentation(Representation::Tagged());
2960 }
2961 }
2962 for (HInstruction* current = block->first();
2963 current != NULL; current = current->next()) {
2964 if (current->representation().IsNone() &&
2965 current->CheckFlag(HInstruction::kFlexibleRepresentation)) {
2966 current->ChangeRepresentation(Representation::Tagged());
2967 }
2968 }
2969 }
2970}
2971
2972
2973void HGraph::MergeRemovableSimulates() {
2974 for (int i = 0; i < blocks()->length(); ++i) {
2975 HBasicBlock* block = blocks()->at(i);
2976 // Always reset the folding candidate at the start of a block.
2977 HSimulate* folding_candidate = NULL;
2978 // Nasty heuristic: Never remove the first simulate in a block. This
2979 // just so happens to have a beneficial effect on register allocation.
2980 bool first = true;
2981 for (HInstruction* current = block->first();
2982 current != NULL; current = current->next()) {
2983 if (current->IsLeaveInlined()) {
2984 // Never fold simulates from inlined environments into simulates
2985 // in the outer environment.
2986 // (Before each HEnterInlined, there is a non-foldable HSimulate
2987 // anyway, so we get the barrier in the other direction for free.)
2988 if (folding_candidate != NULL) {
2989 folding_candidate->DeleteAndReplaceWith(NULL);
2990 }
2991 folding_candidate = NULL;
2992 continue;
2993 }
2994 // If we have an HSimulate and a candidate, perform the folding.
2995 if (!current->IsSimulate()) continue;
2996 if (first) {
2997 first = false;
2998 continue;
2999 }
3000 HSimulate* current_simulate = HSimulate::cast(current);
3001 if (folding_candidate != NULL) {
3002 folding_candidate->MergeInto(current_simulate);
3003 folding_candidate->DeleteAndReplaceWith(NULL);
3004 folding_candidate = NULL;
3005 }
3006 // Check if the current simulate is a candidate for folding.
3007 if (current_simulate->previous()->HasObservableSideEffects() &&
3008 !current_simulate->next()->IsSimulate()) {
3009 continue;
3010 }
3011 if (!current_simulate->is_candidate_for_removal()) {
3012 continue;
3013 }
3014 folding_candidate = current_simulate;
3015 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003016 }
3017}
3018
3019
3020void HGraph::InitializeInferredTypes() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003021 HPhase phase("H_Inferring types", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003022 InitializeInferredTypes(0, this->blocks_.length() - 1);
3023}
3024
3025
3026void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
3027 for (int i = from_inclusive; i <= to_inclusive; ++i) {
3028 HBasicBlock* block = blocks_[i];
3029
3030 const ZoneList<HPhi*>* phis = block->phis();
3031 for (int j = 0; j < phis->length(); j++) {
3032 phis->at(j)->UpdateInferredType();
3033 }
3034
3035 HInstruction* current = block->first();
3036 while (current != NULL) {
3037 current->UpdateInferredType();
3038 current = current->next();
3039 }
3040
3041 if (block->IsLoopHeader()) {
3042 HBasicBlock* last_back_edge =
3043 block->loop_information()->GetLastBackEdge();
3044 InitializeInferredTypes(i + 1, last_back_edge->block_id());
3045 // Skip all blocks already processed by the recursive call.
3046 i = last_back_edge->block_id();
3047 // Update phis of the loop header now after the whole loop body is
3048 // guaranteed to be processed.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003049 ZoneList<HValue*> worklist(block->phis()->length(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003050 for (int j = 0; j < block->phis()->length(); ++j) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003051 worklist.Add(block->phis()->at(j), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003052 }
3053 InferTypes(&worklist);
3054 }
3055 }
3056}
3057
3058
3059void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
3060 HValue* current = value;
3061 while (current != NULL) {
3062 if (visited->Contains(current->id())) return;
3063
3064 // For phis, we must propagate the check to all of its inputs.
3065 if (current->IsPhi()) {
3066 visited->Add(current->id());
3067 HPhi* phi = HPhi::cast(current);
3068 for (int i = 0; i < phi->OperandCount(); ++i) {
3069 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
3070 }
3071 break;
3072 }
3073
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003074 // For multiplication, division, and Math.min/max(), we must propagate
3075 // to the left and the right side.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003076 if (current->IsMul()) {
3077 HMul* mul = HMul::cast(current);
3078 mul->EnsureAndPropagateNotMinusZero(visited);
3079 PropagateMinusZeroChecks(mul->left(), visited);
3080 PropagateMinusZeroChecks(mul->right(), visited);
3081 } else if (current->IsDiv()) {
3082 HDiv* div = HDiv::cast(current);
3083 div->EnsureAndPropagateNotMinusZero(visited);
3084 PropagateMinusZeroChecks(div->left(), visited);
3085 PropagateMinusZeroChecks(div->right(), visited);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003086 } else if (current->IsMathMinMax()) {
3087 HMathMinMax* minmax = HMathMinMax::cast(current);
3088 visited->Add(minmax->id());
3089 PropagateMinusZeroChecks(minmax->left(), visited);
3090 PropagateMinusZeroChecks(minmax->right(), visited);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003091 }
3092
3093 current = current->EnsureAndPropagateNotMinusZero(visited);
3094 }
3095}
3096
3097
3098void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003099 HValue* use_value,
3100 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003101 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003102 // Insert the representation change right before its use. For phi-uses we
3103 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003104 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003105 if (use_value->IsPhi()) {
3106 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003107 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003108 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003109 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003110 // For constants we try to make the representation change at compile
3111 // time. When a representation change is not possible without loss of
3112 // information we treat constants like normal instructions and insert the
3113 // change instructions for them.
3114 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003115 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003116 bool deoptimize_on_undefined =
3117 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003118 if (value->IsConstant()) {
3119 HConstant* constant = HConstant::cast(value);
3120 // Try to create a new copy of the constant with the new representation.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003121 new_value = (is_truncating && to.IsInteger32())
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003122 ? constant->CopyToTruncatedInt32(zone())
3123 : constant->CopyToRepresentation(to, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003124 }
3125
3126 if (new_value == NULL) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003127 new_value = new(zone()) HChange(value, to,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003128 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003129 }
3130
3131 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003132 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003133}
3134
3135
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003136void HGraph::InsertRepresentationChangesForValue(HValue* value) {
3137 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003138 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003139 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003140
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003141 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
3142 HValue* use_value = it.value();
3143 int use_index = it.index();
3144 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003145 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003146 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003147 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003148 if (value->HasNoUses()) {
3149 ASSERT(value->IsConstant());
3150 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003151 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003152
3153 // The only purpose of a HForceRepresentation is to represent the value
3154 // after the (possible) HChange instruction. We make it disappear.
3155 if (value->IsForceRepresentation()) {
3156 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
3157 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003158}
3159
3160
3161void HGraph::InsertRepresentationChanges() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003162 HPhase phase("H_Representation changes", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003163
3164 // Compute truncation flag for phis: Initially assume that all
3165 // int32-phis allow truncation and iteratively remove the ones that
3166 // are used in an operation that does not allow a truncating
3167 // conversion.
3168 // TODO(fschneider): Replace this with a worklist-based iteration.
3169 for (int i = 0; i < phi_list()->length(); i++) {
3170 HPhi* phi = phi_list()->at(i);
3171 if (phi->representation().IsInteger32()) {
3172 phi->SetFlag(HValue::kTruncatingToInt32);
3173 }
3174 }
3175 bool change = true;
3176 while (change) {
3177 change = false;
3178 for (int i = 0; i < phi_list()->length(); i++) {
3179 HPhi* phi = phi_list()->at(i);
3180 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003181 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
3182 // If a Phi is used as a non-truncating int32 or as a double,
3183 // clear its "truncating" flag.
3184 HValue* use = it.value();
3185 Representation input_representation =
3186 use->RequiredInputRepresentation(it.index());
3187 if ((input_representation.IsInteger32() &&
3188 !use->CheckFlag(HValue::kTruncatingToInt32)) ||
3189 input_representation.IsDouble()) {
3190 if (FLAG_trace_representation) {
3191 PrintF("#%d Phi is not truncating because of #%d %s\n",
3192 phi->id(), it.value()->id(), it.value()->Mnemonic());
3193 }
3194 phi->ClearFlag(HValue::kTruncatingToInt32);
3195 change = true;
3196 break;
3197 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003198 }
3199 }
3200 }
3201
3202 for (int i = 0; i < blocks_.length(); ++i) {
3203 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003204 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
3205 for (int j = 0; j < phis->length(); j++) {
3206 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003207 }
3208
3209 // Process normal instructions.
3210 HInstruction* current = blocks_[i]->first();
3211 while (current != NULL) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003212 HInstruction* next = current->next();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003213 InsertRepresentationChangesForValue(current);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003214 current = next;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003215 }
3216 }
3217}
3218
3219
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003220void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
3221 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
3222 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
3223 for (int i = 0; i < phi->OperandCount(); ++i) {
3224 HValue* input = phi->OperandAt(i);
3225 if (input->IsPhi()) {
3226 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
3227 }
3228 }
3229}
3230
3231
3232void HGraph::MarkDeoptimizeOnUndefined() {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003233 HPhase phase("H_MarkDeoptimizeOnUndefined", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003234 // Compute DeoptimizeOnUndefined flag for phis.
3235 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003236 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003237 // double input representation, has this flag set.
3238 // The flag is used by HChange tagged->double, which must deoptimize
3239 // if one of its uses has this flag set.
3240 for (int i = 0; i < phi_list()->length(); i++) {
3241 HPhi* phi = phi_list()->at(i);
3242 if (phi->representation().IsDouble()) {
3243 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
3244 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
3245 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
3246 break;
3247 }
3248 }
3249 }
3250 }
3251}
3252
3253
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003254// Discover instructions that can be marked with kUint32 flag allowing
3255// them to produce full range uint32 values.
3256class Uint32Analysis BASE_EMBEDDED {
3257 public:
3258 explicit Uint32Analysis(Zone* zone) : zone_(zone), phis_(4, zone) { }
3259
3260 void Analyze(HInstruction* current);
3261
3262 void UnmarkUnsafePhis();
3263
3264 private:
3265 bool IsSafeUint32Use(HValue* val, HValue* use);
3266 bool Uint32UsesAreSafe(HValue* uint32val);
3267 bool CheckPhiOperands(HPhi* phi);
3268 void UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist);
3269
3270 Zone* zone_;
3271 ZoneList<HPhi*> phis_;
3272};
3273
3274
3275bool Uint32Analysis::IsSafeUint32Use(HValue* val, HValue* use) {
3276 // Operations that operatate on bits are safe.
3277 if (use->IsBitwise() ||
3278 use->IsShl() ||
3279 use->IsSar() ||
3280 use->IsShr() ||
3281 use->IsBitNot()) {
3282 return true;
3283 } else if (use->IsChange() || use->IsSimulate()) {
3284 // Conversions and deoptimization have special support for unt32.
3285 return true;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00003286 } else if (use->IsStoreKeyed()) {
3287 HStoreKeyed* store = HStoreKeyed::cast(use);
3288 if (store->is_external()) {
3289 // Storing a value into an external integer array is a bit level
3290 // operation.
3291 if (store->value() == val) {
3292 // Clamping or a conversion to double should have beed inserted.
3293 ASSERT(store->elements_kind() != EXTERNAL_PIXEL_ELEMENTS);
3294 ASSERT(store->elements_kind() != EXTERNAL_FLOAT_ELEMENTS);
3295 ASSERT(store->elements_kind() != EXTERNAL_DOUBLE_ELEMENTS);
3296 return true;
3297 }
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003298 }
3299 }
3300
3301 return false;
3302}
3303
3304
3305// Iterate over all uses and verify that they are uint32 safe: either don't
3306// distinguish between int32 and uint32 due to their bitwise nature or
3307// have special support for uint32 values.
3308// Encountered phis are optimisitically treated as safe uint32 uses,
3309// marked with kUint32 flag and collected in the phis_ list. A separate
3310// path will be performed later by UnmarkUnsafePhis to clear kUint32 from
3311// phis that are not actually uint32-safe (it requries fix point iteration).
3312bool Uint32Analysis::Uint32UsesAreSafe(HValue* uint32val) {
3313 bool collect_phi_uses = false;
3314 for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
3315 HValue* use = it.value();
3316
3317 if (use->IsPhi()) {
3318 if (!use->CheckFlag(HInstruction::kUint32)) {
3319 // There is a phi use of this value from a phis that is not yet
3320 // collected in phis_ array. Separate pass is required.
3321 collect_phi_uses = true;
3322 }
3323
3324 // Optimistically treat phis as uint32 safe.
3325 continue;
3326 }
3327
3328 if (!IsSafeUint32Use(uint32val, use)) {
3329 return false;
3330 }
3331 }
3332
3333 if (collect_phi_uses) {
3334 for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
3335 HValue* use = it.value();
3336
3337 // There is a phi use of this value from a phis that is not yet
3338 // collected in phis_ array. Separate pass is required.
3339 if (use->IsPhi() && !use->CheckFlag(HInstruction::kUint32)) {
3340 use->SetFlag(HInstruction::kUint32);
3341 phis_.Add(HPhi::cast(use), zone_);
3342 }
3343 }
3344 }
3345
3346 return true;
3347}
3348
3349
3350// Analyze instruction and mark it with kUint32 if all its uses are uint32
3351// safe.
3352void Uint32Analysis::Analyze(HInstruction* current) {
3353 if (Uint32UsesAreSafe(current)) current->SetFlag(HInstruction::kUint32);
3354}
3355
3356
3357// Check if all operands to the given phi are marked with kUint32 flag.
3358bool Uint32Analysis::CheckPhiOperands(HPhi* phi) {
3359 if (!phi->CheckFlag(HInstruction::kUint32)) {
3360 // This phi is not uint32 safe. No need to check operands.
3361 return false;
3362 }
3363
3364 for (int j = 0; j < phi->OperandCount(); j++) {
3365 HValue* operand = phi->OperandAt(j);
3366 if (!operand->CheckFlag(HInstruction::kUint32)) {
3367 // Lazyly mark constants that fit into uint32 range with kUint32 flag.
3368 if (operand->IsConstant() &&
3369 HConstant::cast(operand)->IsUint32()) {
3370 operand->SetFlag(HInstruction::kUint32);
3371 continue;
3372 }
3373
3374 // This phi is not safe, some operands are not uint32 values.
3375 return false;
3376 }
3377 }
3378
3379 return true;
3380}
3381
3382
3383// Remove kUint32 flag from the phi itself and its operands. If any operand
3384// was a phi marked with kUint32 place it into a worklist for
3385// transitive clearing of kUint32 flag.
3386void Uint32Analysis::UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist) {
3387 phi->ClearFlag(HInstruction::kUint32);
3388 for (int j = 0; j < phi->OperandCount(); j++) {
3389 HValue* operand = phi->OperandAt(j);
3390 if (operand->CheckFlag(HInstruction::kUint32)) {
3391 operand->ClearFlag(HInstruction::kUint32);
3392 if (operand->IsPhi()) {
3393 worklist->Add(HPhi::cast(operand), zone_);
3394 }
3395 }
3396 }
3397}
3398
3399
3400void Uint32Analysis::UnmarkUnsafePhis() {
3401 // No phis were collected. Nothing to do.
3402 if (phis_.length() == 0) return;
3403
3404 // Worklist used to transitively clear kUint32 from phis that
3405 // are used as arguments to other phis.
3406 ZoneList<HPhi*> worklist(phis_.length(), zone_);
3407
3408 // Phi can be used as a uint32 value if and only if
3409 // all its operands are uint32 values and all its
3410 // uses are uint32 safe.
3411
3412 // Iterate over collected phis and unmark those that
3413 // are unsafe. When unmarking phi unmark its operands
3414 // and add it to the worklist if it is a phi as well.
3415 // Phis that are still marked as safe are shifted down
3416 // so that all safe phis form a prefix of the phis_ array.
3417 int phi_count = 0;
3418 for (int i = 0; i < phis_.length(); i++) {
3419 HPhi* phi = phis_[i];
3420
3421 if (CheckPhiOperands(phi) && Uint32UsesAreSafe(phi)) {
3422 phis_[phi_count++] = phi;
3423 } else {
3424 UnmarkPhi(phi, &worklist);
3425 }
3426 }
3427
3428 // Now phis array contains only those phis that have safe
3429 // non-phi uses. Start transitively clearing kUint32 flag
3430 // from phi operands of discovered non-safe phies until
3431 // only safe phies are left.
3432 while (!worklist.is_empty()) {
3433 while (!worklist.is_empty()) {
3434 HPhi* phi = worklist.RemoveLast();
3435 UnmarkPhi(phi, &worklist);
3436 }
3437
3438 // Check if any operands to safe phies were unmarked
3439 // turning a safe phi into unsafe. The same value
3440 // can flow into several phis.
3441 int new_phi_count = 0;
3442 for (int i = 0; i < phi_count; i++) {
3443 HPhi* phi = phis_[i];
3444
3445 if (CheckPhiOperands(phi)) {
3446 phis_[new_phi_count++] = phi;
3447 } else {
3448 UnmarkPhi(phi, &worklist);
3449 }
3450 }
3451 phi_count = new_phi_count;
3452 }
3453}
3454
3455
3456void HGraph::ComputeSafeUint32Operations() {
3457 if (!FLAG_opt_safe_uint32_operations || uint32_instructions_ == NULL) {
3458 return;
3459 }
3460
3461 Uint32Analysis analysis(zone());
3462 for (int i = 0; i < uint32_instructions_->length(); ++i) {
3463 HInstruction* current = uint32_instructions_->at(i);
3464 if (current->IsLinked() && current->representation().IsInteger32()) {
3465 analysis.Analyze(current);
3466 }
3467 }
3468
3469 // Some phis might have been optimistically marked with kUint32 flag.
3470 // Remove this flag from those phis that are unsafe and propagate
3471 // this information transitively potentially clearing kUint32 flag
3472 // from some non-phi operations that are used as operands to unsafe phis.
3473 analysis.UnmarkUnsafePhis();
3474}
3475
3476
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003477void HGraph::ComputeMinusZeroChecks() {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003478 BitVector visited(GetMaximumValueID(), zone());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003479 for (int i = 0; i < blocks_.length(); ++i) {
3480 for (HInstruction* current = blocks_[i]->first();
3481 current != NULL;
3482 current = current->next()) {
3483 if (current->IsChange()) {
3484 HChange* change = HChange::cast(current);
3485 // Propagate flags for negative zero checks upwards from conversions
3486 // int32-to-tagged and int32-to-double.
3487 Representation from = change->value()->representation();
3488 ASSERT(from.Equals(change->from()));
3489 if (from.IsInteger32()) {
3490 ASSERT(change->to().IsTagged() || change->to().IsDouble());
3491 ASSERT(visited.IsEmpty());
3492 PropagateMinusZeroChecks(change->value(), &visited);
3493 visited.Clear();
3494 }
3495 }
3496 }
3497 }
3498}
3499
3500
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003501// Implementation of utility class to encapsulate the translation state for
3502// a (possibly inlined) function.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003503FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003504 CompilationInfo* info,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003505 TypeFeedbackOracle* oracle,
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003506 InliningKind inlining_kind)
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003507 : owner_(owner),
3508 compilation_info_(info),
3509 oracle_(oracle),
3510 call_context_(NULL),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003511 inlining_kind_(inlining_kind),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003512 function_return_(NULL),
3513 test_context_(NULL),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003514 entry_(NULL),
3515 arguments_elements_(NULL),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003516 outer_(owner->function_state()) {
3517 if (outer_ != NULL) {
3518 // State for an inline function.
3519 if (owner->ast_context()->IsTest()) {
3520 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
3521 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
3522 if_true->MarkAsInlineReturnTarget();
3523 if_false->MarkAsInlineReturnTarget();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003524 TestContext* outer_test_context = TestContext::cast(owner->ast_context());
3525 Expression* cond = outer_test_context->condition();
3526 TypeFeedbackOracle* outer_oracle = outer_test_context->oracle();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003527 // The AstContext constructor pushed on the context stack. This newed
3528 // instance is the reason that AstContext can't be BASE_EMBEDDED.
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003529 test_context_ =
3530 new TestContext(owner, cond, outer_oracle, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003531 } else {
3532 function_return_ = owner->graph()->CreateBasicBlock();
3533 function_return()->MarkAsInlineReturnTarget();
3534 }
3535 // Set this after possibly allocating a new TestContext above.
3536 call_context_ = owner->ast_context();
3537 }
3538
3539 // Push on the state stack.
3540 owner->set_function_state(this);
3541}
3542
3543
3544FunctionState::~FunctionState() {
3545 delete test_context_;
3546 owner_->set_function_state(outer_);
3547}
3548
3549
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003550// Implementation of utility classes to represent an expression's context in
3551// the AST.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003552AstContext::AstContext(HOptimizedGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003553 : owner_(owner),
3554 kind_(kind),
3555 outer_(owner->ast_context()),
3556 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003557 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003558#ifdef DEBUG
ulan@chromium.org967e2702012-02-28 09:49:15 +00003559 ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
lrn@chromium.org5d00b602011-01-05 09:51:43 +00003560 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003561#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003562}
3563
3564
3565AstContext::~AstContext() {
3566 owner_->set_ast_context(outer_); // Pop.
3567}
3568
3569
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003570EffectContext::~EffectContext() {
3571 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003572 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00003573 (owner()->environment()->length() == original_length_ &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00003574 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003575}
3576
3577
3578ValueContext::~ValueContext() {
3579 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003580 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00003581 (owner()->environment()->length() == original_length_ + 1 &&
ulan@chromium.org967e2702012-02-28 09:49:15 +00003582 owner()->environment()->frame_type() == JS_FUNCTION));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003583}
3584
3585
3586void EffectContext::ReturnValue(HValue* value) {
3587 // The value is simply ignored.
3588}
3589
3590
3591void ValueContext::ReturnValue(HValue* value) {
3592 // The value is tracked in the bailout environment, and communicated
3593 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003594 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
3595 owner()->Bailout("bad value context for arguments value");
3596 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003597 owner()->Push(value);
3598}
3599
3600
3601void TestContext::ReturnValue(HValue* value) {
3602 BuildBranch(value);
3603}
3604
3605
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003606void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003607 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003608 owner()->AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003609 if (instr->HasObservableSideEffects()) {
3610 owner()->AddSimulate(ast_id, REMOVABLE_SIMULATE);
3611 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003612}
3613
3614
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003615void EffectContext::ReturnControl(HControlInstruction* instr,
3616 BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003617 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003618 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
3619 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
3620 instr->SetSuccessorAt(0, empty_true);
3621 instr->SetSuccessorAt(1, empty_false);
3622 owner()->current_block()->Finish(instr);
3623 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
3624 owner()->set_current_block(join);
3625}
3626
3627
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003628void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003629 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003630 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003631 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003632 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003633 owner()->AddInstruction(instr);
3634 owner()->Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003635 if (instr->HasObservableSideEffects()) {
3636 owner()->AddSimulate(ast_id, REMOVABLE_SIMULATE);
3637 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003638}
3639
3640
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003641void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003642 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003643 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
3644 return owner()->Bailout("bad value context for arguments object value");
3645 }
3646 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
3647 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
3648 instr->SetSuccessorAt(0, materialize_true);
3649 instr->SetSuccessorAt(1, materialize_false);
3650 owner()->current_block()->Finish(instr);
3651 owner()->set_current_block(materialize_true);
3652 owner()->Push(owner()->graph()->GetConstantTrue());
3653 owner()->set_current_block(materialize_false);
3654 owner()->Push(owner()->graph()->GetConstantFalse());
3655 HBasicBlock* join =
3656 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
3657 owner()->set_current_block(join);
3658}
3659
3660
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003661void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003662 ASSERT(!instr->IsControlInstruction());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003663 HOptimizedGraphBuilder* builder = owner();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003664 builder->AddInstruction(instr);
3665 // We expect a simulate after every expression with side effects, though
3666 // this one isn't actually needed (and wouldn't work if it were targeted).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003667 if (instr->HasObservableSideEffects()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003668 builder->Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003669 builder->AddSimulate(ast_id, REMOVABLE_SIMULATE);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003670 builder->Pop();
3671 }
3672 BuildBranch(instr);
3673}
3674
3675
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003676void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003677 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003678 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
3679 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
3680 instr->SetSuccessorAt(0, empty_true);
3681 instr->SetSuccessorAt(1, empty_false);
3682 owner()->current_block()->Finish(instr);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00003683 empty_true->Goto(if_true(), owner()->function_state());
3684 empty_false->Goto(if_false(), owner()->function_state());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003685 owner()->set_current_block(NULL);
3686}
3687
3688
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003689void TestContext::BuildBranch(HValue* value) {
3690 // We expect the graph to be in edge-split form: there is no edge that
3691 // connects a branch node to a join node. We conservatively ensure that
3692 // property by always adding an empty block on the outgoing edges of this
3693 // branch.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003694 HOptimizedGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003695 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003696 builder->Bailout("arguments object value in a test context");
3697 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +00003698 if (value->IsConstant()) {
3699 HConstant* constant_value = HConstant::cast(value);
3700 if (constant_value->ToBoolean()) {
3701 builder->current_block()->Goto(if_true(), builder->function_state());
3702 } else {
3703 builder->current_block()->Goto(if_false(), builder->function_state());
3704 }
3705 builder->set_current_block(NULL);
3706 return;
3707 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003708 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
3709 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003710 TypeFeedbackId test_id = condition()->test_id();
3711 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org2c99e282011-07-28 09:15:17 +00003712 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003713 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003714
ulan@chromium.org2e04b582013-02-21 14:06:02 +00003715 empty_true->Goto(if_true(), builder->function_state());
3716 empty_false->Goto(if_false(), builder->function_state());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003717 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003718}
3719
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003720
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003721// HOptimizedGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003722#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003723 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00003724 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003725 if (HasStackOverflow()) return; \
3726 } while (false)
3727
3728
danno@chromium.org160a7b02011-04-18 15:51:38 +00003729#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003730 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00003731 call; \
3732 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003733 } while (false)
3734
3735
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003736void HOptimizedGraphBuilder::Bailout(const char* reason) {
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003737 info()->set_bailout_reason(reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003738 SetStackOverflow();
3739}
3740
3741
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003742void HOptimizedGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003743 EffectContext for_effect(this);
3744 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003745}
3746
3747
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003748void HOptimizedGraphBuilder::VisitForValue(Expression* expr,
3749 ArgumentsAllowedFlag flag) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003750 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003751 Visit(expr);
3752}
3753
3754
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003755void HOptimizedGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003756 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003757 for_value.set_for_typeof(true);
3758 Visit(expr);
3759}
3760
3761
3762
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003763void HOptimizedGraphBuilder::VisitForControl(Expression* expr,
3764 HBasicBlock* true_block,
3765 HBasicBlock* false_block) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00003766 TestContext for_test(this, expr, oracle(), true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003767 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003768}
3769
3770
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003771void HOptimizedGraphBuilder::VisitArgument(Expression* expr) {
verwaest@chromium.org753aee42012-07-17 16:15:42 +00003772 CHECK_ALIVE(VisitForValue(expr));
3773 Push(AddInstruction(new(zone()) HPushArgument(Pop())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003774}
3775
3776
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003777void HOptimizedGraphBuilder::VisitArgumentList(
3778 ZoneList<Expression*>* arguments) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003779 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003780 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003781 }
3782}
3783
3784
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003785void HOptimizedGraphBuilder::VisitExpressions(
3786 ZoneList<Expression*>* exprs) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003787 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003788 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003789 }
3790}
3791
3792
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003793bool HOptimizedGraphBuilder::BuildGraph() {
3794 Scope* scope = info()->scope();
3795 if (scope->HasIllegalRedeclaration()) {
3796 Bailout("function with illegal redeclaration");
3797 return false;
3798 }
3799 if (scope->calls_eval()) {
3800 Bailout("function calls eval");
3801 return false;
3802 }
3803 SetUpScope(scope);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003804
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003805 // Add an edge to the body entry. This is warty: the graph's start
3806 // environment will be used by the Lithium translation as the initial
3807 // environment on graph entry, but it has now been mutated by the
3808 // Hydrogen translation of the instructions in the start block. This
3809 // environment uses values which have not been defined yet. These
3810 // Hydrogen instructions will then be replayed by the Lithium
3811 // translation, so they cannot have an environment effect. The edge to
3812 // the body's entry block (along with some special logic for the start
3813 // block in HInstruction::InsertAfter) seals the start block from
3814 // getting unwanted instructions inserted.
3815 //
3816 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
3817 // Make the Hydrogen instructions in the initial block into Hydrogen
3818 // values (but not instructions), present in the initial environment and
3819 // not replayed by the Lithium translation.
3820 HEnvironment* initial_env = environment()->CopyWithoutHistory();
3821 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
3822 current_block()->Goto(body_entry);
3823 body_entry->SetJoinId(BailoutId::FunctionEntry());
3824 set_current_block(body_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003825
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003826 // Handle implicit declaration of the function name in named function
3827 // expressions before other declarations.
3828 if (scope->is_function_scope() && scope->function() != NULL) {
3829 VisitVariableDeclaration(scope->function());
3830 }
3831 VisitDeclarations(scope->declarations());
3832 AddSimulate(BailoutId::Declarations());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003833
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003834 HValue* context = environment()->LookupContext();
3835 AddInstruction(
3836 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003837
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003838 VisitStatements(info()->function()->body());
3839 if (HasStackOverflow()) return false;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003840
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003841 if (current_block() != NULL) {
yangguo@chromium.org4cd70b42013-01-04 08:57:54 +00003842 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined(),
3843 context);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003844 current_block()->FinishExit(instr);
3845 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003846 }
3847
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003848 // If the checksum of the number of type info changes is the same as the
3849 // last time this function was compiled, then this recompile is likely not
3850 // due to missing/inadequate type feedback, but rather too aggressive
3851 // optimization. Disable optimistic LICM in that case.
3852 Handle<Code> unoptimized_code(info()->shared_info()->code());
3853 ASSERT(unoptimized_code->kind() == Code::FUNCTION);
3854 Handle<TypeFeedbackInfo> type_info(
3855 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
3856 int checksum = type_info->own_type_change_checksum();
3857 int composite_checksum = graph()->update_type_change_checksum(checksum);
3858 graph()->set_use_optimistic_licm(
3859 !type_info->matches_inlined_type_change_checksum(composite_checksum));
3860 type_info->set_inlined_type_change_checksum(composite_checksum);
3861
3862 return true;
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003863}
3864
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00003865
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003866void HGraph::GlobalValueNumbering() {
3867 // Perform common subexpression elimination and loop-invariant code motion.
3868 if (FLAG_use_gvn) {
3869 HPhase phase("H_Global value numbering", this);
3870 HGlobalValueNumberer gvn(this, info());
3871 bool removed_side_effects = gvn.Analyze();
3872 // Trigger a second analysis pass to further eliminate duplicate values that
3873 // could only be discovered by removing side-effect-generating instructions
3874 // during the first pass.
3875 if (FLAG_smi_only_arrays && removed_side_effects) {
3876 removed_side_effects = gvn.Analyze();
3877 ASSERT(!removed_side_effects);
3878 }
3879 }
3880}
3881
3882
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003883bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003884 *bailout_reason = SmartArrayPointer<char>();
3885 OrderBlocks();
3886 AssignDominators();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003887
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00003888 // We need to create a HConstant "zero" now so that GVN will fold every
3889 // zero-valued constant in the graph together.
3890 // The constant is needed to make idef-based bounds check work: the pass
3891 // evaluates relations with "zero" and that zero cannot be created after GVN.
3892 GetConstant0();
3893
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003894#ifdef DEBUG
3895 // Do a full verify after building the graph and computing dominators.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003896 Verify(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003897#endif
3898
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003899 PropagateDeoptimizingMark();
3900 if (!CheckConstPhiUses()) {
3901 *bailout_reason = SmartArrayPointer<char>(StrDup(
3902 "Unsupported phi use of const variable"));
3903 return false;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003904 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003905 EliminateRedundantPhis();
3906 if (!CheckArgumentsPhiUses()) {
3907 *bailout_reason = SmartArrayPointer<char>(StrDup(
3908 "Unsupported phi use of arguments"));
3909 return false;
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00003910 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003911 if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
3912 CollectPhis();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003913
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003914 if (has_osr_loop_entry()) {
3915 const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00003916 for (int j = 0; j < phis->length(); j++) {
3917 HPhi* phi = phis->at(j);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003918 osr_values()->at(phi->merged_index())->set_incoming_value(phi);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00003919 }
3920 }
3921
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003922 HInferRepresentation rep(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003923 rep.Analyze();
3924
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003925 // Remove HSimulate instructions that have turned out not to be needed
3926 // after all by folding them into the following HSimulate.
3927 // This must happen after inferring representations.
3928 MergeRemovableSimulates();
3929
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003930 MarkDeoptimizeOnUndefined();
3931 InsertRepresentationChanges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003932
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003933 InitializeInferredTypes();
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003934
3935 // Must be performed before canonicalization to ensure that Canonicalize
3936 // will not remove semantically meaningful ToInt32 operations e.g. BIT_OR with
3937 // zero.
3938 ComputeSafeUint32Operations();
3939
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003940 Canonicalize();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003941
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003942 GlobalValueNumbering();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003943
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003944 if (FLAG_use_range) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003945 HRangeAnalysis rangeAnalysis(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003946 rangeAnalysis.Analyze();
3947 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003948 ComputeMinusZeroChecks();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003949
3950 // Eliminate redundant stack checks on backwards branches.
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003951 HStackCheckEliminator sce(this);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003952 sce.Process();
3953
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003954 if (FLAG_idefs) SetupInformativeDefinitions();
3955 if (FLAG_array_bounds_checks_elimination && !FLAG_idefs) {
3956 EliminateRedundantBoundsChecks();
3957 }
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00003958 if (FLAG_array_index_dehoisting) DehoistSimpleArrayIndexComputations();
jkummerow@chromium.orgc1956672012-10-11 15:57:38 +00003959 if (FLAG_dead_code_elimination) DeadCodeElimination();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003960
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00003961 RestoreActualValues();
3962
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00003963 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003964}
3965
3966
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003967void HGraph::SetupInformativeDefinitionsInBlock(HBasicBlock* block) {
3968 for (int phi_index = 0; phi_index < block->phis()->length(); phi_index++) {
3969 HPhi* phi = block->phis()->at(phi_index);
3970 phi->AddInformativeDefinitions();
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00003971 phi->SetFlag(HValue::kIDefsProcessingDone);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003972 // We do not support phis that "redefine just one operand".
3973 ASSERT(!phi->IsInformativeDefinition());
3974 }
3975
3976 for (HInstruction* i = block->first(); i != NULL; i = i->next()) {
3977 i->AddInformativeDefinitions();
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00003978 i->SetFlag(HValue::kIDefsProcessingDone);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00003979 i->UpdateRedefinedUsesWhileSettingUpInformativeDefinitions();
3980 }
3981}
3982
3983
3984// This method is recursive, so if its stack frame is large it could
3985// cause a stack overflow.
3986// To keep the individual stack frames small we do the actual work inside
3987// SetupInformativeDefinitionsInBlock();
3988void HGraph::SetupInformativeDefinitionsRecursively(HBasicBlock* block) {
3989 SetupInformativeDefinitionsInBlock(block);
3990 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
3991 SetupInformativeDefinitionsRecursively(block->dominated_blocks()->at(i));
3992 }
3993}
3994
3995
3996void HGraph::SetupInformativeDefinitions() {
3997 HPhase phase("H_Setup informative definitions", this);
3998 SetupInformativeDefinitionsRecursively(entry_block());
3999}
4000
4001
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004002// We try to "factor up" HBoundsCheck instructions towards the root of the
4003// dominator tree.
4004// For now we handle checks where the index is like "exp + int32value".
4005// If in the dominator tree we check "exp + v1" and later (dominated)
4006// "exp + v2", if v2 <= v1 we can safely remove the second check, and if
4007// v2 > v1 we can use v2 in the 1st check and again remove the second.
4008// To do so we keep a dictionary of all checks where the key if the pair
4009// "exp, length".
4010// The class BoundsCheckKey represents this key.
4011class BoundsCheckKey : public ZoneObject {
4012 public:
4013 HValue* IndexBase() const { return index_base_; }
4014 HValue* Length() const { return length_; }
4015
4016 uint32_t Hash() {
4017 return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
4018 }
4019
4020 static BoundsCheckKey* Create(Zone* zone,
4021 HBoundsCheck* check,
4022 int32_t* offset) {
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004023 if (!check->index()->representation().IsInteger32()) return NULL;
4024
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004025 HValue* index_base = NULL;
4026 HConstant* constant = NULL;
4027 bool is_sub = false;
4028
4029 if (check->index()->IsAdd()) {
4030 HAdd* index = HAdd::cast(check->index());
4031 if (index->left()->IsConstant()) {
4032 constant = HConstant::cast(index->left());
4033 index_base = index->right();
4034 } else if (index->right()->IsConstant()) {
4035 constant = HConstant::cast(index->right());
4036 index_base = index->left();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004037 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004038 } else if (check->index()->IsSub()) {
4039 HSub* index = HSub::cast(check->index());
4040 is_sub = true;
4041 if (index->left()->IsConstant()) {
4042 constant = HConstant::cast(index->left());
4043 index_base = index->right();
4044 } else if (index->right()->IsConstant()) {
4045 constant = HConstant::cast(index->right());
4046 index_base = index->left();
4047 }
4048 }
4049
4050 if (constant != NULL && constant->HasInteger32Value()) {
4051 *offset = is_sub ? - constant->Integer32Value()
4052 : constant->Integer32Value();
4053 } else {
4054 *offset = 0;
4055 index_base = check->index();
4056 }
4057
4058 return new(zone) BoundsCheckKey(index_base, check->length());
4059 }
4060
4061 private:
4062 BoundsCheckKey(HValue* index_base, HValue* length)
4063 : index_base_(index_base),
4064 length_(length) { }
4065
4066 HValue* index_base_;
4067 HValue* length_;
4068};
4069
4070
4071// Data about each HBoundsCheck that can be eliminated or moved.
4072// It is the "value" in the dictionary indexed by "base-index, length"
4073// (the key is BoundsCheckKey).
4074// We scan the code with a dominator tree traversal.
4075// Traversing the dominator tree we keep a stack (implemented as a singly
4076// linked list) of "data" for each basic block that contains a relevant check
4077// with the same key (the dictionary holds the head of the list).
4078// We also keep all the "data" created for a given basic block in a list, and
4079// use it to "clean up" the dictionary when backtracking in the dominator tree
4080// traversal.
4081// Doing this each dictionary entry always directly points to the check that
4082// is dominating the code being examined now.
4083// We also track the current "offset" of the index expression and use it to
4084// decide if any check is already "covered" (so it can be removed) or not.
4085class BoundsCheckBbData: public ZoneObject {
4086 public:
4087 BoundsCheckKey* Key() const { return key_; }
4088 int32_t LowerOffset() const { return lower_offset_; }
4089 int32_t UpperOffset() const { return upper_offset_; }
4090 HBasicBlock* BasicBlock() const { return basic_block_; }
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004091 HBoundsCheck* LowerCheck() const { return lower_check_; }
4092 HBoundsCheck* UpperCheck() const { return upper_check_; }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004093 BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
4094 BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
4095
4096 bool OffsetIsCovered(int32_t offset) const {
4097 return offset >= LowerOffset() && offset <= UpperOffset();
4098 }
4099
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004100 bool HasSingleCheck() { return lower_check_ == upper_check_; }
4101
4102 // The goal of this method is to modify either upper_offset_ or
4103 // lower_offset_ so that also new_offset is covered (the covered
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004104 // range grows).
4105 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004106 // The precondition is that new_check follows UpperCheck() and
4107 // LowerCheck() in the same basic block, and that new_offset is not
4108 // covered (otherwise we could simply remove new_check).
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004109 //
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004110 // If HasSingleCheck() is true then new_check is added as "second check"
4111 // (either upper or lower; note that HasSingleCheck() becomes false).
4112 // Otherwise one of the current checks is modified so that it also covers
4113 // new_offset, and new_check is removed.
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004114 //
4115 // If the check cannot be modified because the context is unknown it
4116 // returns false, otherwise it returns true.
4117 bool CoverCheck(HBoundsCheck* new_check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004118 int32_t new_offset) {
4119 ASSERT(new_check->index()->representation().IsInteger32());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004120 bool keep_new_check = false;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004121
4122 if (new_offset > upper_offset_) {
4123 upper_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004124 if (HasSingleCheck()) {
4125 keep_new_check = true;
4126 upper_check_ = new_check;
4127 } else {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004128 bool result = BuildOffsetAdd(upper_check_,
4129 &added_upper_index_,
4130 &added_upper_offset_,
4131 Key()->IndexBase(),
4132 new_check->index()->representation(),
4133 new_offset);
4134 if (!result) return false;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004135 upper_check_->ReplaceAllUsesWith(upper_check_->index());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004136 upper_check_->SetOperandAt(0, added_upper_index_);
4137 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004138 } else if (new_offset < lower_offset_) {
4139 lower_offset_ = new_offset;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004140 if (HasSingleCheck()) {
4141 keep_new_check = true;
4142 lower_check_ = new_check;
4143 } else {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004144 bool result = BuildOffsetAdd(lower_check_,
4145 &added_lower_index_,
4146 &added_lower_offset_,
4147 Key()->IndexBase(),
4148 new_check->index()->representation(),
4149 new_offset);
4150 if (!result) return false;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004151 lower_check_->ReplaceAllUsesWith(lower_check_->index());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004152 lower_check_->SetOperandAt(0, added_lower_index_);
4153 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004154 } else {
4155 ASSERT(false);
4156 }
4157
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004158 if (!keep_new_check) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004159 new_check->DeleteAndReplaceWith(new_check->ActualValue());
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004160 }
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004161
4162 return true;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004163 }
4164
4165 void RemoveZeroOperations() {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004166 RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
4167 RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004168 }
4169
4170 BoundsCheckBbData(BoundsCheckKey* key,
4171 int32_t lower_offset,
4172 int32_t upper_offset,
4173 HBasicBlock* bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004174 HBoundsCheck* lower_check,
4175 HBoundsCheck* upper_check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004176 BoundsCheckBbData* next_in_bb,
4177 BoundsCheckBbData* father_in_dt)
4178 : key_(key),
4179 lower_offset_(lower_offset),
4180 upper_offset_(upper_offset),
4181 basic_block_(bb),
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004182 lower_check_(lower_check),
4183 upper_check_(upper_check),
4184 added_lower_index_(NULL),
4185 added_lower_offset_(NULL),
4186 added_upper_index_(NULL),
4187 added_upper_offset_(NULL),
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004188 next_in_bb_(next_in_bb),
4189 father_in_dt_(father_in_dt) { }
4190
4191 private:
4192 BoundsCheckKey* key_;
4193 int32_t lower_offset_;
4194 int32_t upper_offset_;
4195 HBasicBlock* basic_block_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004196 HBoundsCheck* lower_check_;
4197 HBoundsCheck* upper_check_;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004198 HInstruction* added_lower_index_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004199 HConstant* added_lower_offset_;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004200 HInstruction* added_upper_index_;
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004201 HConstant* added_upper_offset_;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004202 BoundsCheckBbData* next_in_bb_;
4203 BoundsCheckBbData* father_in_dt_;
4204
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004205 // Given an existing add instruction and a bounds check it tries to
4206 // find the current context (either of the add or of the check index).
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004207 HValue* IndexContext(HInstruction* add, HBoundsCheck* check) {
4208 if (add != NULL && add->IsAdd()) {
4209 return HAdd::cast(add)->context();
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004210 }
4211 if (check->index()->IsBinaryOperation()) {
4212 return HBinaryOperation::cast(check->index())->context();
4213 }
4214 return NULL;
4215 }
4216
4217 // This function returns false if it cannot build the add because the
4218 // current context cannot be determined.
4219 bool BuildOffsetAdd(HBoundsCheck* check,
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004220 HInstruction** add,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004221 HConstant** constant,
4222 HValue* original_value,
4223 Representation representation,
4224 int32_t new_offset) {
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004225 HValue* index_context = IndexContext(*add, check);
4226 if (index_context == NULL) return false;
4227
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004228 HConstant* new_constant = new(BasicBlock()->zone())
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00004229 HConstant(new_offset, Representation::Integer32());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004230 if (*add == NULL) {
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004231 new_constant->InsertBefore(check);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004232 (*add) = HAdd::New(
4233 BasicBlock()->zone(), index_context, original_value, new_constant);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004234 (*add)->AssumeRepresentation(representation);
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004235 (*add)->InsertBefore(check);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004236 } else {
4237 new_constant->InsertBefore(*add);
4238 (*constant)->DeleteAndReplaceWith(new_constant);
4239 }
4240 *constant = new_constant;
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004241 return true;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004242 }
4243
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004244 void RemoveZeroAdd(HInstruction** add, HConstant** constant) {
4245 if (*add != NULL && (*add)->IsAdd() && (*constant)->Integer32Value() == 0) {
4246 (*add)->DeleteAndReplaceWith(HAdd::cast(*add)->left());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004247 (*constant)->DeleteAndReplaceWith(NULL);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004248 }
4249 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004250};
4251
4252
4253static bool BoundsCheckKeyMatch(void* key1, void* key2) {
4254 BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
4255 BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
4256 return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
4257}
4258
4259
4260class BoundsCheckTable : private ZoneHashMap {
4261 public:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004262 BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004263 return reinterpret_cast<BoundsCheckBbData**>(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004264 &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004265 }
4266
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004267 void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone) {
4268 Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004269 }
4270
4271 void Delete(BoundsCheckKey* key) {
4272 Remove(key, key->Hash());
4273 }
4274
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004275 explicit BoundsCheckTable(Zone* zone)
4276 : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
4277 ZoneAllocationPolicy(zone)) { }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004278};
4279
4280
4281// Eliminates checks in bb and recursively in the dominated blocks.
4282// Also replace the results of check instructions with the original value, if
4283// the result is used. This is safe now, since we don't do code motion after
4284// this point. It enables better register allocation since the value produced
4285// by check instructions is really a copy of the original value.
4286void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
4287 BoundsCheckTable* table) {
4288 BoundsCheckBbData* bb_data_list = NULL;
4289
4290 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
4291 if (!i->IsBoundsCheck()) continue;
4292
4293 HBoundsCheck* check = HBoundsCheck::cast(i);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004294 int32_t offset;
4295 BoundsCheckKey* key =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004296 BoundsCheckKey::Create(zone(), check, &offset);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004297 if (key == NULL) continue;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004298 BoundsCheckBbData** data_p = table->LookupOrInsert(key, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004299 BoundsCheckBbData* data = *data_p;
4300 if (data == NULL) {
4301 bb_data_list = new(zone()) BoundsCheckBbData(key,
4302 offset,
4303 offset,
4304 bb,
4305 check,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004306 check,
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004307 bb_data_list,
4308 NULL);
4309 *data_p = bb_data_list;
4310 } else if (data->OffsetIsCovered(offset)) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004311 check->DeleteAndReplaceWith(check->ActualValue());
mstarzinger@chromium.org0ae265a2012-12-11 17:41:11 +00004312 } else if (data->BasicBlock() != bb ||
4313 !data->CoverCheck(check, offset)) {
4314 // If the check is in the current BB we try to modify it by calling
4315 // "CoverCheck", but if also that fails we record the current offsets
4316 // in a new data instance because from now on they are covered.
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004317 int32_t new_lower_offset = offset < data->LowerOffset()
4318 ? offset
4319 : data->LowerOffset();
4320 int32_t new_upper_offset = offset > data->UpperOffset()
4321 ? offset
4322 : data->UpperOffset();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004323 bb_data_list = new(zone()) BoundsCheckBbData(key,
4324 new_lower_offset,
4325 new_upper_offset,
4326 bb,
danno@chromium.org81cac2b2012-07-10 11:28:27 +00004327 data->LowerCheck(),
4328 data->UpperCheck(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004329 bb_data_list,
4330 data);
4331 table->Insert(key, bb_data_list, zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004332 }
4333 }
4334
4335 for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
4336 EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i), table);
4337 }
4338
4339 for (BoundsCheckBbData* data = bb_data_list;
4340 data != NULL;
4341 data = data->NextInBasicBlock()) {
4342 data->RemoveZeroOperations();
4343 if (data->FatherInDominatorTree()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004344 table->Insert(data->Key(), data->FatherInDominatorTree(), zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004345 } else {
4346 table->Delete(data->Key());
4347 }
4348 }
4349}
4350
4351
4352void HGraph::EliminateRedundantBoundsChecks() {
4353 HPhase phase("H_Eliminate bounds checks", this);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004354 BoundsCheckTable checks_table(zone());
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00004355 EliminateRedundantBoundsChecks(entry_block(), &checks_table);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004356}
4357
4358
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00004359static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004360 HValue* index = array_operation->GetKey()->ActualValue();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004361 if (!index->representation().IsInteger32()) return;
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00004362
4363 HConstant* constant;
4364 HValue* subexpression;
4365 int32_t sign;
4366 if (index->IsAdd()) {
4367 sign = 1;
4368 HAdd* add = HAdd::cast(index);
4369 if (add->left()->IsConstant()) {
4370 subexpression = add->right();
4371 constant = HConstant::cast(add->left());
4372 } else if (add->right()->IsConstant()) {
4373 subexpression = add->left();
4374 constant = HConstant::cast(add->right());
4375 } else {
4376 return;
4377 }
4378 } else if (index->IsSub()) {
4379 sign = -1;
4380 HSub* sub = HSub::cast(index);
4381 if (sub->left()->IsConstant()) {
4382 subexpression = sub->right();
4383 constant = HConstant::cast(sub->left());
4384 } else if (sub->right()->IsConstant()) {
4385 subexpression = sub->left();
4386 constant = HConstant::cast(sub->right());
4387 } return;
4388 } else {
4389 return;
4390 }
4391
4392 if (!constant->HasInteger32Value()) return;
4393 int32_t value = constant->Integer32Value() * sign;
4394 // We limit offset values to 30 bits because we want to avoid the risk of
4395 // overflows when the offset is added to the object header size.
4396 if (value >= 1 << 30 || value < 0) return;
4397 array_operation->SetKey(subexpression);
4398 if (index->HasNoUses()) {
4399 index->DeleteAndReplaceWith(NULL);
4400 }
4401 ASSERT(value >= 0);
4402 array_operation->SetIndexOffset(static_cast<uint32_t>(value));
4403 array_operation->SetDehoisted(true);
4404}
4405
4406
4407void HGraph::DehoistSimpleArrayIndexComputations() {
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00004408 HPhase phase("H_Dehoist index computations", this);
4409 for (int i = 0; i < blocks()->length(); ++i) {
4410 for (HInstruction* instr = blocks()->at(i)->first();
4411 instr != NULL;
4412 instr = instr->next()) {
4413 ArrayInstructionInterface* array_instruction = NULL;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00004414 if (instr->IsLoadKeyed()) {
4415 HLoadKeyed* op = HLoadKeyed::cast(instr);
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00004416 array_instruction = static_cast<ArrayInstructionInterface*>(op);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00004417 } else if (instr->IsStoreKeyed()) {
4418 HStoreKeyed* op = HStoreKeyed::cast(instr);
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00004419 array_instruction = static_cast<ArrayInstructionInterface*>(op);
4420 } else {
4421 continue;
4422 }
4423 DehoistArrayIndex(array_instruction);
4424 }
4425 }
4426}
4427
4428
jkummerow@chromium.orgc1956672012-10-11 15:57:38 +00004429void HGraph::DeadCodeElimination() {
4430 HPhase phase("H_Dead code elimination", this);
4431 ZoneList<HInstruction*> worklist(blocks_.length(), zone());
4432 for (int i = 0; i < blocks()->length(); ++i) {
4433 for (HInstruction* instr = blocks()->at(i)->first();
4434 instr != NULL;
4435 instr = instr->next()) {
4436 if (instr->IsDead()) worklist.Add(instr, zone());
4437 }
4438 }
4439
4440 while (!worklist.is_empty()) {
4441 HInstruction* instr = worklist.RemoveLast();
4442 if (FLAG_trace_dead_code_elimination) {
4443 HeapStringAllocator allocator;
4444 StringStream stream(&allocator);
4445 instr->PrintNameTo(&stream);
4446 stream.Add(" = ");
4447 instr->PrintTo(&stream);
4448 PrintF("[removing dead instruction %s]\n", *stream.ToCString());
4449 }
4450 instr->DeleteAndReplaceWith(NULL);
4451 for (int i = 0; i < instr->OperandCount(); ++i) {
4452 HValue* operand = instr->OperandAt(i);
4453 if (operand->IsDead()) worklist.Add(HInstruction::cast(operand), zone());
4454 }
4455 }
4456}
4457
4458
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004459void HGraph::RestoreActualValues() {
4460 HPhase phase("H_Restore actual values", this);
4461
4462 for (int block_index = 0; block_index < blocks()->length(); block_index++) {
4463 HBasicBlock* block = blocks()->at(block_index);
4464
4465#ifdef DEBUG
4466 for (int i = 0; i < block->phis()->length(); i++) {
4467 HPhi* phi = block->phis()->at(i);
4468 ASSERT(phi->ActualValue() == phi);
4469 }
4470#endif
4471
4472 for (HInstruction* instruction = block->first();
4473 instruction != NULL;
4474 instruction = instruction->next()) {
4475 if (instruction->ActualValue() != instruction) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004476 ASSERT(instruction->IsInformativeDefinition());
4477 if (instruction->IsPurelyInformativeDefinition()) {
4478 instruction->DeleteAndReplaceWith(instruction->RedefinedOperand());
4479 } else {
4480 instruction->ReplaceAllUsesWith(instruction->ActualValue());
4481 }
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00004482 }
4483 }
4484 }
4485}
4486
4487
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004488void HOptimizedGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004489 ASSERT(current_block() != NULL);
4490 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004491}
4492
4493
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004494void HOptimizedGraphBuilder::PushAndAdd(HInstruction* instr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004495 Push(instr);
4496 AddInstruction(instr);
4497}
4498
4499
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00004500void HOptimizedGraphBuilder::AddSoftDeoptimize() {
4501 if (FLAG_always_opt) return;
4502 if (current_block()->IsDeoptimizing()) return;
4503 AddInstruction(new(zone()) HSoftDeoptimize());
4504 current_block()->MarkAsDeoptimizing();
4505 graph()->set_has_soft_deoptimize(true);
4506}
4507
4508
ulan@chromium.org967e2702012-02-28 09:49:15 +00004509template <class Instruction>
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004510HInstruction* HOptimizedGraphBuilder::PreProcessCall(Instruction* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004511 int count = call->argument_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004512 ZoneList<HValue*> arguments(count, zone());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004513 for (int i = 0; i < count; ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00004514 arguments.Add(Pop(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004515 }
4516
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004517 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004518 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004519 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004520 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004521}
4522
4523
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004524void HOptimizedGraphBuilder::SetUpScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004525 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004526 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004527 AddInstruction(undefined_constant);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004528 graph()->set_undefined_constant(undefined_constant);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004529
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00004530 HArgumentsObject* object = new(zone()) HArgumentsObject;
4531 AddInstruction(object);
4532 graph()->SetArgumentsObject(object);
4533
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004534 // Set the initial values of parameters including "this". "This" has
4535 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004536 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
4537
4538 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004539 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004540 environment()->Bind(i, parameter);
4541 }
4542
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004543 // First special is HContext.
4544 HInstruction* context = AddInstruction(new(zone()) HContext);
4545 environment()->BindContext(context);
4546
4547 // Initialize specials and locals to undefined.
4548 for (int i = environment()->parameter_count() + 1;
4549 i < environment()->length();
4550 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004551 environment()->Bind(i, undefined_constant);
4552 }
4553
4554 // Handle the arguments and arguments shadow variables specially (they do
4555 // not have declarations).
4556 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004557 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004558 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004559 }
yangguo@chromium.org154ff992012-03-13 08:09:54 +00004560
yangguo@chromium.org154ff992012-03-13 08:09:54 +00004561 environment()->Bind(scope->arguments(),
4562 graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004563 }
4564}
4565
4566
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004567void HOptimizedGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004568 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004569 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004570 }
4571}
4572
4573
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004574void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004575 ASSERT(!HasStackOverflow());
4576 ASSERT(current_block() != NULL);
4577 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004578 if (stmt->scope() != NULL) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004579 return Bailout("ScopedBlock");
4580 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004581 BreakAndContinueInfo break_info(stmt);
4582 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004583 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004584 }
4585 HBasicBlock* break_block = break_info.break_block();
4586 if (break_block != NULL) {
4587 if (current_block() != NULL) current_block()->Goto(break_block);
4588 break_block->SetJoinId(stmt->ExitId());
4589 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004590 }
4591}
4592
4593
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004594void HOptimizedGraphBuilder::VisitExpressionStatement(
4595 ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004596 ASSERT(!HasStackOverflow());
4597 ASSERT(current_block() != NULL);
4598 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004599 VisitForEffect(stmt->expression());
4600}
4601
4602
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004603void HOptimizedGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004604 ASSERT(!HasStackOverflow());
4605 ASSERT(current_block() != NULL);
4606 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004607}
4608
4609
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004610void HOptimizedGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004611 ASSERT(!HasStackOverflow());
4612 ASSERT(current_block() != NULL);
4613 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004614 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004615 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004616 Visit(stmt->then_statement());
4617 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004618 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004619 Visit(stmt->else_statement());
4620 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004621 HBasicBlock* cond_true = graph()->CreateBasicBlock();
4622 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00004623 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004624
danno@chromium.org160a7b02011-04-18 15:51:38 +00004625 if (cond_true->HasPredecessor()) {
4626 cond_true->SetJoinId(stmt->ThenId());
4627 set_current_block(cond_true);
4628 CHECK_BAILOUT(Visit(stmt->then_statement()));
4629 cond_true = current_block();
4630 } else {
4631 cond_true = NULL;
4632 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004633
danno@chromium.org160a7b02011-04-18 15:51:38 +00004634 if (cond_false->HasPredecessor()) {
4635 cond_false->SetJoinId(stmt->ElseId());
4636 set_current_block(cond_false);
4637 CHECK_BAILOUT(Visit(stmt->else_statement()));
4638 cond_false = current_block();
4639 } else {
4640 cond_false = NULL;
4641 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004642
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004643 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004644 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004645 }
4646}
4647
4648
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004649HBasicBlock* HOptimizedGraphBuilder::BreakAndContinueScope::Get(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004650 BreakableStatement* stmt,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004651 BreakType type,
4652 int* drop_extra) {
4653 *drop_extra = 0;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004654 BreakAndContinueScope* current = this;
4655 while (current != NULL && current->info()->target() != stmt) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004656 *drop_extra += current->info()->drop_extra();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004657 current = current->next();
4658 }
4659 ASSERT(current != NULL); // Always found (unless stack is malformed).
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004660
4661 if (type == BREAK) {
4662 *drop_extra += current->info()->drop_extra();
4663 }
4664
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004665 HBasicBlock* block = NULL;
4666 switch (type) {
4667 case BREAK:
4668 block = current->info()->break_block();
4669 if (block == NULL) {
4670 block = current->owner()->graph()->CreateBasicBlock();
4671 current->info()->set_break_block(block);
4672 }
4673 break;
4674
4675 case CONTINUE:
4676 block = current->info()->continue_block();
4677 if (block == NULL) {
4678 block = current->owner()->graph()->CreateBasicBlock();
4679 current->info()->set_continue_block(block);
4680 }
4681 break;
4682 }
4683
4684 return block;
4685}
4686
4687
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004688void HOptimizedGraphBuilder::VisitContinueStatement(
4689 ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004690 ASSERT(!HasStackOverflow());
4691 ASSERT(current_block() != NULL);
4692 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004693 int drop_extra = 0;
4694 HBasicBlock* continue_block = break_scope()->Get(stmt->target(),
4695 CONTINUE,
4696 &drop_extra);
4697 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004698 current_block()->Goto(continue_block);
4699 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004700}
4701
4702
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004703void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004704 ASSERT(!HasStackOverflow());
4705 ASSERT(current_block() != NULL);
4706 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00004707 int drop_extra = 0;
4708 HBasicBlock* break_block = break_scope()->Get(stmt->target(),
4709 BREAK,
4710 &drop_extra);
4711 Drop(drop_extra);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004712 current_block()->Goto(break_block);
4713 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004714}
4715
4716
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004717void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004718 ASSERT(!HasStackOverflow());
4719 ASSERT(current_block() != NULL);
4720 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004721 FunctionState* state = function_state();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004722 AstContext* context = call_context();
4723 if (context == NULL) {
4724 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004725 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004726 HValue* result = environment()->Pop();
yangguo@chromium.org4cd70b42013-01-04 08:57:54 +00004727 current_block()->FinishExit(new(zone()) HReturn(
4728 result,
4729 environment()->LookupContext()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004730 } else if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
4731 // Return from an inlined construct call. In a test context the return value
4732 // will always evaluate to true, in a value context the return value needs
4733 // to be a JSObject.
ulan@chromium.org967e2702012-02-28 09:49:15 +00004734 if (context->IsTest()) {
4735 TestContext* test = TestContext::cast(context);
4736 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004737 current_block()->Goto(test->if_true(), state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00004738 } else if (context->IsEffect()) {
4739 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004740 current_block()->Goto(function_return(), state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00004741 } else {
4742 ASSERT(context->IsValue());
4743 CHECK_ALIVE(VisitForValue(stmt->expression()));
4744 HValue* return_value = Pop();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004745 HValue* receiver = environment()->arguments_environment()->Lookup(0);
ulan@chromium.org967e2702012-02-28 09:49:15 +00004746 HHasInstanceTypeAndBranch* typecheck =
4747 new(zone()) HHasInstanceTypeAndBranch(return_value,
4748 FIRST_SPEC_OBJECT_TYPE,
4749 LAST_SPEC_OBJECT_TYPE);
4750 HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
4751 HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
4752 typecheck->SetSuccessorAt(0, if_spec_object);
4753 typecheck->SetSuccessorAt(1, not_spec_object);
4754 current_block()->Finish(typecheck);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004755 if_spec_object->AddLeaveInlined(return_value, state);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004756 if (!FLAG_harmony_symbols) {
4757 not_spec_object->AddLeaveInlined(receiver, state);
4758 } else {
4759 HHasInstanceTypeAndBranch* symbolcheck =
4760 new(zone()) HHasInstanceTypeAndBranch(return_value, SYMBOL_TYPE);
4761 HBasicBlock* is_symbol = graph()->CreateBasicBlock();
4762 HBasicBlock* not_symbol = graph()->CreateBasicBlock();
4763 symbolcheck->SetSuccessorAt(0, is_symbol);
4764 symbolcheck->SetSuccessorAt(1, not_symbol);
4765 not_spec_object->Finish(symbolcheck);
4766 is_symbol->AddLeaveInlined(return_value, state);
4767 not_symbol->AddLeaveInlined(receiver, state);
4768 }
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004769 }
4770 } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
4771 // Return from an inlined setter call. The returned value is never used, the
4772 // value of an assignment is always the value of the RHS of the assignment.
4773 CHECK_ALIVE(VisitForEffect(stmt->expression()));
4774 if (context->IsTest()) {
4775 HValue* rhs = environment()->arguments_environment()->Lookup(1);
4776 context->ReturnValue(rhs);
4777 } else if (context->IsEffect()) {
4778 current_block()->Goto(function_return(), state);
4779 } else {
4780 ASSERT(context->IsValue());
4781 HValue* rhs = environment()->arguments_environment()->Lookup(1);
4782 current_block()->AddLeaveInlined(rhs, state);
ulan@chromium.org967e2702012-02-28 09:49:15 +00004783 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004784 } else {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004785 // Return from a normal inlined function. Visit the subexpression in the
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004786 // expression context of the call.
4787 if (context->IsTest()) {
4788 TestContext* test = TestContext::cast(context);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004789 VisitForControl(stmt->expression(), test->if_true(), test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004790 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004791 CHECK_ALIVE(VisitForEffect(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004792 current_block()->Goto(function_return(), state);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004793 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004794 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004795 CHECK_ALIVE(VisitForValue(stmt->expression()));
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004796 current_block()->AddLeaveInlined(Pop(), state);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004797 }
4798 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00004799 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004800}
4801
4802
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004803void HOptimizedGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004804 ASSERT(!HasStackOverflow());
4805 ASSERT(current_block() != NULL);
4806 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004807 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004808}
4809
4810
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004811void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004812 ASSERT(!HasStackOverflow());
4813 ASSERT(current_block() != NULL);
4814 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004815 // We only optimize switch statements with smi-literal smi comparisons,
4816 // with a bounded number of clauses.
4817 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004818 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004819 int clause_count = clauses->length();
4820 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004821 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004822 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004823
erikcorry0ad885c2011-11-21 13:51:57 +00004824 HValue* context = environment()->LookupContext();
4825
danno@chromium.org160a7b02011-04-18 15:51:38 +00004826 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004827 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004828 HValue* tag_value = Pop();
4829 HBasicBlock* first_test_block = current_block();
4830
erikcorry0ad885c2011-11-21 13:51:57 +00004831 SwitchType switch_type = UNKNOWN_SWITCH;
4832
4833 // 1. Extract clause type
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004834 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004835 CaseClause* clause = clauses->at(i);
4836 if (clause->is_default()) continue;
erikcorry0ad885c2011-11-21 13:51:57 +00004837
4838 if (switch_type == UNKNOWN_SWITCH) {
4839 if (clause->label()->IsSmiLiteral()) {
4840 switch_type = SMI_SWITCH;
4841 } else if (clause->label()->IsStringLiteral()) {
4842 switch_type = STRING_SWITCH;
4843 } else {
4844 return Bailout("SwitchStatement: non-literal switch label");
4845 }
4846 } else if ((switch_type == STRING_SWITCH &&
4847 !clause->label()->IsStringLiteral()) ||
4848 (switch_type == SMI_SWITCH &&
4849 !clause->label()->IsSmiLiteral())) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004850 return Bailout("SwitchStatement: mixed label types are not supported");
erikcorry0ad885c2011-11-21 13:51:57 +00004851 }
4852 }
4853
4854 HUnaryControlInstruction* string_check = NULL;
4855 HBasicBlock* not_string_block = NULL;
4856
4857 // Test switch's tag value if all clauses are string literals
4858 if (switch_type == STRING_SWITCH) {
4859 string_check = new(zone()) HIsStringAndBranch(tag_value);
4860 first_test_block = graph()->CreateBasicBlock();
4861 not_string_block = graph()->CreateBasicBlock();
4862
4863 string_check->SetSuccessorAt(0, first_test_block);
4864 string_check->SetSuccessorAt(1, not_string_block);
4865 current_block()->Finish(string_check);
4866
4867 set_current_block(first_test_block);
4868 }
4869
4870 // 2. Build all the tests, with dangling true branches
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004871 BailoutId default_id = BailoutId::None();
erikcorry0ad885c2011-11-21 13:51:57 +00004872 for (int i = 0; i < clause_count; ++i) {
4873 CaseClause* clause = clauses->at(i);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004874 if (clause->is_default()) {
4875 default_id = clause->EntryId();
4876 continue;
4877 }
erikcorry0ad885c2011-11-21 13:51:57 +00004878 if (switch_type == SMI_SWITCH) {
4879 clause->RecordTypeFeedback(oracle());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004880 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004881
erikcorry0ad885c2011-11-21 13:51:57 +00004882 // Generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004883 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004884 HValue* label_value = Pop();
erikcorry0ad885c2011-11-21 13:51:57 +00004885
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004886 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
erikcorry0ad885c2011-11-21 13:51:57 +00004887 HBasicBlock* body_block = graph()->CreateBasicBlock();
4888
4889 HControlInstruction* compare;
4890
4891 if (switch_type == SMI_SWITCH) {
4892 if (!clause->IsSmiCompare()) {
4893 // Finish with deoptimize and add uses of enviroment values to
4894 // account for invisible uses.
4895 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
4896 set_current_block(NULL);
4897 break;
4898 }
4899
4900 HCompareIDAndBranch* compare_ =
4901 new(zone()) HCompareIDAndBranch(tag_value,
4902 label_value,
4903 Token::EQ_STRICT);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004904 compare_->set_observed_input_representation(
4905 Representation::Integer32(), Representation::Integer32());
erikcorry0ad885c2011-11-21 13:51:57 +00004906 compare = compare_;
4907 } else {
4908 compare = new(zone()) HStringCompareAndBranch(context, tag_value,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004909 label_value,
4910 Token::EQ_STRICT);
erikcorry0ad885c2011-11-21 13:51:57 +00004911 }
4912
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004913 compare->SetSuccessorAt(0, body_block);
4914 compare->SetSuccessorAt(1, next_test_block);
4915 current_block()->Finish(compare);
erikcorry0ad885c2011-11-21 13:51:57 +00004916
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004917 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004918 }
4919
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004920 // Save the current block to use for the default or to join with the
4921 // exit. This block is NULL if we deoptimized.
4922 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004923
erikcorry0ad885c2011-11-21 13:51:57 +00004924 if (not_string_block != NULL) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00004925 BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004926 last_block = CreateJoin(last_block, not_string_block, join_id);
erikcorry0ad885c2011-11-21 13:51:57 +00004927 }
4928
4929 // 3. Loop over the clauses and the linked list of tests in lockstep,
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004930 // translating the clause bodies.
4931 HBasicBlock* curr_test_block = first_test_block;
4932 HBasicBlock* fall_through_block = NULL;
erikcorry0ad885c2011-11-21 13:51:57 +00004933
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004934 BreakAndContinueInfo break_info(stmt);
4935 { BreakAndContinueScope push(&break_info, this);
4936 for (int i = 0; i < clause_count; ++i) {
4937 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004938
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004939 // Identify the block where normal (non-fall-through) control flow
4940 // goes to.
4941 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00004942 if (clause->is_default()) {
4943 if (last_block != NULL) {
4944 normal_block = last_block;
4945 last_block = NULL; // Cleared to indicate we've handled it.
4946 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004947 } else if (!curr_test_block->end()->IsDeoptimize()) {
4948 normal_block = curr_test_block->end()->FirstSuccessor();
4949 curr_test_block = curr_test_block->end()->SecondSuccessor();
4950 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004951
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004952 // Identify a block to emit the body into.
4953 if (normal_block == NULL) {
4954 if (fall_through_block == NULL) {
4955 // (a) Unreachable.
4956 if (clause->is_default()) {
4957 continue; // Might still be reachable clause bodies.
4958 } else {
4959 break;
4960 }
4961 } else {
4962 // (b) Reachable only as fall through.
4963 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004964 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004965 } else if (fall_through_block == NULL) {
4966 // (c) Reachable only normally.
4967 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004968 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004969 // (d) Reachable both ways.
4970 HBasicBlock* join = CreateJoin(fall_through_block,
4971 normal_block,
4972 clause->EntryId());
4973 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004974 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004975
danno@chromium.org160a7b02011-04-18 15:51:38 +00004976 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004977 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004978 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004979 }
4980
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004981 // Create an up-to-3-way join. Use the break block if it exists since
4982 // it's already a join block.
4983 HBasicBlock* break_block = break_info.break_block();
4984 if (break_block == NULL) {
4985 set_current_block(CreateJoin(fall_through_block,
4986 last_block,
4987 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004988 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004989 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
4990 if (last_block != NULL) last_block->Goto(break_block);
4991 break_block->SetJoinId(stmt->ExitId());
4992 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004993 }
4994}
4995
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004996
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004997bool HOptimizedGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004998 return statement->OsrEntryId() == info()->osr_ast_id();
4999}
5000
5001
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005002bool HOptimizedGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005003 if (!HasOsrEntryAt(statement)) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005004
5005 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
5006 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
5007 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005008 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005009 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005010
5011 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
5012 non_osr_entry->Goto(loop_predecessor);
5013
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005014 set_current_block(osr_entry);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00005015 osr_entry->set_osr_entry();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00005016 BailoutId osr_entry_id = statement->OsrEntryId();
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005017 int first_expression_index = environment()->first_expression_index();
5018 int length = environment()->length();
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005019 ZoneList<HUnknownOSRValue*>* osr_values =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005020 new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005021
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005022 for (int i = 0; i < first_expression_index; ++i) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005023 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
5024 AddInstruction(osr_value);
5025 environment()->Bind(i, osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005026 osr_values->Add(osr_value, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005027 }
5028
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005029 if (first_expression_index != length) {
5030 environment()->Drop(length - first_expression_index);
5031 for (int i = first_expression_index; i < length; ++i) {
5032 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
5033 AddInstruction(osr_value);
5034 environment()->Push(osr_value);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005035 osr_values->Add(osr_value, zone());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005036 }
5037 }
5038
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005039 graph()->set_osr_values(osr_values);
5040
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005041 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005042 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005043 HContext* context = new(zone()) HContext;
5044 AddInstruction(context);
5045 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005046 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005047 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00005048 set_current_block(loop_predecessor);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005049 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005050}
5051
5052
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005053void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
5054 HBasicBlock* loop_entry,
5055 BreakAndContinueInfo* break_info) {
ager@chromium.org04921a82011-06-27 13:21:41 +00005056 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005057 AddSimulate(stmt->StackCheckId());
5058 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00005059 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005060 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00005061 AddInstruction(stack_check);
5062 ASSERT(loop_entry->IsLoopHeader());
5063 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005064 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00005065}
5066
5067
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005068void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005069 ASSERT(!HasStackOverflow());
5070 ASSERT(current_block() != NULL);
5071 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005072 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005073 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005074 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00005075 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005076 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005077 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005078
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005079 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005080 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005081 HBasicBlock* body_exit =
5082 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005083 HBasicBlock* loop_successor = NULL;
5084 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005085 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005086 // The block for a true condition, the actual predecessor block of the
5087 // back edge.
5088 body_exit = graph()->CreateBasicBlock();
5089 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005090 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
5091 if (body_exit->HasPredecessor()) {
5092 body_exit->SetJoinId(stmt->BackEdgeId());
5093 } else {
5094 body_exit = NULL;
5095 }
5096 if (loop_successor->HasPredecessor()) {
5097 loop_successor->SetJoinId(stmt->ExitId());
5098 } else {
5099 loop_successor = NULL;
5100 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005101 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005102 HBasicBlock* loop_exit = CreateLoop(stmt,
5103 loop_entry,
5104 body_exit,
5105 loop_successor,
5106 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005107 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005108}
5109
5110
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005111void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005112 ASSERT(!HasStackOverflow());
5113 ASSERT(current_block() != NULL);
5114 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005115 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005116 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005117 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00005118 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005119 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005120 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
5121
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005122
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005123 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005124 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005125 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005126 HBasicBlock* body_entry = graph()->CreateBasicBlock();
5127 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005128 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
5129 if (body_entry->HasPredecessor()) {
5130 body_entry->SetJoinId(stmt->BodyId());
5131 set_current_block(body_entry);
5132 }
5133 if (loop_successor->HasPredecessor()) {
5134 loop_successor->SetJoinId(stmt->ExitId());
5135 } else {
5136 loop_successor = NULL;
5137 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005138 }
5139
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005140 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005141 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005142 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005143 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005144 HBasicBlock* body_exit =
5145 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005146 HBasicBlock* loop_exit = CreateLoop(stmt,
5147 loop_entry,
5148 body_exit,
5149 loop_successor,
5150 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005151 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005152}
5153
5154
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005155void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005156 ASSERT(!HasStackOverflow());
5157 ASSERT(current_block() != NULL);
5158 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005159 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005160 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005161 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005162 ASSERT(current_block() != NULL);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005163 bool osr_entry = PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005164 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00005165 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005166 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005167 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005168
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005169 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005170 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005171 HBasicBlock* body_entry = graph()->CreateBasicBlock();
5172 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005173 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
5174 if (body_entry->HasPredecessor()) {
5175 body_entry->SetJoinId(stmt->BodyId());
5176 set_current_block(body_entry);
5177 }
5178 if (loop_successor->HasPredecessor()) {
5179 loop_successor->SetJoinId(stmt->ExitId());
5180 } else {
5181 loop_successor = NULL;
5182 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005183 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005184
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005185 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005186 if (current_block() != NULL) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005187 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005188 }
5189 HBasicBlock* body_exit =
5190 JoinContinue(stmt, current_block(), break_info.continue_block());
5191
5192 if (stmt->next() != NULL && body_exit != NULL) {
5193 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005194 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005195 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005196 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005197
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005198 HBasicBlock* loop_exit = CreateLoop(stmt,
5199 loop_entry,
5200 body_exit,
5201 loop_successor,
5202 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00005203 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005204}
5205
5206
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005207void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005208 ASSERT(!HasStackOverflow());
5209 ASSERT(current_block() != NULL);
5210 ASSERT(current_block()->HasPredecessor());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005211
yangguo@chromium.orga7d3df92012-02-27 11:46:55 +00005212 if (!FLAG_optimize_for_in) {
5213 return Bailout("ForInStatement optimization is disabled");
5214 }
5215
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00005216 if (!oracle()->IsForInFastCase(stmt)) {
5217 return Bailout("ForInStatement is not fast case");
5218 }
5219
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005220 if (!stmt->each()->IsVariableProxy() ||
5221 !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
5222 return Bailout("ForInStatement with non-local each variable");
5223 }
5224
5225 Variable* each_var = stmt->each()->AsVariableProxy()->var();
5226
5227 CHECK_ALIVE(VisitForValue(stmt->enumerable()));
5228 HValue* enumerable = Top(); // Leave enumerable at the top.
5229
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005230 HInstruction* map = AddInstruction(new(zone()) HForInPrepareMap(
ulan@chromium.org967e2702012-02-28 09:49:15 +00005231 environment()->LookupContext(), enumerable));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005232 AddSimulate(stmt->PrepareId());
5233
5234 HInstruction* array = AddInstruction(
5235 new(zone()) HForInCacheArray(
5236 enumerable,
5237 map,
5238 DescriptorArray::kEnumCacheBridgeCacheIndex));
5239
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00005240 HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005241
5242 HInstruction* start_index = AddInstruction(new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005243 Handle<Object>(Smi::FromInt(0), isolate()), Representation::Integer32()));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005244
5245 Push(map);
5246 Push(array);
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00005247 Push(enum_length);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005248 Push(start_index);
5249
5250 HInstruction* index_cache = AddInstruction(
5251 new(zone()) HForInCacheArray(
5252 enumerable,
5253 map,
5254 DescriptorArray::kEnumCacheBridgeIndicesCacheIndex));
5255 HForInCacheArray::cast(array)->set_index_cache(
5256 HForInCacheArray::cast(index_cache));
5257
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005258 bool osr_entry = PreProcessOsrEntry(stmt);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005259 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
5260 current_block()->Goto(loop_entry);
5261 set_current_block(loop_entry);
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00005262 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005263
5264 HValue* index = environment()->ExpressionStackAt(0);
5265 HValue* limit = environment()->ExpressionStackAt(1);
5266
5267 // Check that we still have more keys.
5268 HCompareIDAndBranch* compare_index =
5269 new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005270 compare_index->set_observed_input_representation(
5271 Representation::Integer32(), Representation::Integer32());
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005272
5273 HBasicBlock* loop_body = graph()->CreateBasicBlock();
5274 HBasicBlock* loop_successor = graph()->CreateBasicBlock();
5275
5276 compare_index->SetSuccessorAt(0, loop_body);
5277 compare_index->SetSuccessorAt(1, loop_successor);
5278 current_block()->Finish(compare_index);
5279
5280 set_current_block(loop_successor);
5281 Drop(5);
5282
5283 set_current_block(loop_body);
5284
5285 HValue* key = AddInstruction(
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005286 new(zone()) HLoadKeyed(
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005287 environment()->ExpressionStackAt(2), // Enum cache.
yangguo@chromium.org304cc332012-07-24 07:59:48 +00005288 environment()->ExpressionStackAt(0), // Iteration index.
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005289 environment()->ExpressionStackAt(0),
5290 FAST_ELEMENTS));
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005291
5292 // Check if the expected map still matches that of the enumerable.
5293 // If not just deoptimize.
5294 AddInstruction(new(zone()) HCheckMapValue(
5295 environment()->ExpressionStackAt(4),
5296 environment()->ExpressionStackAt(3)));
5297
5298 Bind(each_var, key);
5299
5300 BreakAndContinueInfo break_info(stmt, 5);
5301 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
5302
5303 HBasicBlock* body_exit =
5304 JoinContinue(stmt, current_block(), break_info.continue_block());
5305
5306 if (body_exit != NULL) {
5307 set_current_block(body_exit);
5308
5309 HValue* current_index = Pop();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005310 HInstruction* new_index = HAdd::New(zone(),
5311 environment()->LookupContext(),
5312 current_index,
5313 graph()->GetConstant1());
ulan@chromium.org967e2702012-02-28 09:49:15 +00005314 new_index->AssumeRepresentation(Representation::Integer32());
5315 PushAndAdd(new_index);
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00005316 body_exit = current_block();
5317 }
5318
5319 HBasicBlock* loop_exit = CreateLoop(stmt,
5320 loop_entry,
5321 body_exit,
5322 loop_successor,
5323 break_info.break_block());
5324
5325 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005326}
5327
5328
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005329void HOptimizedGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005330 ASSERT(!HasStackOverflow());
5331 ASSERT(current_block() != NULL);
5332 ASSERT(current_block()->HasPredecessor());
5333 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005334}
5335
5336
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005337void HOptimizedGraphBuilder::VisitTryFinallyStatement(
5338 TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005339 ASSERT(!HasStackOverflow());
5340 ASSERT(current_block() != NULL);
5341 ASSERT(current_block()->HasPredecessor());
5342 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005343}
5344
5345
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005346void HOptimizedGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005347 ASSERT(!HasStackOverflow());
5348 ASSERT(current_block() != NULL);
5349 ASSERT(current_block()->HasPredecessor());
5350 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005351}
5352
5353
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005354static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
5355 Code* unoptimized_code, FunctionLiteral* expr) {
5356 int start_position = expr->start_position();
5357 RelocIterator it(unoptimized_code);
5358 for (;!it.done(); it.next()) {
5359 RelocInfo* rinfo = it.rinfo();
5360 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
5361 Object* obj = rinfo->target_object();
5362 if (obj->IsSharedFunctionInfo()) {
5363 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
5364 if (shared->start_position() == start_position) {
5365 return Handle<SharedFunctionInfo>(shared);
5366 }
5367 }
5368 }
5369
5370 return Handle<SharedFunctionInfo>();
5371}
5372
5373
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005374void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005375 ASSERT(!HasStackOverflow());
5376 ASSERT(current_block() != NULL);
5377 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005378 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005379 SearchSharedFunctionInfo(info()->shared_info()->code(),
5380 expr);
5381 if (shared_info.is_null()) {
5382 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
5383 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00005384 // We also have a stack overflow if the recursive compilation did.
5385 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005386 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005387 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005388 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005389 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005390}
5391
5392
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005393void HOptimizedGraphBuilder::VisitSharedFunctionInfoLiteral(
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005394 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005395 ASSERT(!HasStackOverflow());
5396 ASSERT(current_block() != NULL);
5397 ASSERT(current_block()->HasPredecessor());
5398 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005399}
5400
5401
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005402void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005403 ASSERT(!HasStackOverflow());
5404 ASSERT(current_block() != NULL);
5405 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005406 HBasicBlock* cond_true = graph()->CreateBasicBlock();
5407 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005408 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005409
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005410 // Visit the true and false subexpressions in the same AST context as the
5411 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005412 if (cond_true->HasPredecessor()) {
5413 cond_true->SetJoinId(expr->ThenId());
5414 set_current_block(cond_true);
5415 CHECK_BAILOUT(Visit(expr->then_expression()));
5416 cond_true = current_block();
5417 } else {
5418 cond_true = NULL;
5419 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005420
danno@chromium.org160a7b02011-04-18 15:51:38 +00005421 if (cond_false->HasPredecessor()) {
5422 cond_false->SetJoinId(expr->ElseId());
5423 set_current_block(cond_false);
5424 CHECK_BAILOUT(Visit(expr->else_expression()));
5425 cond_false = current_block();
5426 } else {
5427 cond_false = NULL;
5428 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005429
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005430 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005431 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005432 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005433 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005434 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005435 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005436 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005437}
5438
5439
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005440HOptimizedGraphBuilder::GlobalPropertyAccess
5441 HOptimizedGraphBuilder::LookupGlobalProperty(
5442 Variable* var, LookupResult* lookup, bool is_store) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005443 if (var->is_this() || !info()->has_global_object()) {
5444 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005445 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005446 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005447 global->Lookup(*var->name(), lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005448 if (!lookup->IsNormal() ||
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005449 (is_store && lookup->IsReadOnly()) ||
5450 lookup->holder() != *global) {
5451 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005452 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005453
5454 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005455}
5456
5457
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005458HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005459 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005460 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005461 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005462 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005463 HInstruction* context_instruction = new(zone()) HOuterContext(context);
5464 AddInstruction(context_instruction);
5465 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005466 }
5467 return context;
5468}
5469
5470
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005471void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005472 ASSERT(!HasStackOverflow());
5473 ASSERT(current_block() != NULL);
5474 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005475 Variable* variable = expr->var();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005476 switch (variable->location()) {
5477 case Variable::UNALLOCATED: {
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00005478 if (IsLexicalVariableMode(variable->mode())) {
5479 // TODO(rossberg): should this be an ASSERT?
5480 return Bailout("reference to global lexical variable");
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005481 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005482 // Handle known global constants like 'undefined' specially to avoid a
5483 // load from a global cell for them.
5484 Handle<Object> constant_value =
5485 isolate()->factory()->GlobalConstantFor(variable->name());
5486 if (!constant_value.is_null()) {
5487 HConstant* instr =
5488 new(zone()) HConstant(constant_value, Representation::Tagged());
5489 return ast_context()->ReturnInstruction(instr, expr->id());
5490 }
5491
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005492 LookupResult lookup(isolate());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005493 GlobalPropertyAccess type =
5494 LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005495
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005496 if (type == kUseCell &&
5497 info()->global_object()->IsAccessCheckNeeded()) {
5498 type = kUseGeneric;
5499 }
5500
5501 if (type == kUseCell) {
5502 Handle<GlobalObject> global(info()->global_object());
5503 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005504 HLoadGlobalCell* instr =
5505 new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005506 return ast_context()->ReturnInstruction(instr, expr->id());
5507 } else {
5508 HValue* context = environment()->LookupContext();
5509 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
5510 AddInstruction(global_object);
5511 HLoadGlobalGeneric* instr =
5512 new(zone()) HLoadGlobalGeneric(context,
5513 global_object,
5514 variable->name(),
5515 ast_context()->is_for_typeof());
5516 instr->set_position(expr->position());
5517 return ast_context()->ReturnInstruction(instr, expr->id());
5518 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005519 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005520
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005521 case Variable::PARAMETER:
5522 case Variable::LOCAL: {
5523 HValue* value = environment()->Lookup(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005524 if (value == graph()->GetConstantHole()) {
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00005525 ASSERT(IsDeclaredVariableMode(variable->mode()) &&
5526 variable->mode() != VAR);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005527 return Bailout("reference to uninitialized variable");
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005528 }
5529 return ast_context()->ReturnValue(value);
5530 }
5531
5532 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005533 HValue* context = BuildContextChainWalk(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005534 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005535 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005536 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005537
5538 case Variable::LOOKUP:
5539 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005540 }
5541}
5542
5543
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005544void HOptimizedGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005545 ASSERT(!HasStackOverflow());
5546 ASSERT(current_block() != NULL);
5547 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005548 HConstant* instr =
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005549 new(zone()) HConstant(expr->handle(), Representation::None());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005550 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005551}
5552
5553
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005554void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005555 ASSERT(!HasStackOverflow());
5556 ASSERT(current_block() != NULL);
5557 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00005558 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
5559 Handle<FixedArray> literals(closure->literals());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005560 HValue* context = environment()->LookupContext();
5561
5562 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
yangguo@chromium.org9c741c82012-06-28 15:04:22 +00005563 literals,
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005564 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005565 expr->flags(),
5566 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005567 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005568}
5569
5570
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005571static void LookupInPrototypes(Handle<Map> map,
5572 Handle<String> name,
5573 LookupResult* lookup) {
5574 while (map->prototype()->IsJSObject()) {
5575 Handle<JSObject> holder(JSObject::cast(map->prototype()));
5576 if (!holder->HasFastProperties()) break;
5577 map = Handle<Map>(holder->map());
5578 map->LookupDescriptor(*holder, *name, lookup);
5579 if (lookup->IsFound()) return;
5580 }
5581 lookup->NotFound();
5582}
5583
5584
5585// Tries to find a JavaScript accessor of the given name in the prototype chain
5586// starting at the given map. Return true iff there is one, including the
5587// corresponding AccessorPair plus its holder (which could be null when the
5588// accessor is found directly in the given map).
5589static bool LookupAccessorPair(Handle<Map> map,
5590 Handle<String> name,
5591 Handle<AccessorPair>* accessors,
5592 Handle<JSObject>* holder) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005593 Isolate* isolate = map->GetIsolate();
5594 LookupResult lookup(isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005595
5596 // Check for a JavaScript accessor directly in the map.
5597 map->LookupDescriptor(NULL, *name, &lookup);
5598 if (lookup.IsPropertyCallbacks()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005599 Handle<Object> callback(lookup.GetValueFromMap(*map), isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005600 if (!callback->IsAccessorPair()) return false;
5601 *accessors = Handle<AccessorPair>::cast(callback);
5602 *holder = Handle<JSObject>();
5603 return true;
5604 }
5605
5606 // Everything else, e.g. a field, can't be an accessor call.
5607 if (lookup.IsFound()) return false;
5608
5609 // Check for a JavaScript accessor somewhere in the proto chain.
5610 LookupInPrototypes(map, name, &lookup);
5611 if (lookup.IsPropertyCallbacks()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005612 Handle<Object> callback(lookup.GetValue(), isolate);
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005613 if (!callback->IsAccessorPair()) return false;
5614 *accessors = Handle<AccessorPair>::cast(callback);
5615 *holder = Handle<JSObject>(lookup.holder());
5616 return true;
5617 }
5618
5619 // We haven't found a JavaScript accessor anywhere.
5620 return false;
5621}
5622
5623
5624static bool LookupGetter(Handle<Map> map,
5625 Handle<String> name,
5626 Handle<JSFunction>* getter,
5627 Handle<JSObject>* holder) {
5628 Handle<AccessorPair> accessors;
5629 if (LookupAccessorPair(map, name, &accessors, holder) &&
5630 accessors->getter()->IsJSFunction()) {
5631 *getter = Handle<JSFunction>(JSFunction::cast(accessors->getter()));
5632 return true;
5633 }
5634 return false;
5635}
5636
5637
5638static bool LookupSetter(Handle<Map> map,
5639 Handle<String> name,
5640 Handle<JSFunction>* setter,
5641 Handle<JSObject>* holder) {
5642 Handle<AccessorPair> accessors;
5643 if (LookupAccessorPair(map, name, &accessors, holder) &&
5644 accessors->setter()->IsJSFunction()) {
5645 *setter = Handle<JSFunction>(JSFunction::cast(accessors->setter()));
5646 return true;
5647 }
5648 return false;
5649}
5650
5651
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005652// Determines whether the given array or object literal boilerplate satisfies
5653// all limits to be considered for fast deep-copying and computes the total
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005654// size of all objects that are part of the graph.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005655static bool IsFastLiteral(Handle<JSObject> boilerplate,
5656 int max_depth,
5657 int* max_properties,
5658 int* total_size) {
5659 ASSERT(max_depth >= 0 && *max_properties >= 0);
5660 if (max_depth == 0) return false;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005661
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005662 Isolate* isolate = boilerplate->GetIsolate();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005663 Handle<FixedArrayBase> elements(boilerplate->elements());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005664 if (elements->length() > 0 &&
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005665 elements->map() != isolate->heap()->fixed_cow_array_map()) {
danno@chromium.org88aa0582012-03-23 15:11:57 +00005666 if (boilerplate->HasFastDoubleElements()) {
5667 *total_size += FixedDoubleArray::SizeFor(elements->length());
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005668 } else if (boilerplate->HasFastObjectElements()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00005669 Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
danno@chromium.org88aa0582012-03-23 15:11:57 +00005670 int length = elements->length();
5671 for (int i = 0; i < length; i++) {
5672 if ((*max_properties)-- == 0) return false;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005673 Handle<Object> value(fast_elements->get(i), isolate);
danno@chromium.org88aa0582012-03-23 15:11:57 +00005674 if (value->IsJSObject()) {
5675 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
5676 if (!IsFastLiteral(value_object,
5677 max_depth - 1,
5678 max_properties,
5679 total_size)) {
5680 return false;
5681 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005682 }
5683 }
danno@chromium.org88aa0582012-03-23 15:11:57 +00005684 *total_size += FixedArray::SizeFor(length);
5685 } else {
5686 return false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005687 }
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005688 }
5689
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005690 Handle<FixedArray> properties(boilerplate->properties());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005691 if (properties->length() > 0) {
5692 return false;
5693 } else {
5694 int nof = boilerplate->map()->inobject_properties();
5695 for (int i = 0; i < nof; i++) {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005696 if ((*max_properties)-- == 0) return false;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005697 Handle<Object> value(boilerplate->InObjectPropertyAt(i), isolate);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005698 if (value->IsJSObject()) {
5699 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005700 if (!IsFastLiteral(value_object,
5701 max_depth - 1,
5702 max_properties,
5703 total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005704 return false;
5705 }
5706 }
5707 }
5708 }
5709
5710 *total_size += boilerplate->map()->instance_size();
5711 return true;
5712}
5713
5714
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005715void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005716 ASSERT(!HasStackOverflow());
5717 ASSERT(current_block() != NULL);
5718 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005719 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005720 HValue* context = environment()->LookupContext();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005721 HInstruction* literal;
5722
5723 // Check whether to use fast or slow deep-copying for boilerplate.
5724 int total_size = 0;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005725 int max_properties = HFastLiteral::kMaxLiteralProperties;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005726 Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()),
5727 isolate());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005728 if (boilerplate->IsJSObject() &&
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005729 IsFastLiteral(Handle<JSObject>::cast(boilerplate),
5730 HFastLiteral::kMaxLiteralDepth,
5731 &max_properties,
5732 &total_size)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005733 Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005734 literal = new(zone()) HFastLiteral(context,
5735 boilerplate_object,
5736 total_size,
5737 expr->literal_index(),
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005738 expr->depth(),
5739 DONT_TRACK_ALLOCATION_SITE);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005740 } else {
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005741 literal = new(zone()) HObjectLiteral(context,
5742 expr->constant_properties(),
5743 expr->fast_elements(),
5744 expr->literal_index(),
5745 expr->depth(),
5746 expr->has_function());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00005747 }
5748
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005749 // The object is expected in the bailout environment during computation
5750 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005751 PushAndAdd(literal);
5752
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005753 expr->CalculateEmitStore(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005754
5755 for (int i = 0; i < expr->properties()->length(); i++) {
5756 ObjectLiteral::Property* property = expr->properties()->at(i);
5757 if (property->IsCompileTimeValue()) continue;
5758
5759 Literal* key = property->key();
5760 Expression* value = property->value();
5761
5762 switch (property->kind()) {
5763 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
5764 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
5765 // Fall through.
5766 case ObjectLiteral::Property::COMPUTED:
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005767 if (key->handle()->IsInternalizedString()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005768 if (property->emit_store()) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00005769 property->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005770 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005771 HValue* value = Pop();
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00005772 Handle<Map> map = property->GetReceiverType();
5773 Handle<String> name = property->key()->AsPropertyName();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005774 HInstruction* store;
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00005775 if (map.is_null()) {
5776 // If we don't know the monomorphic type, do a generic store.
5777 CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value));
5778 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00005779#if DEBUG
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005780 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00005781 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00005782 ASSERT(!LookupSetter(map, name, &setter, &holder));
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00005783#endif
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00005784 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal,
5785 name,
5786 value,
5787 map));
5788 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005789 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005790 if (store->HasObservableSideEffects()) {
5791 AddSimulate(key->id(), REMOVABLE_SIMULATE);
5792 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005793 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005794 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005795 }
5796 break;
5797 }
5798 // Fall through.
5799 case ObjectLiteral::Property::PROTOTYPE:
5800 case ObjectLiteral::Property::SETTER:
5801 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00005802 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005803 default: UNREACHABLE();
5804 }
5805 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005806
5807 if (expr->has_function()) {
5808 // Return the result of the transformation to fast properties
5809 // instead of the original since this operation changes the map
5810 // of the object. This makes sure that the original object won't
5811 // be used by other optimized code before it is transformed
5812 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005813 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005814 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005815 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005816 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005817 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005818 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005819}
5820
5821
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005822void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005823 ASSERT(!HasStackOverflow());
5824 ASSERT(current_block() != NULL);
5825 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005826 ZoneList<Expression*>* subexprs = expr->values();
5827 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005828 HValue* context = environment()->LookupContext();
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005829 HInstruction* literal;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005830
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005831 Handle<FixedArray> literals(environment()->closure()->literals());
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005832 Handle<Object> raw_boilerplate(literals->get(expr->literal_index()),
5833 isolate());
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005834
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005835 if (raw_boilerplate->IsUndefined()) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005836 raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
5837 isolate(), literals, expr->constant_elements());
5838 if (raw_boilerplate.is_null()) {
5839 return Bailout("array boilerplate creation failed");
5840 }
5841 literals->set(expr->literal_index(), *raw_boilerplate);
5842 if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
5843 isolate()->heap()->fixed_cow_array_map()) {
5844 isolate()->counters()->cow_arrays_created_runtime()->Increment();
5845 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005846 }
5847
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005848 Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
5849 ElementsKind boilerplate_elements_kind =
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005850 Handle<JSObject>::cast(boilerplate)->GetElementsKind();
5851
5852 // TODO(mvstanton): This heuristic is only a temporary solution. In the
5853 // end, we want to quit creating allocation site info after a certain number
5854 // of GCs for a call site.
5855 AllocationSiteMode mode = AllocationSiteInfo::GetMode(
5856 boilerplate_elements_kind);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005857
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005858 // Check whether to use fast or slow deep-copying for boilerplate.
5859 int total_size = 0;
5860 int max_properties = HFastLiteral::kMaxLiteralProperties;
5861 if (IsFastLiteral(boilerplate,
5862 HFastLiteral::kMaxLiteralDepth,
5863 &max_properties,
5864 &total_size)) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005865 if (mode == TRACK_ALLOCATION_SITE) {
5866 total_size += AllocationSiteInfo::kSize;
5867 }
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005868 literal = new(zone()) HFastLiteral(context,
5869 boilerplate,
5870 total_size,
5871 expr->literal_index(),
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005872 expr->depth(),
5873 mode);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005874 } else {
5875 literal = new(zone()) HArrayLiteral(context,
5876 boilerplate,
5877 length,
5878 expr->literal_index(),
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005879 expr->depth(),
5880 mode);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +00005881 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005882
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005883 // The array is expected in the bailout environment during computation
5884 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005885 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005886
5887 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005888
5889 for (int i = 0; i < length; i++) {
5890 Expression* subexpr = subexprs->at(i);
5891 // If the subexpression is a literal or a simple materialized literal it
5892 // is already set in the cloned array.
5893 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
5894
danno@chromium.org160a7b02011-04-18 15:51:38 +00005895 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005896 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005897 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005898
verwaest@chromium.org68c05782012-09-04 09:58:32 +00005899 // Pass in literal as dummy depedency, since the receiver always has
5900 // elements.
5901 elements = new(zone()) HLoadElements(literal, literal);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005902 AddInstruction(elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005903
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005904 HValue* key = AddInstruction(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00005905 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i), isolate()),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005906 Representation::Integer32()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005907
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005908 switch (boilerplate_elements_kind) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005909 case FAST_SMI_ELEMENTS:
5910 case FAST_HOLEY_SMI_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00005911 // Smi-only arrays need a smi check.
5912 AddInstruction(new(zone()) HCheckSmi(value));
5913 // Fall through.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005914 case FAST_ELEMENTS:
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005915 case FAST_HOLEY_ELEMENTS:
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005916 case FAST_DOUBLE_ELEMENTS:
5917 case FAST_HOLEY_DOUBLE_ELEMENTS:
5918 AddInstruction(new(zone()) HStoreKeyed(
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005919 elements,
5920 key,
5921 value,
5922 boilerplate_elements_kind));
5923 break;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005924 default:
5925 UNREACHABLE();
5926 break;
5927 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005928
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005929 AddSimulate(expr->GetIdForElement(i));
5930 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005931 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005932}
5933
5934
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005935// Sets the lookup result and returns true if the load/store can be inlined.
5936static bool ComputeLoadStoreField(Handle<Map> type,
5937 Handle<String> name,
5938 LookupResult* lookup,
5939 bool is_store) {
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005940 if (type->has_named_interceptor()) {
5941 lookup->InterceptorResult(NULL);
5942 return false;
5943 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005944 // If we directly find a field, the access can be inlined.
5945 type->LookupDescriptor(NULL, *name, lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005946 if (lookup->IsField()) return true;
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00005947
5948 // For a load, we are out of luck if there is no such field.
5949 if (!is_store) return false;
5950
5951 // 2nd chance: A store into a non-existent field can still be inlined if we
5952 // have a matching transition and some room left in the object.
5953 type->LookupTransition(NULL, *name, lookup);
5954 return lookup->IsTransitionToField(*type) &&
5955 (type->unused_property_fields() > 0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005956}
5957
5958
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005959static int ComputeLoadStoreFieldIndex(Handle<Map> type,
5960 Handle<String> name,
5961 LookupResult* lookup) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00005962 ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type));
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00005963 if (lookup->IsField()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005964 return lookup->GetLocalFieldIndexFromMap(*type);
5965 } else {
5966 Map* transition = lookup->GetTransitionMapFromMap(*type);
5967 return transition->PropertyIndexFor(*name) - type->inobject_properties();
5968 }
5969}
5970
5971
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005972void HOptimizedGraphBuilder::AddCheckMapsWithTransitions(HValue* object,
5973 Handle<Map> map) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00005974 AddInstruction(new(zone()) HCheckNonSmi(object));
5975 AddInstruction(HCheckMaps::NewWithTransitions(object, map, zone()));
5976}
5977
5978
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00005979HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
5980 HValue* object,
5981 Handle<String> name,
5982 HValue* value,
5983 Handle<Map> map,
5984 LookupResult* lookup) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005985 ASSERT(lookup->IsFound());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005986 // If the property does not exist yet, we have to check that it wasn't made
5987 // readonly or turned into a setter by some meanwhile modifications on the
5988 // prototype chain.
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00005989 if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) {
5990 Object* proto = map->prototype();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00005991 // First check that the prototype chain isn't affected already.
5992 LookupResult proto_result(isolate());
5993 proto->Lookup(*name, &proto_result);
5994 if (proto_result.IsProperty()) {
5995 // If the inherited property could induce readonly-ness, bail out.
5996 if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
5997 Bailout("improper object on prototype chain for store");
5998 return NULL;
5999 }
6000 // We only need to check up to the preexisting property.
6001 proto = proto_result.holder();
6002 } else {
6003 // Otherwise, find the top prototype.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006004 while (proto->GetPrototype(isolate())->IsJSObject()) {
6005 proto = proto->GetPrototype(isolate());
6006 }
6007 ASSERT(proto->GetPrototype(isolate())->IsNull());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006008 }
6009 ASSERT(proto->IsJSObject());
6010 AddInstruction(new(zone()) HCheckPrototypeMaps(
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006011 Handle<JSObject>(JSObject::cast(map->prototype())),
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00006012 Handle<JSObject>(JSObject::cast(proto)),
6013 zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006014 }
6015
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006016 int index = ComputeLoadStoreFieldIndex(map, name, lookup);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006017 bool is_in_object = index < 0;
6018 int offset = index * kPointerSize;
6019 if (index < 0) {
6020 // Negative property indices are in-object properties, indexed
6021 // from the end of the fixed part of the object.
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006022 offset += map->instance_size();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006023 } else {
6024 offset += FixedArray::kHeaderSize;
6025 }
6026 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006027 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006028 if (lookup->IsTransitionToField(*map)) {
6029 Handle<Map> transition(lookup->GetTransitionMapFromMap(*map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006030 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00006031 // TODO(fschneider): Record the new map type of the object in the IR to
6032 // enable elimination of redundant checks after the transition store.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00006033 instr->SetGVNFlag(kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006034 }
6035 return instr;
6036}
6037
6038
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006039HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
6040 HValue* object,
6041 Handle<String> name,
6042 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006043 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006044 return new(zone()) HStoreNamedGeneric(
6045 context,
6046 object,
6047 name,
6048 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006049 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006050}
6051
6052
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006053HInstruction* HOptimizedGraphBuilder::BuildCallSetter(
6054 HValue* object,
6055 HValue* value,
6056 Handle<Map> map,
6057 Handle<JSFunction> setter,
6058 Handle<JSObject> holder) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006059 AddCheckConstantFunction(holder, object, map);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006060 AddInstruction(new(zone()) HPushArgument(object));
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006061 AddInstruction(new(zone()) HPushArgument(value));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006062 return new(zone()) HCallConstantFunction(setter, 2);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006063}
6064
6065
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006066HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
6067 HValue* object,
6068 Handle<String> name,
6069 HValue* value,
6070 Handle<Map> map) {
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006071 // Handle a store to a known field.
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00006072 LookupResult lookup(isolate());
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006073 if (ComputeLoadStoreField(map, name, &lookup, true)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006074 AddCheckMapsWithTransitions(object, map);
6075 return BuildStoreNamedField(object, name, value, map, &lookup);
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006076 }
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00006077
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006078 // No luck, do a generic store.
6079 return BuildStoreNamedGeneric(object, name, value);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00006080}
6081
6082
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006083void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
6084 Property* expr,
6085 HValue* object,
6086 SmallMapList* types,
6087 Handle<String> name) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006088 int count = 0;
6089 int previous_field_offset = 0;
6090 bool previous_field_is_in_object = false;
6091 bool is_monomorphic_field = true;
6092 Handle<Map> map;
6093 LookupResult lookup(isolate());
6094 for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
6095 map = types->at(i);
6096 if (ComputeLoadStoreField(map, name, &lookup, false)) {
6097 int index = ComputeLoadStoreFieldIndex(map, name, &lookup);
6098 bool is_in_object = index < 0;
6099 int offset = index * kPointerSize;
6100 if (index < 0) {
6101 // Negative property indices are in-object properties, indexed
6102 // from the end of the fixed part of the object.
6103 offset += map->instance_size();
6104 } else {
6105 offset += FixedArray::kHeaderSize;
6106 }
6107 if (count == 0) {
6108 previous_field_offset = offset;
6109 previous_field_is_in_object = is_in_object;
6110 } else if (is_monomorphic_field) {
6111 is_monomorphic_field = (offset == previous_field_offset) &&
6112 (is_in_object == previous_field_is_in_object);
6113 }
6114 ++count;
6115 }
6116 }
6117
6118 // Use monomorphic load if property lookup results in the same field index
6119 // for all maps. Requires special map check on the set of all handled maps.
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006120 AddInstruction(new(zone()) HCheckNonSmi(object));
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006121 HInstruction* instr;
6122 if (count == types->length() && is_monomorphic_field) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006123 AddInstruction(new(zone()) HCheckMaps(object, types, zone()));
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006124 instr = BuildLoadNamedField(object, map, &lookup);
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006125 } else {
6126 HValue* context = environment()->LookupContext();
6127 instr = new(zone()) HLoadNamedFieldPolymorphic(context,
6128 object,
6129 types,
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006130 name,
6131 zone());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006132 }
6133
6134 instr->set_position(expr->position());
6135 return ast_context()->ReturnInstruction(instr, expr->id());
6136}
6137
6138
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006139void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
6140 Assignment* expr,
6141 HValue* object,
6142 HValue* value,
6143 SmallMapList* types,
6144 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006145 // TODO(ager): We should recognize when the prototype chains for different
6146 // maps are identical. In that case we can avoid repeatedly generating the
6147 // same prototype map checks.
6148 int count = 0;
6149 HBasicBlock* join = NULL;
6150 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006151 Handle<Map> map = types->at(i);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006152 LookupResult lookup(isolate());
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006153 if (ComputeLoadStoreField(map, name, &lookup, true)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006154 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006155 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006156 join = graph()->CreateBasicBlock();
6157 }
6158 ++count;
6159 HBasicBlock* if_true = graph()->CreateBasicBlock();
6160 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006161 HCompareMap* compare =
6162 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006163 current_block()->Finish(compare);
6164
6165 set_current_block(if_true);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006166 HInstruction* instr;
6167 CHECK_ALIVE(instr =
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006168 BuildStoreNamedField(object, name, value, map, &lookup));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006169 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006170 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006171 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006172 if (!ast_context()->IsEffect()) Push(value);
6173 current_block()->Goto(join);
6174
6175 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006176 }
6177 }
6178
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006179 // Finish up. Unconditionally deoptimize if we've handled all the maps we
6180 // know about and do not want to handle ones we've never seen. Otherwise
6181 // use a generic IC.
6182 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006183 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006184 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006185 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006186 instr->set_position(expr->position());
6187 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006188
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006189 if (join != NULL) {
6190 if (!ast_context()->IsEffect()) Push(value);
6191 current_block()->Goto(join);
6192 } else {
6193 // The HSimulate for the store should not see the stored value in
6194 // effect contexts (it is not materialized at expr->id() in the
6195 // unoptimized code).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006196 if (instr->HasObservableSideEffects()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006197 if (ast_context()->IsEffect()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006198 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006199 } else {
6200 Push(value);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006201 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006202 Drop(1);
6203 }
6204 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006205 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00006206 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00006207 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006208
6209 ASSERT(join != NULL);
6210 join->SetJoinId(expr->id());
6211 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006212 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006213}
6214
6215
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006216void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006217 Property* prop = expr->target()->AsProperty();
6218 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006219 expr->RecordTypeFeedback(oracle(), zone());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006220 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006221
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006222 if (prop->key()->IsPropertyName()) {
6223 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006224 CHECK_ALIVE(VisitForValue(expr->value()));
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006225 HValue* value = environment()->ExpressionStackAt(0);
6226 HValue* object = environment()->ExpressionStackAt(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006227
6228 Literal* key = prop->key()->AsLiteral();
6229 Handle<String> name = Handle<String>::cast(key->handle());
6230 ASSERT(!name.is_null());
6231
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006232 HInstruction* instr = NULL;
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006233 SmallMapList* types = expr->GetReceiverTypes();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00006234 bool monomorphic = expr->IsMonomorphic();
6235 Handle<Map> map;
6236 if (monomorphic) {
6237 map = types->first();
6238 if (map->is_dictionary_map()) monomorphic = false;
6239 }
6240 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006241 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006242 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006243 if (LookupSetter(map, name, &setter, &holder)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006244 AddCheckConstantFunction(holder, object, map);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00006245 if (FLAG_inline_accessors && TryInlineSetter(setter, expr, value)) {
6246 return;
6247 }
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006248 Drop(2);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00006249 AddInstruction(new(zone()) HPushArgument(object));
6250 AddInstruction(new(zone()) HPushArgument(value));
6251 instr = new(zone()) HCallConstantFunction(setter, 2);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006252 } else {
6253 Drop(2);
6254 CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object,
6255 name,
6256 value,
6257 map));
6258 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006259
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006260 } else if (types != NULL && types->length() > 1) {
6261 Drop(2);
6262 return HandlePolymorphicStoreNamedField(expr, object, value, types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006263 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006264 Drop(2);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006265 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006266 }
6267
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006268 Push(value);
6269 instr->set_position(expr->position());
6270 AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006271 if (instr->HasObservableSideEffects()) {
6272 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
6273 }
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006274 return ast_context()->ReturnValue(Pop());
6275
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006276 } else {
6277 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006278 CHECK_ALIVE(VisitForValue(prop->key()));
6279 CHECK_ALIVE(VisitForValue(expr->value()));
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006280 HValue* value = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006281 HValue* key = Pop();
6282 HValue* object = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006283 bool has_side_effects = false;
6284 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
6285 expr->position(),
6286 true, // is_store
6287 &has_side_effects);
6288 Push(value);
6289 ASSERT(has_side_effects); // Stores always have side effects.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006290 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006291 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006292 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006293}
6294
6295
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006296// Because not every expression has a position and there is not common
6297// superclass of Assignment and CountOperation, we cannot just pass the
6298// owning expression instead of position and ast_id separately.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006299void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
6300 Variable* var,
6301 HValue* value,
6302 int position,
6303 BailoutId ast_id) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006304 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006305 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
6306 if (type == kUseCell) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006307 Handle<GlobalObject> global(info()->global_object());
6308 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006309 HInstruction* instr =
6310 new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006311 instr->set_position(position);
6312 AddInstruction(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006313 if (instr->HasObservableSideEffects()) {
6314 AddSimulate(ast_id, REMOVABLE_SIMULATE);
6315 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006316 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006317 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006318 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
6319 AddInstruction(global_object);
6320 HStoreGlobalGeneric* instr =
6321 new(zone()) HStoreGlobalGeneric(context,
6322 global_object,
6323 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006324 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006325 function_strict_mode_flag());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006326 instr->set_position(position);
6327 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006328 ASSERT(instr->HasObservableSideEffects());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006329 AddSimulate(ast_id, REMOVABLE_SIMULATE);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006330 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006331}
6332
6333
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006334void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006335 Expression* target = expr->target();
6336 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006337 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006338 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006339
6340 // We have a second position recorded in the FullCodeGenerator to have
6341 // type feedback for the binary operation.
6342 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006343
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006344 if (proxy != NULL) {
6345 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006346 if (var->mode() == LET) {
6347 return Bailout("unsupported let compound assignment");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006348 }
6349
danno@chromium.org160a7b02011-04-18 15:51:38 +00006350 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006351
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006352 switch (var->location()) {
6353 case Variable::UNALLOCATED:
6354 HandleGlobalVariableAssignment(var,
6355 Top(),
6356 expr->position(),
6357 expr->AssignmentId());
6358 break;
6359
6360 case Variable::PARAMETER:
6361 case Variable::LOCAL:
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006362 if (var->mode() == CONST) {
6363 return Bailout("unsupported const compound assignment");
6364 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006365 Bind(var, Top());
6366 break;
6367
6368 case Variable::CONTEXT: {
6369 // Bail out if we try to mutate a parameter value in a function
6370 // using the arguments object. We do not (yet) correctly handle the
6371 // arguments property of the function.
6372 if (info()->scope()->arguments() != NULL) {
6373 // Parameters will be allocated to context slots. We have no
6374 // direct way to detect that the variable is a parameter so we do
6375 // a linear search of the parameter variables.
6376 int count = info()->scope()->num_parameters();
6377 for (int i = 0; i < count; ++i) {
6378 if (var == info()->scope()->parameter(i)) {
6379 Bailout(
6380 "assignment to parameter, function uses arguments object");
6381 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006382 }
6383 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006384
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006385 HStoreContextSlot::Mode mode;
6386
6387 switch (var->mode()) {
6388 case LET:
6389 mode = HStoreContextSlot::kCheckDeoptimize;
6390 break;
6391 case CONST:
6392 return ast_context()->ReturnValue(Pop());
6393 case CONST_HARMONY:
6394 // This case is checked statically so no need to
6395 // perform checks here
6396 UNREACHABLE();
6397 default:
6398 mode = HStoreContextSlot::kNoCheck;
6399 }
6400
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006401 HValue* context = BuildContextChainWalk(var);
6402 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006403 new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006404 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006405 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006406 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006407 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006408 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00006409 }
6410
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006411 case Variable::LOOKUP:
6412 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006413 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006414 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006415
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006416 } else if (prop != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006417 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006418
6419 if (prop->key()->IsPropertyName()) {
6420 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006421 CHECK_ALIVE(VisitForValue(prop->obj()));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006422 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006423
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006424 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00006425 Handle<Map> map;
6426 HInstruction* load;
erik.corry@gmail.com88767242012-08-08 14:43:45 +00006427 bool monomorphic = prop->IsMonomorphic();
6428 if (monomorphic) {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00006429 map = prop->GetReceiverTypes()->first();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00006430 // We can't generate code for a monomorphic dict mode load so
6431 // just pretend it is not monomorphic.
6432 if (map->is_dictionary_map()) monomorphic = false;
6433 }
6434 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006435 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006436 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006437 if (LookupGetter(map, name, &getter, &holder)) {
6438 load = BuildCallGetter(object, map, getter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006439 } else {
6440 load = BuildLoadNamedMonomorphic(object, name, prop, map);
6441 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006442 } else {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006443 load = BuildLoadNamedGeneric(object, name, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006444 }
6445 PushAndAdd(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006446 if (load->HasObservableSideEffects()) {
6447 AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
6448 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006449
danno@chromium.org160a7b02011-04-18 15:51:38 +00006450 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006451 HValue* right = Pop();
6452 HValue* left = Pop();
6453
6454 HInstruction* instr = BuildBinaryOperation(operation, left, right);
6455 PushAndAdd(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006456 if (instr->HasObservableSideEffects()) {
6457 AddSimulate(operation->id(), REMOVABLE_SIMULATE);
6458 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006459
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006460 HInstruction* store;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006461 if (!monomorphic || map->is_observed()) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006462 // If we don't know the monomorphic type, do a generic store.
6463 CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, instr));
6464 } else {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006465 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006466 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00006467 if (LookupSetter(map, name, &setter, &holder)) {
6468 store = BuildCallSetter(object, instr, map, setter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00006469 } else {
6470 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object,
6471 name,
6472 instr,
6473 map));
6474 }
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006475 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006476 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006477 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006478 Drop(2);
6479 Push(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006480 if (store->HasObservableSideEffects()) {
6481 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
6482 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006483 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006484
6485 } else {
6486 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006487 CHECK_ALIVE(VisitForValue(prop->obj()));
6488 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006489 HValue* obj = environment()->ExpressionStackAt(1);
6490 HValue* key = environment()->ExpressionStackAt(0);
6491
whesse@chromium.org7b260152011-06-20 15:33:18 +00006492 bool has_side_effects = false;
6493 HValue* load = HandleKeyedElementAccess(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00006494 obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
whesse@chromium.org7b260152011-06-20 15:33:18 +00006495 false, // is_store
6496 &has_side_effects);
6497 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006498 if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006499
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006500
danno@chromium.org160a7b02011-04-18 15:51:38 +00006501 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006502 HValue* right = Pop();
6503 HValue* left = Pop();
6504
6505 HInstruction* instr = BuildBinaryOperation(operation, left, right);
6506 PushAndAdd(instr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006507 if (instr->HasObservableSideEffects()) {
6508 AddSimulate(operation->id(), REMOVABLE_SIMULATE);
6509 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006510
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006511 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00006512 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
6513 RelocInfo::kNoPosition,
6514 true, // is_store
6515 &has_side_effects);
6516
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006517 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006518 Drop(3);
6519 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006520 ASSERT(has_side_effects); // Stores always have side effects.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006521 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006522 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006523 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006524
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006525 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006526 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006527 }
6528}
6529
6530
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006531void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006532 ASSERT(!HasStackOverflow());
6533 ASSERT(current_block() != NULL);
6534 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006535 VariableProxy* proxy = expr->target()->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006536 Property* prop = expr->target()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006537 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006538
6539 if (expr->is_compound()) {
6540 HandleCompoundAssignment(expr);
6541 return;
6542 }
6543
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006544 if (prop != NULL) {
6545 HandlePropertyAssignment(expr);
6546 } else if (proxy != NULL) {
6547 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006548
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00006549 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006550 if (expr->op() != Token::INIT_CONST) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006551 CHECK_ALIVE(VisitForValue(expr->value()));
6552 return ast_context()->ReturnValue(Pop());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006553 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006554
6555 if (var->IsStackAllocated()) {
6556 // We insert a use of the old value to detect unsupported uses of const
6557 // variables (e.g. initialization inside a loop).
6558 HValue* old_value = environment()->Lookup(var);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006559 AddInstruction(new(zone()) HUseConst(old_value));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006560 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006561 } else if (var->mode() == CONST_HARMONY) {
6562 if (expr->op() != Token::INIT_CONST_HARMONY) {
6563 return Bailout("non-initializer assignment to const");
6564 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006565 }
6566
danno@chromium.org160a7b02011-04-18 15:51:38 +00006567 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006568
6569 // Handle the assignment.
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006570 switch (var->location()) {
6571 case Variable::UNALLOCATED:
6572 CHECK_ALIVE(VisitForValue(expr->value()));
6573 HandleGlobalVariableAssignment(var,
6574 Top(),
6575 expr->position(),
6576 expr->AssignmentId());
6577 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006578
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006579 case Variable::PARAMETER:
6580 case Variable::LOCAL: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006581 // Perform an initialization check for let declared variables
6582 // or parameters.
6583 if (var->mode() == LET && expr->op() == Token::ASSIGN) {
6584 HValue* env_value = environment()->Lookup(var);
6585 if (env_value == graph()->GetConstantHole()) {
6586 return Bailout("assignment to let variable before initialization");
6587 }
6588 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006589 // We do not allow the arguments object to occur in a context where it
6590 // may escape, but assignments to stack-allocated locals are
6591 // permitted.
6592 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
6593 HValue* value = Pop();
6594 Bind(var, value);
6595 return ast_context()->ReturnValue(value);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006596 }
6597
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006598 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006599 // Bail out if we try to mutate a parameter value in a function using
6600 // the arguments object. We do not (yet) correctly handle the
6601 // arguments property of the function.
6602 if (info()->scope()->arguments() != NULL) {
6603 // Parameters will rewrite to context slots. We have no direct way
6604 // to detect that the variable is a parameter.
6605 int count = info()->scope()->num_parameters();
6606 for (int i = 0; i < count; ++i) {
6607 if (var == info()->scope()->parameter(i)) {
6608 return Bailout("assignment to parameter in arguments object");
6609 }
6610 }
6611 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006612
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006613 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006614 HStoreContextSlot::Mode mode;
6615 if (expr->op() == Token::ASSIGN) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006616 switch (var->mode()) {
6617 case LET:
6618 mode = HStoreContextSlot::kCheckDeoptimize;
6619 break;
6620 case CONST:
6621 return ast_context()->ReturnValue(Pop());
6622 case CONST_HARMONY:
6623 // This case is checked statically so no need to
6624 // perform checks here
6625 UNREACHABLE();
6626 default:
6627 mode = HStoreContextSlot::kNoCheck;
6628 }
6629 } else if (expr->op() == Token::INIT_VAR ||
6630 expr->op() == Token::INIT_LET ||
6631 expr->op() == Token::INIT_CONST_HARMONY) {
6632 mode = HStoreContextSlot::kNoCheck;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006633 } else {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006634 ASSERT(expr->op() == Token::INIT_CONST);
6635
6636 mode = HStoreContextSlot::kCheckIgnoreAssignment;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006637 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006638
6639 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006640 HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
6641 context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006642 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006643 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006644 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006645 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006646 return ast_context()->ReturnValue(Pop());
6647 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006648
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006649 case Variable::LOOKUP:
6650 return Bailout("assignment to LOOKUP variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006651 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006652 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006653 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006654 }
6655}
6656
6657
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006658void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006659 ASSERT(!HasStackOverflow());
6660 ASSERT(current_block() != NULL);
6661 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006662 // We don't optimize functions with invalid left-hand sides in
6663 // assignments, count operations, or for-in. Consequently throw can
6664 // currently only occur in an effect context.
6665 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006666 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006667
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006668 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006669 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006670 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006671 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00006672 AddInstruction(instr);
6673 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006674 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006675 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006676}
6677
6678
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006679HLoadNamedField* HOptimizedGraphBuilder::BuildLoadNamedField(
6680 HValue* object,
6681 Handle<Map> map,
6682 LookupResult* lookup) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006683 int index = lookup->GetLocalFieldIndexFromMap(*map);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006684 if (index < 0) {
6685 // Negative property indices are in-object properties, indexed
6686 // from the end of the fixed part of the object.
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006687 int offset = (index * kPointerSize) + map->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006688 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006689 } else {
6690 // Non-negative property indices are in the properties array.
6691 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006692 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006693 }
6694}
6695
6696
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006697HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric(
6698 HValue* object,
6699 Handle<String> name,
6700 Property* expr) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00006701 if (expr->IsUninitialized()) {
6702 AddSoftDeoptimize();
jkummerow@chromium.org531dfe82012-03-20 13:01:16 +00006703 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006704 HValue* context = environment()->LookupContext();
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006705 return new(zone()) HLoadNamedGeneric(context, object, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006706}
6707
6708
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006709HInstruction* HOptimizedGraphBuilder::BuildCallGetter(
6710 HValue* object,
6711 Handle<Map> map,
6712 Handle<JSFunction> getter,
6713 Handle<JSObject> holder) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006714 AddCheckConstantFunction(holder, object, map);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006715 AddInstruction(new(zone()) HPushArgument(object));
6716 return new(zone()) HCallConstantFunction(getter, 1);
danno@chromium.org81cac2b2012-07-10 11:28:27 +00006717}
6718
6719
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006720HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
6721 HValue* object,
6722 Handle<String> name,
6723 Property* expr,
6724 Handle<Map> map) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006725 // Handle a load from a known field.
erik.corry@gmail.com88767242012-08-08 14:43:45 +00006726 ASSERT(!map->is_dictionary_map());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006727 LookupResult lookup(isolate());
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00006728 map->LookupDescriptor(NULL, *name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00006729 if (lookup.IsField()) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006730 AddCheckMapsWithTransitions(object, map);
6731 return BuildLoadNamedField(object, map, &lookup);
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006732 }
6733
6734 // Handle a load of a constant known function.
6735 if (lookup.IsConstantFunction()) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006736 AddCheckMapsWithTransitions(object, map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006737 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006738 return new(zone()) HConstant(function, Representation::Tagged());
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006739 }
6740
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00006741 // Handle a load from a known field somewhere in the prototype chain.
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006742 LookupInPrototypes(map, name, &lookup);
6743 if (lookup.IsField()) {
6744 Handle<JSObject> prototype(JSObject::cast(map->prototype()));
6745 Handle<JSObject> holder(lookup.holder());
6746 Handle<Map> holder_map(holder->map());
6747 AddCheckMapsWithTransitions(object, map);
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00006748 HInstruction* holder_value = AddInstruction(
6749 new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006750 return BuildLoadNamedField(holder_value, holder_map, &lookup);
6751 }
6752
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00006753 // Handle a load of a constant function somewhere in the prototype chain.
6754 if (lookup.IsConstantFunction()) {
6755 Handle<JSObject> prototype(JSObject::cast(map->prototype()));
6756 Handle<JSObject> holder(lookup.holder());
6757 Handle<Map> holder_map(holder->map());
6758 AddCheckMapsWithTransitions(object, map);
6759 AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
6760 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*holder_map));
6761 return new(zone()) HConstant(function, Representation::Tagged());
6762 }
6763
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00006764 // No luck, do a generic load.
6765 return BuildLoadNamedGeneric(object, name, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006766}
6767
6768
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006769HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
6770 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006771 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006772 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006773}
6774
6775
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006776HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
6777 HValue* object,
6778 HValue* key,
whesse@chromium.org7b260152011-06-20 15:33:18 +00006779 HValue* val,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00006780 HValue* dependency,
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006781 Handle<Map> map,
whesse@chromium.org7b260152011-06-20 15:33:18 +00006782 bool is_store) {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006783 HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
6784 zone(), dependency);
6785 AddInstruction(mapcheck);
6786 if (dependency) {
6787 mapcheck->ClearGVNFlag(kDependsOnElementsKind);
6788 }
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006789 return BuildUncheckedMonomorphicElementAccess(
6790 object, key, val,
6791 mapcheck, map->instance_type() == JS_ARRAY_TYPE,
6792 map->elements_kind(), is_store);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006793}
6794
6795
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006796HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad(
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006797 HValue* object,
6798 HValue* key,
6799 HValue* val,
6800 SmallMapList* maps) {
6801 // For polymorphic loads of similar elements kinds (i.e. all tagged or all
6802 // double), always use the "worst case" code without a transition. This is
6803 // much faster than transitioning the elements to the worst case, trading a
6804 // HTransitionElements for a HCheckMaps, and avoiding mutation of the array.
6805 bool has_double_maps = false;
6806 bool has_smi_or_object_maps = false;
6807 bool has_js_array_access = false;
6808 bool has_non_js_array_access = false;
6809 Handle<Map> most_general_consolidated_map;
6810 for (int i = 0; i < maps->length(); ++i) {
6811 Handle<Map> map = maps->at(i);
6812 // Don't allow mixing of JSArrays with JSObjects.
6813 if (map->instance_type() == JS_ARRAY_TYPE) {
6814 if (has_non_js_array_access) return NULL;
6815 has_js_array_access = true;
6816 } else if (has_js_array_access) {
6817 return NULL;
6818 } else {
6819 has_non_js_array_access = true;
6820 }
6821 // Don't allow mixed, incompatible elements kinds.
6822 if (map->has_fast_double_elements()) {
6823 if (has_smi_or_object_maps) return NULL;
6824 has_double_maps = true;
6825 } else if (map->has_fast_smi_or_object_elements()) {
6826 if (has_double_maps) return NULL;
6827 has_smi_or_object_maps = true;
6828 } else {
6829 return NULL;
6830 }
6831 // Remember the most general elements kind, the code for its load will
6832 // properly handle all of the more specific cases.
6833 if ((i == 0) || IsMoreGeneralElementsKindTransition(
6834 most_general_consolidated_map->elements_kind(),
6835 map->elements_kind())) {
6836 most_general_consolidated_map = map;
6837 }
6838 }
6839 if (!has_double_maps && !has_smi_or_object_maps) return NULL;
6840
6841 HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
6842 AddInstruction(check_maps);
6843 HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006844 object, key, val, check_maps,
6845 most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE,
6846 most_general_consolidated_map->elements_kind(),
6847 false);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006848 return instr;
6849}
6850
6851
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00006852HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
6853 HValue* object,
6854 HValue* key,
6855 HValue* val,
6856 Expression* prop,
6857 BailoutId ast_id,
6858 int position,
6859 bool is_store,
6860 bool* has_side_effects) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00006861 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006862 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006863 SmallMapList* maps = prop->GetReceiverTypes();
whesse@chromium.org7b260152011-06-20 15:33:18 +00006864 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006865
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006866 if (!is_store) {
6867 HInstruction* consolidated_load =
6868 TryBuildConsolidatedElementLoad(object, key, val, maps);
6869 if (consolidated_load != NULL) {
6870 AddInstruction(consolidated_load);
6871 *has_side_effects |= consolidated_load->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006872 if (position != RelocInfo::kNoPosition) {
6873 consolidated_load->set_position(position);
6874 }
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00006875 return consolidated_load;
6876 }
6877 }
6878
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006879 static const int kNumElementTypes = kElementsKindCount;
whesse@chromium.org7b260152011-06-20 15:33:18 +00006880 bool type_todo[kNumElementTypes];
6881 for (int i = 0; i < kNumElementTypes; ++i) {
6882 type_todo[i] = false;
6883 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00006884
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006885 // Elements_kind transition support.
6886 MapHandleList transition_target(maps->length());
6887 // Collect possible transition targets.
6888 MapHandleList possible_transitioned_maps(maps->length());
whesse@chromium.org7b260152011-06-20 15:33:18 +00006889 for (int i = 0; i < maps->length(); ++i) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006890 Handle<Map> map = maps->at(i);
6891 ElementsKind elements_kind = map->elements_kind();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006892 if (IsFastElementsKind(elements_kind) &&
6893 elements_kind != GetInitialFastElementsKind()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006894 possible_transitioned_maps.Add(map);
6895 }
6896 }
6897 // Get transition target for each map (NULL == no transition).
6898 for (int i = 0; i < maps->length(); ++i) {
6899 Handle<Map> map = maps->at(i);
6900 Handle<Map> transitioned_map =
6901 map->FindTransitionedMap(&possible_transitioned_maps);
6902 transition_target.Add(transitioned_map);
6903 }
6904
6905 int num_untransitionable_maps = 0;
6906 Handle<Map> untransitionable_map;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006907 HTransitionElementsKind* transition = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006908 for (int i = 0; i < maps->length(); ++i) {
6909 Handle<Map> map = maps->at(i);
6910 ASSERT(map->IsMap());
6911 if (!transition_target.at(i).is_null()) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006912 ASSERT(Map::IsValidElementsTransition(
6913 map->elements_kind(),
6914 transition_target.at(i)->elements_kind()));
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00006915 HValue* context = environment()->LookupContext();
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006916 transition = new(zone()) HTransitionElementsKind(
danno@chromium.org94b0d6f2013-02-04 13:33:20 +00006917 context, object, map, transition_target.at(i));
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006918 AddInstruction(transition);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006919 } else {
6920 type_todo[map->elements_kind()] = true;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006921 if (IsExternalArrayElementsKind(map->elements_kind())) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006922 todo_external_array = true;
6923 }
6924 num_untransitionable_maps++;
6925 untransitionable_map = map;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006926 }
6927 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00006928
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006929 // If only one map is left after transitioning, handle this case
6930 // monomorphically.
6931 if (num_untransitionable_maps == 1) {
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006932 HInstruction* instr = NULL;
6933 if (untransitionable_map->has_slow_elements_kind()) {
6934 instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
6935 : BuildLoadKeyedGeneric(object, key));
6936 } else {
6937 instr = AddInstruction(BuildMonomorphicElementAccess(
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006938 object, key, val, transition, untransitionable_map, is_store));
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006939 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006940 *has_side_effects |= instr->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00006941 if (position != RelocInfo::kNoPosition) instr->set_position(position);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006942 return is_store ? NULL : instr;
6943 }
6944
verwaest@chromium.org68c05782012-09-04 09:58:32 +00006945 HInstruction* checkspec =
6946 AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006947 HBasicBlock* join = graph()->CreateBasicBlock();
6948
6949 HInstruction* elements_kind_instr =
6950 AddInstruction(new(zone()) HElementsKind(object));
verwaest@chromium.org68c05782012-09-04 09:58:32 +00006951 HInstruction* elements =
6952 AddInstruction(new(zone()) HLoadElements(object, checkspec));
whesse@chromium.org7b260152011-06-20 15:33:18 +00006953 HLoadExternalArrayPointer* external_elements = NULL;
6954 HInstruction* checked_key = NULL;
6955
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006956 // Generated code assumes that FAST_* and DICTIONARY_ELEMENTS ElementsKinds
6957 // are handled before external arrays.
6958 STATIC_ASSERT(FAST_SMI_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
6959 STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006960 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
6961 STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006962
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006963 for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006964 elements_kind <= LAST_ELEMENTS_KIND;
6965 elements_kind = ElementsKind(elements_kind + 1)) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006966 // After having handled FAST_* and DICTIONARY_ELEMENTS, we need to add some
6967 // code that's executed for all external array cases.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00006968 STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
6969 LAST_ELEMENTS_KIND);
6970 if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006971 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00006972 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006973 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00006974 checked_key = AddBoundsCheck(key, length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006975 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
6976 AddInstruction(external_elements);
6977 }
6978 if (type_todo[elements_kind]) {
6979 HBasicBlock* if_true = graph()->CreateBasicBlock();
6980 HBasicBlock* if_false = graph()->CreateBasicBlock();
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00006981 HCompareConstantEqAndBranch* elements_kind_branch =
6982 new(zone()) HCompareConstantEqAndBranch(
6983 elements_kind_instr, elements_kind, Token::EQ_STRICT);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006984 elements_kind_branch->SetSuccessorAt(0, if_true);
6985 elements_kind_branch->SetSuccessorAt(1, if_false);
6986 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006987
6988 set_current_block(if_true);
6989 HInstruction* access;
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006990 if (IsFastElementsKind(elements_kind)) {
6991 if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006992 AddInstruction(new(zone()) HCheckMaps(
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006993 elements, isolate()->factory()->fixed_array_map(),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00006994 zone(), elements_kind_branch));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00006995 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006996 // TODO(jkummerow): The need for these two blocks could be avoided
6997 // in one of two ways:
6998 // (1) Introduce ElementsKinds for JSArrays that are distinct from
6999 // those for fast objects.
7000 // (2) Put the common instructions into a third "join" block. This
7001 // requires additional AST IDs that we can deopt to from inside
7002 // that join block. They must be added to the Property class (when
7003 // it's a keyed property) and registered in the full codegen.
whesse@chromium.org7b260152011-06-20 15:33:18 +00007004 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
7005 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007006 HHasInstanceTypeAndBranch* typecheck =
7007 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
7008 typecheck->SetSuccessorAt(0, if_jsarray);
7009 typecheck->SetSuccessorAt(1, if_fastobject);
7010 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007011
7012 set_current_block(if_jsarray);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007013 HInstruction* length;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007014 length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck,
7015 HType::Smi()));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00007016 checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007017 access = AddInstruction(BuildFastElementAccess(
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007018 elements, checked_key, val, elements_kind_branch,
7019 elements_kind, is_store));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007020 if (!is_store) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00007021 Push(access);
7022 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007023
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007024 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00007025 if (position != -1) {
7026 access->set_position(position);
7027 }
7028 if_jsarray->Goto(join);
7029
7030 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00007031 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00007032 checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007033 access = AddInstruction(BuildFastElementAccess(
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007034 elements, checked_key, val, elements_kind_branch,
7035 elements_kind, is_store));
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00007036 } else if (elements_kind == DICTIONARY_ELEMENTS) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007037 if (is_store) {
7038 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
7039 } else {
7040 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
7041 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007042 } else { // External array elements.
7043 access = AddInstruction(BuildExternalArrayElementAccess(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007044 external_elements, checked_key, val,
7045 elements_kind_branch, elements_kind, is_store));
whesse@chromium.org7b260152011-06-20 15:33:18 +00007046 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007047 *has_side_effects |= access->HasObservableSideEffects();
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007048 if (position != RelocInfo::kNoPosition) access->set_position(position);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007049 if (!is_store) {
7050 Push(access);
7051 }
7052 current_block()->Goto(join);
7053 set_current_block(if_false);
7054 }
7055 }
7056
7057 // Deopt if none of the cases matched.
7058 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
7059 join->SetJoinId(ast_id);
7060 set_current_block(join);
7061 return is_store ? NULL : Pop();
7062}
7063
7064
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007065HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
7066 HValue* obj,
7067 HValue* key,
7068 HValue* val,
7069 Expression* expr,
7070 BailoutId ast_id,
7071 int position,
7072 bool is_store,
7073 bool* has_side_effects) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00007074 ASSERT(!expr->IsPropertyName());
7075 HInstruction* instr = NULL;
7076 if (expr->IsMonomorphic()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007077 Handle<Map> map = expr->GetMonomorphicReceiverType();
ulan@chromium.org2efb9002012-01-19 15:36:35 +00007078 if (map->has_slow_elements_kind()) {
7079 instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
7080 : BuildLoadKeyedGeneric(obj, key);
7081 } else {
7082 AddInstruction(new(zone()) HCheckNonSmi(obj));
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007083 instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00007084 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007085 } else if (expr->GetReceiverTypes() != NULL &&
7086 !expr->GetReceiverTypes()->is_empty()) {
7087 return HandlePolymorphicElementAccess(
7088 obj, key, val, expr, ast_id, position, is_store, has_side_effects);
7089 } else {
7090 if (is_store) {
7091 instr = BuildStoreKeyedGeneric(obj, key, val);
7092 } else {
7093 instr = BuildLoadKeyedGeneric(obj, key);
7094 }
7095 }
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00007096 if (position != RelocInfo::kNoPosition) instr->set_position(position);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007097 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007098 *has_side_effects = instr->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00007099 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007100}
7101
7102
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007103HInstruction* HOptimizedGraphBuilder::BuildStoreKeyedGeneric(
7104 HValue* object,
7105 HValue* key,
7106 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007107 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007108 return new(zone()) HStoreKeyedGeneric(
7109 context,
7110 object,
7111 key,
7112 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007113 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007114}
7115
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007116
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007117void HOptimizedGraphBuilder::EnsureArgumentsArePushedForAccess() {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007118 // Outermost function already has arguments on the stack.
7119 if (function_state()->outer() == NULL) return;
7120
7121 if (function_state()->arguments_pushed()) return;
7122
7123 // Push arguments when entering inlined function.
7124 HEnterInlined* entry = function_state()->entry();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00007125 entry->set_arguments_pushed();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007126
7127 ZoneList<HValue*>* arguments_values = entry->arguments_values();
7128
7129 HInstruction* insert_after = entry;
7130 for (int i = 0; i < arguments_values->length(); i++) {
7131 HValue* argument = arguments_values->at(i);
7132 HInstruction* push_argument = new(zone()) HPushArgument(argument);
7133 push_argument->InsertAfter(insert_after);
7134 insert_after = push_argument;
7135 }
7136
7137 HArgumentsElements* arguments_elements =
7138 new(zone()) HArgumentsElements(true);
7139 arguments_elements->ClearFlag(HValue::kUseGVN);
7140 arguments_elements->InsertAfter(insert_after);
7141 function_state()->set_arguments_elements(arguments_elements);
7142}
7143
7144
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007145bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007146 VariableProxy* proxy = expr->obj()->AsVariableProxy();
7147 if (proxy == NULL) return false;
7148 if (!proxy->var()->IsStackAllocated()) return false;
7149 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
7150 return false;
7151 }
7152
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007153 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007154 if (expr->key()->IsPropertyName()) {
7155 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00007156 if (!name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("length"))) return false;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007157
7158 if (function_state()->outer() == NULL) {
7159 HInstruction* elements = AddInstruction(
7160 new(zone()) HArgumentsElements(false));
7161 result = new(zone()) HArgumentsLength(elements);
7162 } else {
7163 // Number of arguments without receiver.
7164 int argument_count = environment()->
7165 arguments_environment()->parameter_count() - 1;
7166 result = new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00007167 Handle<Object>(Smi::FromInt(argument_count), isolate()),
7168 Representation::Integer32());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007169 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007170 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00007171 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007172 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007173 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007174 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00007175 Drop(1); // Arguments object.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007176 if (function_state()->outer() == NULL) {
7177 HInstruction* elements = AddInstruction(
7178 new(zone()) HArgumentsElements(false));
7179 HInstruction* length = AddInstruction(
7180 new(zone()) HArgumentsLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00007181 HInstruction* checked_key = AddBoundsCheck(key, length);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007182 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
7183 } else {
7184 EnsureArgumentsArePushedForAccess();
7185
7186 // Number of arguments without receiver.
7187 HInstruction* elements = function_state()->arguments_elements();
7188 int argument_count = environment()->
7189 arguments_environment()->parameter_count() - 1;
7190 HInstruction* length = AddInstruction(new(zone()) HConstant(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00007191 Handle<Object>(Smi::FromInt(argument_count), isolate()),
7192 Representation::Integer32()));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00007193 HInstruction* checked_key = AddBoundsCheck(key, length);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007194 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
7195 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007196 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007197 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007198 return true;
7199}
7200
7201
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007202void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007203 ASSERT(!HasStackOverflow());
7204 ASSERT(current_block() != NULL);
7205 ASSERT(current_block()->HasPredecessor());
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007206 expr->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007207
7208 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007209
danno@chromium.org160a7b02011-04-18 15:51:38 +00007210 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007211
7212 HInstruction* instr = NULL;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007213 if (expr->AsProperty()->IsArrayLength()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007214 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007215 AddInstruction(new(zone()) HCheckNonSmi(array));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007216 HInstruction* mapcheck =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007217 AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone()));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007218 instr = new(zone()) HJSArrayLength(array, mapcheck);
ager@chromium.org378b34e2011-01-28 08:04:38 +00007219 } else if (expr->IsStringLength()) {
7220 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007221 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007222 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007223 instr = HStringLength::New(zone(), string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007224 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007225 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007226 HValue* index = Pop();
7227 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007228 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007229 HInstruction* char_code =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007230 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007231 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007232 instr = HStringCharFromCode::New(zone(), context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00007233
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00007234 } else if (expr->IsFunctionPrototype()) {
7235 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007236 AddInstruction(new(zone()) HCheckNonSmi(function));
7237 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007238
7239 } else if (expr->key()->IsPropertyName()) {
7240 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00007241 SmallMapList* types = expr->GetReceiverTypes();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00007242 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007243
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007244 Handle<Map> map;
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00007245 bool monomorphic = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007246 if (expr->IsMonomorphic()) {
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007247 map = types->first();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00007248 monomorphic = !map->is_dictionary_map();
7249 } else if (object->HasMonomorphicJSObjectType()) {
7250 map = object->GetMonomorphicJSObjectMap();
7251 monomorphic = !map->is_dictionary_map();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00007252 }
7253 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007254 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007255 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00007256 if (LookupGetter(map, name, &getter, &holder)) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007257 AddCheckConstantFunction(holder, Top(), map);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007258 if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
7259 AddInstruction(new(zone()) HPushArgument(Pop()));
7260 instr = new(zone()) HCallConstantFunction(getter, 1);
7261 } else {
7262 instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
7263 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007264 } else if (types != NULL && types->length() > 1) {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007265 return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007266 } else {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007267 instr = BuildLoadNamedGeneric(Pop(), name, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007268 }
7269
7270 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007271 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007272
7273 HValue* key = Pop();
7274 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00007275
7276 bool has_side_effects = false;
7277 HValue* load = HandleKeyedElementAccess(
7278 obj, key, NULL, expr, expr->id(), expr->position(),
7279 false, // is_store
7280 &has_side_effects);
7281 if (has_side_effects) {
7282 if (ast_context()->IsEffect()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007283 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007284 } else {
7285 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007286 AddSimulate(expr->id(), REMOVABLE_SIMULATE);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007287 Drop(1);
7288 }
7289 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007290 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007291 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007292 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007293 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007294}
7295
7296
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007297void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
7298 Handle<Map> receiver_map) {
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00007299 if (!holder.is_null()) {
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007300 Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
7301 AddInstruction(
7302 new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007303 }
7304}
7305
7306
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007307void HOptimizedGraphBuilder::AddCheckConstantFunction(
7308 Handle<JSObject> holder,
7309 HValue* receiver,
7310 Handle<Map> receiver_map) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007311 // Constant functions have the nice property that the map will change if they
7312 // are overwritten. Therefore it is enough to check the map of the holder and
7313 // its prototypes.
7314 AddCheckMapsWithTransitions(receiver, receiver_map);
7315 AddCheckPrototypeMaps(holder, receiver_map);
7316}
7317
7318
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007319class FunctionSorter {
7320 public:
7321 FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
7322 FunctionSorter(int index, int ticks, int ast_length, int src_length)
7323 : index_(index),
7324 ticks_(ticks),
7325 ast_length_(ast_length),
7326 src_length_(src_length) { }
7327
7328 int index() const { return index_; }
7329 int ticks() const { return ticks_; }
7330 int ast_length() const { return ast_length_; }
7331 int src_length() const { return src_length_; }
7332
7333 private:
7334 int index_;
7335 int ticks_;
7336 int ast_length_;
7337 int src_length_;
7338};
7339
7340
7341static int CompareHotness(void const* a, void const* b) {
7342 FunctionSorter const* function1 = reinterpret_cast<FunctionSorter const*>(a);
7343 FunctionSorter const* function2 = reinterpret_cast<FunctionSorter const*>(b);
7344 int diff = function1->ticks() - function2->ticks();
7345 if (diff != 0) return -diff;
7346 diff = function1->ast_length() - function2->ast_length();
7347 if (diff != 0) return diff;
7348 return function1->src_length() - function2->src_length();
7349}
7350
7351
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007352void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
7353 Call* expr,
7354 HValue* receiver,
7355 SmallMapList* types,
7356 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007357 // TODO(ager): We should recognize when the prototype chains for different
7358 // maps are identical. In that case we can avoid repeatedly generating the
7359 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007360 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007361 HBasicBlock* join = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007362 FunctionSorter order[kMaxCallPolymorphism];
7363 int ordered_functions = 0;
7364 for (int i = 0;
7365 i < types->length() && ordered_functions < kMaxCallPolymorphism;
7366 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007367 Handle<Map> map = types->at(i);
7368 if (expr->ComputeTarget(map, name)) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007369 order[ordered_functions++] =
7370 FunctionSorter(i,
7371 expr->target()->shared()->profiler_ticks(),
7372 InliningAstSize(expr->target()),
7373 expr->target()->shared()->SourceSize());
danno@chromium.org2c26cb12012-05-03 09:06:43 +00007374 }
danno@chromium.org2c26cb12012-05-03 09:06:43 +00007375 }
7376
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007377 qsort(reinterpret_cast<void*>(&order[0]),
7378 ordered_functions,
7379 sizeof(order[0]),
7380 &CompareHotness);
7381
7382 for (int fn = 0; fn < ordered_functions; ++fn) {
7383 int i = order[fn].index();
7384 Handle<Map> map = types->at(i);
7385 if (fn == 0) {
7386 // Only needed once.
7387 AddInstruction(new(zone()) HCheckNonSmi(receiver));
7388 join = graph()->CreateBasicBlock();
7389 }
7390 HBasicBlock* if_true = graph()->CreateBasicBlock();
7391 HBasicBlock* if_false = graph()->CreateBasicBlock();
7392 HCompareMap* compare =
7393 new(zone()) HCompareMap(receiver, map, if_true, if_false);
7394 current_block()->Finish(compare);
7395
7396 set_current_block(if_true);
7397 expr->ComputeTarget(map, name);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007398 AddCheckPrototypeMaps(expr->holder(), map);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007399 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
7400 Handle<JSFunction> caller = info()->closure();
7401 SmartArrayPointer<char> caller_name =
7402 caller->shared()->DebugName()->ToCString();
7403 PrintF("Trying to inline the polymorphic call to %s from %s\n",
7404 *name->ToCString(),
7405 *caller_name);
7406 }
7407 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
7408 // Trying to inline will signal that we should bailout from the
7409 // entire compilation by setting stack overflow on the visitor.
7410 if (HasStackOverflow()) return;
7411 } else {
7412 HCallConstantFunction* call =
7413 new(zone()) HCallConstantFunction(expr->target(), argument_count);
7414 call->set_position(expr->position());
7415 PreProcessCall(call);
7416 AddInstruction(call);
7417 if (!ast_context()->IsEffect()) Push(call);
7418 }
7419
7420 if (current_block() != NULL) current_block()->Goto(join);
7421 set_current_block(if_false);
7422 }
7423
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007424 // Finish up. Unconditionally deoptimize if we've handled all the maps we
7425 // know about and do not want to handle ones we've never seen. Otherwise
7426 // use a generic IC.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007427 if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007428 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007429 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007430 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007431 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007432 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007433 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00007434
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007435 if (join != NULL) {
7436 AddInstruction(call);
7437 if (!ast_context()->IsEffect()) Push(call);
7438 current_block()->Goto(join);
7439 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007440 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00007441 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007442 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007443
7444 // We assume that control flow is always live after an expression. So
7445 // even without predecessors to the join block, we set it as the exit
7446 // block and continue by adding instructions there.
7447 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007448 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00007449 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007450 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007451 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007452 } else {
7453 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007454 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007455}
7456
7457
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007458void HOptimizedGraphBuilder::TraceInline(Handle<JSFunction> target,
7459 Handle<JSFunction> caller,
7460 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007461 if (FLAG_trace_inlining) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00007462 SmartArrayPointer<char> target_name =
7463 target->shared()->DebugName()->ToCString();
7464 SmartArrayPointer<char> caller_name =
7465 caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007466 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007467 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007468 } else {
7469 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007470 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007471 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007472 }
7473}
7474
7475
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007476static const int kNotInlinable = 1000000000;
7477
7478
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007479int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007480 if (!FLAG_use_inlining) return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007481
7482 // Precondition: call is monomorphic and we have found a target with the
7483 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007484 Handle<JSFunction> caller = info()->closure();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007485 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007486
7487 // Do a quick check on source code length to avoid parsing large
7488 // inlining candidates.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00007489 if (target_shared->SourceSize() >
7490 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007491 TraceInline(target, caller, "target text too big");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007492 return kNotInlinable;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007493 }
7494
7495 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007496 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007497 TraceInline(target, caller, "target not inlineable");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007498 return kNotInlinable;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007499 }
yangguo@chromium.org56454712012-02-16 15:33:53 +00007500 if (target_shared->dont_inline() || target_shared->dont_optimize()) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00007501 TraceInline(target, caller, "target contains unsupported syntax [early]");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007502 return kNotInlinable;
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00007503 }
7504
7505 int nodes_added = target_shared->ast_node_count();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007506 return nodes_added;
7507}
7508
7509
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007510bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
7511 Handle<JSFunction> target,
7512 int arguments_count,
7513 HValue* implicit_return_value,
7514 BailoutId ast_id,
7515 BailoutId return_id,
7516 InliningKind inlining_kind) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00007517 int nodes_added = InliningAstSize(target);
7518 if (nodes_added == kNotInlinable) return false;
7519
7520 Handle<JSFunction> caller = info()->closure();
7521
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00007522 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00007523 TraceInline(target, caller, "target AST is too large [early]");
7524 return false;
7525 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007526
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007527#if !defined(V8_TARGET_ARCH_IA32)
7528 // Target must be able to use caller's context.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007529 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007530 if (target->context() != outer_info->closure()->context() ||
7531 outer_info->scope()->contains_with() ||
7532 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007533 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007534 return false;
7535 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007536#endif
7537
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007538
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007539 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007540 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007541 int current_level = 1;
7542 while (env->outer() != NULL) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007543 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007544 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007545 return false;
7546 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00007547 if (env->outer()->frame_type() == JS_FUNCTION) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007548 current_level++;
7549 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007550 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007551 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007552
7553 // Don't inline recursive functions.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007554 for (FunctionState* state = function_state();
7555 state != NULL;
7556 state = state->outer()) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007557 if (*state->compilation_info()->closure() == *target) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007558 TraceInline(target, caller, "target is recursive");
7559 return false;
7560 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007561 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007562
7563 // We don't want to add more than a certain number of nodes from inlining.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00007564 if (inlined_count_ > Min(FLAG_max_inlined_nodes_cumulative,
7565 kUnlimitedMaxInlinedNodesCumulative)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007566 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007567 return false;
7568 }
7569
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007570 // Parse and allocate variables.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00007571 CompilationInfo target_info(target, zone());
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007572 Handle<SharedFunctionInfo> target_shared(target->shared());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00007573 if (!ParserApi::Parse(&target_info, kNoParsingFlags) ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007574 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007575 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007576 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007577 SetStackOverflow();
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007578 target_shared->DisableOptimization("parse/scope error");
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007579 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007580 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007581 return false;
7582 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007583
7584 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007585 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007586 return false;
7587 }
7588 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007589
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00007590 // The following conditions must be checked again after re-parsing, because
7591 // earlier the information might not have been complete due to lazy parsing.
7592 nodes_added = function->ast_node_count();
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00007593 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00007594 TraceInline(target, caller, "target AST is too large [late]");
7595 return false;
7596 }
7597 AstProperties::Flags* flags(function->flags());
7598 if (flags->Contains(kDontInline) || flags->Contains(kDontOptimize)) {
7599 TraceInline(target, caller, "target contains unsupported syntax [late]");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007600 return false;
7601 }
7602
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007603 // If the function uses the arguments object check that inlining of functions
7604 // with arguments object is enabled and the arguments-variable is
7605 // stack allocated.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007606 if (function->scope()->arguments() != NULL) {
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007607 if (!FLAG_inline_arguments) {
7608 TraceInline(target, caller, "target uses arguments object");
7609 return false;
7610 }
7611
7612 if (!function->scope()->arguments()->IsStackAllocated()) {
7613 TraceInline(target,
7614 caller,
7615 "target uses non-stackallocated arguments object");
7616 return false;
7617 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007618 }
7619
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007620 // All declarations must be inlineable.
7621 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
7622 int decl_count = decls->length();
7623 for (int i = 0; i < decl_count; ++i) {
7624 if (!decls->at(i)->IsInlineable()) {
7625 TraceInline(target, caller, "target has non-trivial declaration");
7626 return false;
7627 }
7628 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007629
7630 // Generate the deoptimization data for the unoptimized version of
7631 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007632 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007633 // Note that we compile here using the same AST that we will use for
7634 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007635 target_info.EnableDeoptimizationSupport();
7636 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007637 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007638 return false;
7639 }
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007640 if (target_shared->scope_info() == ScopeInfo::Empty(isolate())) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007641 // The scope info might not have been set if a lazily compiled
7642 // function is inlined before being called for the first time.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007643 Handle<ScopeInfo> target_scope_info =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007644 ScopeInfo::Create(target_info.scope(), zone());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007645 target_shared->set_scope_info(*target_scope_info);
7646 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007647 target_shared->EnableDeoptimizationSupport(*target_info.code());
7648 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
7649 &target_info,
7650 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007651 }
7652
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007653 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007654 // After this point, we've made a decision to inline this function (so
7655 // TryInline should always return true).
7656
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007657 // Save the pending call context and type feedback oracle. Set up new ones
7658 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007659 ASSERT(target_shared->has_deoptimization_support());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007660 Handle<Code> unoptimized_code(target_shared->code());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007661 TypeFeedbackOracle target_oracle(
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007662 unoptimized_code,
7663 Handle<Context>(target->context()->native_context()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007664 isolate(),
7665 zone());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007666 // The function state is new-allocated because we need to delete it
7667 // in two different places.
ulan@chromium.org967e2702012-02-28 09:49:15 +00007668 FunctionState* target_state = new FunctionState(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007669 this, &target_info, &target_oracle, inlining_kind);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007670
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007671 HConstant* undefined = graph()->GetConstantUndefined();
yangguo@chromium.org003650e2013-01-24 16:31:08 +00007672 bool undefined_receiver = HEnvironment::UseUndefinedReceiver(
7673 target, function, call_kind, inlining_kind);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007674 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00007675 environment()->CopyForInlining(target,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007676 arguments_count,
lrn@chromium.org1c092762011-05-09 09:42:16 +00007677 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00007678 undefined,
yangguo@chromium.org003650e2013-01-24 16:31:08 +00007679 function_state()->inlining_kind(),
7680 undefined_receiver);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007681#ifdef V8_TARGET_ARCH_IA32
7682 // IA32 only, overwrite the caller's context in the deoptimization
7683 // environment with the correct one.
7684 //
7685 // TODO(kmillikin): implement the same inlining on other platforms so we
7686 // can remove the unsightly ifdefs in this function.
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007687 HConstant* context =
7688 new(zone()) HConstant(Handle<Context>(target->context()),
7689 Representation::Tagged());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007690 AddInstruction(context);
7691 inner_env->BindContext(context);
7692#endif
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007693
danno@chromium.orgb2a1c072012-03-23 15:47:56 +00007694 AddSimulate(return_id);
7695 current_block()->UpdateEnvironment(inner_env);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007696
7697 ZoneList<HValue*>* arguments_values = NULL;
7698
7699 // If the function uses arguments copy current arguments values
7700 // to use them for materialization.
7701 if (function->scope()->arguments() != NULL) {
7702 HEnvironment* arguments_env = inner_env->arguments_environment();
7703 int arguments_count = arguments_env->parameter_count();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007704 arguments_values = new(zone()) ZoneList<HValue*>(arguments_count, zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007705 for (int i = 0; i < arguments_count; i++) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00007706 arguments_values->Add(arguments_env->Lookup(i), zone());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007707 }
7708 }
7709
7710 HEnterInlined* enter_inlined =
7711 new(zone()) HEnterInlined(target,
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007712 arguments_count,
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007713 function,
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007714 function_state()->inlining_kind(),
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007715 function->scope()->arguments(),
yangguo@chromium.org003650e2013-01-24 16:31:08 +00007716 arguments_values,
7717 undefined_receiver);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007718 function_state()->set_entry(enter_inlined);
7719 AddInstruction(enter_inlined);
7720
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007721 // If the function uses arguments object create and bind one.
7722 if (function->scope()->arguments() != NULL) {
7723 ASSERT(function->scope()->arguments()->IsStackAllocated());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007724 inner_env->Bind(function->scope()->arguments(),
7725 graph()->GetArgumentsObject());
yangguo@chromium.org154ff992012-03-13 08:09:54 +00007726 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007727
7728
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007729 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007730 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007731 if (HasStackOverflow()) {
7732 // Bail out if the inline function did, as we cannot residualize a call
7733 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007734 TraceInline(target, caller, "inline graph construction failed");
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007735 target_shared->DisableOptimization("inlining bailed out");
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007736 inline_bailout_ = true;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007737 delete target_state;
danno@chromium.org160a7b02011-04-18 15:51:38 +00007738 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007739 }
7740
7741 // Update inlined nodes count.
7742 inlined_count_ += nodes_added;
7743
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007744 ASSERT(unoptimized_code->kind() == Code::FUNCTION);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007745 Handle<TypeFeedbackInfo> type_info(
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007746 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00007747 graph()->update_type_change_checksum(type_info->own_type_change_checksum());
7748
ager@chromium.orgea91cc52011-05-23 06:06:11 +00007749 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007750
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007751 if (current_block() != NULL) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007752 FunctionState* state = function_state();
7753 if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
7754 // Falling off the end of an inlined construct call. In a test context the
7755 // return value will always evaluate to true, in a value context the
7756 // return value is the newly allocated receiver.
7757 if (call_context()->IsTest()) {
7758 current_block()->Goto(inlined_test_context()->if_true(), state);
7759 } else if (call_context()->IsEffect()) {
7760 current_block()->Goto(function_return(), state);
7761 } else {
7762 ASSERT(call_context()->IsValue());
7763 current_block()->AddLeaveInlined(implicit_return_value, state);
7764 }
7765 } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
7766 // Falling off the end of an inlined setter call. The returned value is
7767 // never used, the value of an assignment is always the value of the RHS
7768 // of the assignment.
7769 if (call_context()->IsTest()) {
7770 inlined_test_context()->ReturnValue(implicit_return_value);
7771 } else if (call_context()->IsEffect()) {
7772 current_block()->Goto(function_return(), state);
7773 } else {
7774 ASSERT(call_context()->IsValue());
7775 current_block()->AddLeaveInlined(implicit_return_value, state);
7776 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007777 } else {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007778 // Falling off the end of a normal inlined function. This basically means
7779 // returning undefined.
7780 if (call_context()->IsTest()) {
7781 current_block()->Goto(inlined_test_context()->if_false(), state);
7782 } else if (call_context()->IsEffect()) {
7783 current_block()->Goto(function_return(), state);
7784 } else {
7785 ASSERT(call_context()->IsValue());
7786 current_block()->AddLeaveInlined(undefined, state);
7787 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007788 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007789 }
7790
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007791 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007792 if (inlined_test_context() != NULL) {
7793 HBasicBlock* if_true = inlined_test_context()->if_true();
7794 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007795
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007796 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007797 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007798 ClearInlinedTestContext();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007799 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007800
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007801 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007802 if (if_true->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00007803 if_true->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007804 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007805 if_true->Goto(true_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007806 }
7807 if (if_false->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00007808 if_false->SetJoinId(ast_id);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007809 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00007810 if_false->Goto(false_target, function_state());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00007811 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007812 set_current_block(NULL);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007813 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007814
danno@chromium.org160a7b02011-04-18 15:51:38 +00007815 } else if (function_return()->HasPredecessor()) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00007816 function_return()->SetJoinId(ast_id);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00007817 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00007818 } else {
7819 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007820 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007821 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007822 return true;
7823}
7824
7825
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007826bool HOptimizedGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00007827 // The function call we are inlining is a method call if the call
7828 // is a property call.
7829 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
7830 ? CALL_AS_FUNCTION
7831 : CALL_AS_METHOD;
7832
7833 return TryInline(call_kind,
7834 expr->target(),
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007835 expr->arguments()->length(),
ulan@chromium.org967e2702012-02-28 09:49:15 +00007836 NULL,
7837 expr->id(),
7838 expr->ReturnId(),
7839 drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
7840}
7841
7842
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007843bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr,
7844 HValue* implicit_return_value) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00007845 return TryInline(CALL_AS_FUNCTION,
7846 expr->target(),
yangguo@chromium.org304cc332012-07-24 07:59:48 +00007847 expr->arguments()->length(),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007848 implicit_return_value,
ulan@chromium.org967e2702012-02-28 09:49:15 +00007849 expr->id(),
7850 expr->ReturnId(),
7851 CONSTRUCT_CALL_RETURN);
7852}
7853
7854
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007855bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter,
7856 Property* prop) {
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007857 return TryInline(CALL_AS_METHOD,
7858 getter,
7859 0,
7860 NULL,
7861 prop->id(),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007862 prop->LoadId(),
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +00007863 GETTER_CALL_RETURN);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00007864}
7865
7866
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007867bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
7868 Assignment* assignment,
7869 HValue* implicit_return_value) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00007870 return TryInline(CALL_AS_METHOD,
7871 setter,
7872 1,
7873 implicit_return_value,
7874 assignment->id(),
7875 assignment->AssignmentId(),
7876 SETTER_CALL_RETURN);
7877}
7878
7879
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00007880bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
7881 Call* expr,
7882 int arguments_count) {
7883 return TryInline(CALL_AS_METHOD,
7884 function,
7885 arguments_count,
7886 NULL,
7887 expr->id(),
7888 expr->ReturnId(),
7889 NORMAL_RETURN);
7890}
7891
7892
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007893bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
7894 bool drop_extra) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007895 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
7896 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
7897 switch (id) {
danno@chromium.org1f34ad32012-11-26 14:53:56 +00007898 case kMathExp:
7899 if (!FLAG_fast_math) break;
7900 // Fall through if FLAG_fast_math.
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007901 case kMathRound:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007902 case kMathFloor:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007903 case kMathAbs:
7904 case kMathSqrt:
7905 case kMathLog:
7906 case kMathSin:
7907 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00007908 case kMathTan:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007909 if (expr->arguments()->length() == 1) {
7910 HValue* argument = Pop();
7911 HValue* context = environment()->LookupContext();
7912 Drop(1); // Receiver.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007913 HInstruction* op =
7914 HUnaryMathOperation::New(zone(), context, argument, id);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00007915 op->set_position(expr->position());
7916 if (drop_extra) Drop(1); // Optionally drop the function.
7917 ast_context()->ReturnInstruction(op, expr->id());
7918 return true;
7919 }
7920 break;
7921 default:
7922 // Not supported for inlining yet.
7923 break;
7924 }
7925 return false;
7926}
7927
7928
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00007929bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
7930 Call* expr,
7931 HValue* receiver,
7932 Handle<Map> receiver_map,
7933 CheckType check_type) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007934 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007935 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007936 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007937 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007938 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
7939 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007940 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007941 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007942 if (argument_count == 2 && check_type == STRING_CHECK) {
7943 HValue* index = Pop();
7944 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007945 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007946 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007947 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007948 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00007949 expr->holder(),
7950 zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007951 HInstruction* char_code =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007952 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007953 if (id == kStringCharCodeAt) {
7954 ast_context()->ReturnInstruction(char_code, expr->id());
7955 return true;
7956 }
7957 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007958 HInstruction* result =
7959 HStringCharFromCode::New(zone(), context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007960 ast_context()->ReturnInstruction(result, expr->id());
7961 return true;
7962 }
7963 break;
danno@chromium.org1f34ad32012-11-26 14:53:56 +00007964 case kMathExp:
7965 if (!FLAG_fast_math) break;
7966 // Fall through if FLAG_fast_math.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007967 case kMathRound:
7968 case kMathFloor:
7969 case kMathAbs:
7970 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007971 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00007972 case kMathSin:
7973 case kMathCos:
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00007974 case kMathTan:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007975 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007976 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007977 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007978 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007979 Drop(1); // Receiver.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00007980 HInstruction* op =
7981 HUnaryMathOperation::New(zone(), context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007982 op->set_position(expr->position());
7983 ast_context()->ReturnInstruction(op, expr->id());
7984 return true;
7985 }
7986 break;
7987 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00007988 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00007989 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007990 HValue* right = Pop();
7991 HValue* left = Pop();
7992 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007993 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00007994 HInstruction* result = NULL;
7995 // Use sqrt() if exponent is 0.5 or -0.5.
7996 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
7997 double exponent = HConstant::cast(right)->DoubleValue();
7998 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00007999 result =
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008000 HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008001 } else if (exponent == -0.5) {
8002 HConstant* double_one =
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00008003 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1),
8004 isolate()),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008005 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008006 AddInstruction(double_one);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008007 HInstruction* sqrt =
8008 HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
8009 AddInstruction(sqrt);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008010 // MathPowHalf doesn't have side effects so there's no need for
8011 // an environment simulation here.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008012 ASSERT(!sqrt->HasObservableSideEffects());
8013 result = HDiv::New(zone(), context, double_one, sqrt);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008014 } else if (exponent == 2.0) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008015 result = HMul::New(zone(), context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008016 }
8017 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008018 HConstant::cast(right)->HasInteger32Value() &&
8019 HConstant::cast(right)->Integer32Value() == 2) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008020 result = HMul::New(zone(), context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008021 }
8022
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008023 if (result == NULL) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008024 result = HPower::New(zone(), left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008025 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008026 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008027 return true;
8028 }
8029 break;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008030 case kMathRandom:
8031 if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008032 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008033 Drop(1); // Receiver.
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008034 HValue* context = environment()->LookupContext();
8035 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
8036 AddInstruction(global_object);
8037 HRandom* result = new(zone()) HRandom(global_object);
8038 ast_context()->ReturnInstruction(result, expr->id());
8039 return true;
8040 }
8041 break;
8042 case kMathMax:
8043 case kMathMin:
8044 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008045 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008046 HValue* right = Pop();
8047 HValue* left = Pop();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008048 Drop(1); // Receiver.
8049 HValue* context = environment()->LookupContext();
8050 HMathMinMax::Operation op = (id == kMathMin) ? HMathMinMax::kMathMin
8051 : HMathMinMax::kMathMax;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008052 HInstruction* result =
8053 HMathMinMax::New(zone(), context, left, right, op);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008054 ast_context()->ReturnInstruction(result, expr->id());
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00008055 return true;
8056 }
8057 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008058 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008059 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008060 break;
8061 }
8062 return false;
8063}
8064
8065
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008066bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008067 Expression* callee = expr->expression();
8068 Property* prop = callee->AsProperty();
8069 ASSERT(prop != NULL);
8070
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008071 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
8072 return false;
8073 }
8074 Handle<Map> function_map = expr->GetReceiverTypes()->first();
8075 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
8076 !expr->target()->shared()->HasBuiltinFunctionId() ||
8077 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
8078 return false;
8079 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008080
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008081 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008082
8083 ZoneList<Expression*>* args = expr->arguments();
8084 if (args->length() != 2) return false;
8085
8086 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00008087 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008088 HValue* arg_two_value = environment()->Lookup(arg_two->var());
8089 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
8090
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008091 // Found pattern f.apply(receiver, arguments).
8092 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00008093 if (HasStackOverflow() || current_block() == NULL) return true;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008094 HValue* function = Top();
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008095 AddCheckConstantFunction(expr->holder(), function, function_map);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00008096 Drop(1);
8097
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008098 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00008099 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008100 HValue* receiver = Pop();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008101
8102 if (function_state()->outer() == NULL) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008103 HInstruction* elements = AddInstruction(
8104 new(zone()) HArgumentsElements(false));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008105 HInstruction* length =
8106 AddInstruction(new(zone()) HArgumentsLength(elements));
8107 HValue* wrapped_receiver =
8108 AddInstruction(new(zone()) HWrapReceiver(receiver, function));
8109 HInstruction* result =
8110 new(zone()) HApplyArguments(function,
8111 wrapped_receiver,
8112 length,
8113 elements);
8114 result->set_position(expr->position());
8115 ast_context()->ReturnInstruction(result, expr->id());
8116 return true;
8117 } else {
8118 // We are inside inlined function and we know exactly what is inside
yangguo@chromium.org39110192013-01-16 09:55:08 +00008119 // arguments object. But we need to be able to materialize at deopt.
8120 // TODO(mstarzinger): For now we just ensure arguments are pushed
8121 // right after HEnterInlined, but we could be smarter about this.
8122 EnsureArgumentsArePushedForAccess();
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008123 ASSERT_EQ(environment()->arguments_environment()->parameter_count(),
8124 function_state()->entry()->arguments_values()->length());
8125 HEnterInlined* entry = function_state()->entry();
8126 ZoneList<HValue*>* arguments_values = entry->arguments_values();
8127 int arguments_count = arguments_values->length();
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008128 PushAndAdd(new(zone()) HWrapReceiver(receiver, function));
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008129 for (int i = 1; i < arguments_count; i++) {
8130 Push(arguments_values->at(i));
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008131 }
8132
8133 Handle<JSFunction> known_function;
8134 if (function->IsConstant()) {
8135 HConstant* constant_function = HConstant::cast(function);
8136 known_function = Handle<JSFunction>::cast(constant_function->handle());
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008137 int args_count = arguments_count - 1; // Excluding receiver.
8138 if (TryInlineApply(known_function, expr, args_count)) return true;
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008139 }
8140
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008141 Drop(arguments_count - 1);
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008142 PushAndAdd(new(zone()) HPushArgument(Pop()));
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008143 for (int i = 1; i < arguments_count; i++) {
8144 PushAndAdd(new(zone()) HPushArgument(arguments_values->at(i)));
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008145 }
8146
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008147 HValue* context = environment()->LookupContext();
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008148 HInvokeFunction* call = new(zone()) HInvokeFunction(
8149 context,
8150 function,
ulan@chromium.org6ba1fd02013-02-14 14:56:11 +00008151 known_function,
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00008152 arguments_count);
8153 Drop(arguments_count);
yangguo@chromium.org154ff992012-03-13 08:09:54 +00008154 call->set_position(expr->position());
8155 ast_context()->ReturnInstruction(call, expr->id());
8156 return true;
8157 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008158}
8159
8160
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008161// Checks if all maps in |types| are from the same family, i.e., are elements
8162// transitions of each other. Returns either NULL if they are not from the same
8163// family, or a Map* indicating the map with the first elements kind of the
8164// family that is in the list.
8165static Map* CheckSameElementsFamily(SmallMapList* types) {
8166 if (types->length() <= 1) return NULL;
8167 // Check if all maps belong to the same transition family.
8168 Map* kinds[kFastElementsKindCount];
8169 Map* first_map = *types->first();
8170 ElementsKind first_kind = first_map->elements_kind();
8171 if (!IsFastElementsKind(first_kind)) return NULL;
8172 int first_index = GetSequenceIndexFromFastElementsKind(first_kind);
8173 int last_index = first_index;
8174
8175 for (int i = 0; i < kFastElementsKindCount; i++) kinds[i] = NULL;
8176
8177 kinds[first_index] = first_map;
8178
8179 for (int i = 1; i < types->length(); ++i) {
8180 Map* map = *types->at(i);
8181 ElementsKind elements_kind = map->elements_kind();
8182 if (!IsFastElementsKind(elements_kind)) return NULL;
8183 int index = GetSequenceIndexFromFastElementsKind(elements_kind);
8184 if (index < first_index) {
8185 first_index = index;
8186 } else if (index > last_index) {
8187 last_index = index;
8188 } else if (kinds[index] != map) {
8189 return NULL;
8190 }
8191 kinds[index] = map;
8192 }
8193
8194 Map* current = kinds[first_index];
8195 for (int i = first_index + 1; i <= last_index; i++) {
8196 Map* next = kinds[i];
8197 if (next != NULL) {
8198 ElementsKind current_kind = next->elements_kind();
8199 if (next != current->LookupElementsTransitionMap(current_kind)) {
8200 return NULL;
8201 }
8202 current = next;
8203 }
8204 }
8205
8206 return kinds[first_index];
8207}
8208
8209
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008210void HOptimizedGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008211 ASSERT(!HasStackOverflow());
8212 ASSERT(current_block() != NULL);
8213 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008214 Expression* callee = expr->expression();
8215 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008216 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008217
8218 Property* prop = callee->AsProperty();
8219 if (prop != NULL) {
8220 if (!prop->key()->IsPropertyName()) {
8221 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008222 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008223
danno@chromium.org160a7b02011-04-18 15:51:38 +00008224 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008225 // Push receiver and key like the non-optimized code generator expects it.
8226 HValue* key = Pop();
8227 HValue* receiver = Pop();
8228 Push(key);
8229 Push(receiver);
8230
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008231 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008232
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008233 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008234 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008235 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008236 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008237 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008238 }
8239
8240 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00008241 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008242
8243 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008244
danno@chromium.org160a7b02011-04-18 15:51:38 +00008245 CHECK_ALIVE(VisitForValue(prop->obj()));
8246 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008247
8248 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00008249 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008250
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008251 bool monomorphic = expr->IsMonomorphic();
8252 Handle<Map> receiver_map;
8253 if (monomorphic) {
8254 receiver_map = (types == NULL || types->is_empty())
8255 ? Handle<Map>::null()
8256 : types->first();
8257 } else {
8258 Map* family_map = CheckSameElementsFamily(types);
8259 if (family_map != NULL) {
8260 receiver_map = Handle<Map>(family_map);
8261 monomorphic = expr->ComputeTarget(receiver_map, name);
8262 }
8263 }
8264
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008265 HValue* receiver =
8266 environment()->ExpressionStackAt(expr->arguments()->length());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008267 if (monomorphic) {
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008268 if (TryInlineBuiltinMethodCall(expr,
8269 receiver,
8270 receiver_map,
8271 expr->check_type())) {
8272 if (FLAG_trace_inlining) {
8273 PrintF("Inlining builtin ");
8274 expr->target()->ShortPrint();
8275 PrintF("\n");
8276 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008277 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008278 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008279
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008280 if (CallStubCompiler::HasCustomCallGenerator(expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008281 expr->check_type() != RECEIVER_MAP_CHECK) {
8282 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008283 // because it is likely to generate better code. Also use the IC
8284 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008285 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008286 call = PreProcessCall(
8287 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008288 } else {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008289 AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008290
ulan@chromium.org967e2702012-02-28 09:49:15 +00008291 if (TryInlineCall(expr)) return;
danno@chromium.org160a7b02011-04-18 15:51:38 +00008292 call = PreProcessCall(
8293 new(zone()) HCallConstantFunction(expr->target(),
8294 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008295 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008296 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00008297 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008298 HandlePolymorphicCallNamed(expr, receiver, types, name);
8299 return;
8300
8301 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008302 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008303 call = PreProcessCall(
8304 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008305 }
8306
8307 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008308 expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008309 VariableProxy* proxy = expr->expression()->AsVariableProxy();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008310 bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008311
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008312 if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00008313 return Bailout("possible direct call to eval");
8314 }
8315
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008316 if (global_call) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008317 Variable* var = proxy->var();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008318 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008319 // If there is a global property cell for the name at compile time and
8320 // access check is not enabled we assume that the function will not change
8321 // and generate optimized code for calling the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00008322 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008323 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
8324 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008325 !info()->global_object()->IsAccessCheckNeeded()) {
8326 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008327 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008328 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008329 if (known_global_function) {
8330 // Push the global object instead of the global receiver because
8331 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008332 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008333 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00008334 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008335 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008336
danno@chromium.org160a7b02011-04-18 15:51:38 +00008337 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008338 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008339 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008340
8341 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008342 HGlobalReceiver* global_receiver =
8343 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008344 // Index of the receiver from the top of the expression stack.
8345 const int receiver_index = argument_count - 1;
8346 AddInstruction(global_receiver);
8347 ASSERT(environment()->ExpressionStackAt(receiver_index)->
8348 IsGlobalObject());
8349 environment()->SetExpressionStackAt(receiver_index, global_receiver);
8350
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008351 if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
8352 if (FLAG_trace_inlining) {
8353 PrintF("Inlining builtin ");
8354 expr->target()->ShortPrint();
8355 PrintF("\n");
8356 }
8357 return;
8358 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00008359 if (TryInlineCall(expr)) return;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008360
8361 if (expr->target().is_identical_to(info()->closure())) {
8362 graph()->MarkRecursive();
8363 }
8364
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008365 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00008366 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008367 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008368 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008369 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
8370 AddInstruction(receiver);
8371 PushAndAdd(new(zone()) HPushArgument(receiver));
8372 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008373
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008374 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
8375 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008376 }
8377
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008378 } else if (expr->IsMonomorphic()) {
8379 // The function is on the stack in the unoptimized code during
8380 // evaluation of the arguments.
8381 CHECK_ALIVE(VisitForValue(expr->expression()));
8382 HValue* function = Top();
8383 HValue* context = environment()->LookupContext();
8384 HGlobalObject* global = new(zone()) HGlobalObject(context);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008385 AddInstruction(global);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008386 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008387 PushAndAdd(receiver);
8388 CHECK_ALIVE(VisitExpressions(expr->arguments()));
8389 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00008390
8391 if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
8392 if (FLAG_trace_inlining) {
8393 PrintF("Inlining builtin ");
8394 expr->target()->ShortPrint();
8395 PrintF("\n");
8396 }
8397 return;
8398 }
8399
ulan@chromium.org967e2702012-02-28 09:49:15 +00008400 if (TryInlineCall(expr, true)) { // Drop function from environment.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008401 return;
8402 } else {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00008403 call = PreProcessCall(
8404 new(zone()) HInvokeFunction(context,
8405 function,
8406 expr->target(),
8407 argument_count));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00008408 Drop(1); // The function.
8409 }
8410
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008411 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00008412 CHECK_ALIVE(VisitForValue(expr->expression()));
8413 HValue* function = Top();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008414 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008415 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00008416 AddInstruction(global_object);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008417 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008418 AddInstruction(receiver);
8419 PushAndAdd(new(zone()) HPushArgument(receiver));
8420 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008421
danno@chromium.orgc612e022011-11-10 11:38:15 +00008422 call = new(zone()) HCallFunction(context, function, argument_count);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00008423 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008424 }
8425 }
8426
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008427 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008428 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008429}
8430
8431
ulan@chromium.org967e2702012-02-28 09:49:15 +00008432// Checks whether allocation using the given constructor can be inlined.
8433static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
8434 return constructor->has_initial_map() &&
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00008435 constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
8436 constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize;
ulan@chromium.org967e2702012-02-28 09:49:15 +00008437}
8438
8439
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008440void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008441 ASSERT(!HasStackOverflow());
8442 ASSERT(current_block() != NULL);
8443 ASSERT(current_block()->HasPredecessor());
ulan@chromium.org967e2702012-02-28 09:49:15 +00008444 expr->RecordTypeFeedback(oracle());
8445 int argument_count = expr->arguments()->length() + 1; // Plus constructor.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008446 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00008447
ulan@chromium.org967e2702012-02-28 09:49:15 +00008448 if (FLAG_inline_construct &&
8449 expr->IsMonomorphic() &&
8450 IsAllocationInlineable(expr->target())) {
8451 // The constructor function is on the stack in the unoptimized code
8452 // during evaluation of the arguments.
8453 CHECK_ALIVE(VisitForValue(expr->expression()));
8454 HValue* function = Top();
8455 CHECK_ALIVE(VisitExpressions(expr->arguments()));
8456 Handle<JSFunction> constructor = expr->target();
fschneider@chromium.org35814e52012-03-01 15:43:35 +00008457 HValue* check = AddInstruction(
8458 new(zone()) HCheckFunction(function, constructor));
8459
8460 // Force completion of inobject slack tracking before generating
8461 // allocation code to finalize instance size.
8462 if (constructor->shared()->IsInobjectSlackTrackingInProgress()) {
8463 constructor->shared()->CompleteInobjectSlackTracking();
8464 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00008465
8466 // Replace the constructor function with a newly allocated receiver.
8467 HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
8468 // Index of the receiver from the top of the expression stack.
8469 const int receiver_index = argument_count - 1;
8470 AddInstruction(receiver);
8471 ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
8472 environment()->SetExpressionStackAt(receiver_index, receiver);
8473
8474 if (TryInlineConstruct(expr, receiver)) return;
8475
8476 // TODO(mstarzinger): For now we remove the previous HAllocateObject and
8477 // add HPushArgument for the arguments in case inlining failed. What we
8478 // actually should do is emit HInvokeFunction on the constructor instead
8479 // of using HCallNew as a fallback.
8480 receiver->DeleteAndReplaceWith(NULL);
fschneider@chromium.org35814e52012-03-01 15:43:35 +00008481 check->DeleteAndReplaceWith(NULL);
ulan@chromium.org967e2702012-02-28 09:49:15 +00008482 environment()->SetExpressionStackAt(receiver_index, function);
8483 HInstruction* call = PreProcessCall(
8484 new(zone()) HCallNew(context, function, argument_count));
8485 call->set_position(expr->position());
8486 return ast_context()->ReturnInstruction(call, expr->id());
8487 } else {
8488 // The constructor function is both an operand to the instruction and an
8489 // argument to the construct call.
verwaest@chromium.org753aee42012-07-17 16:15:42 +00008490 CHECK_ALIVE(VisitArgument(expr->expression()));
8491 HValue* constructor = HPushArgument::cast(Top())->argument();
ulan@chromium.org967e2702012-02-28 09:49:15 +00008492 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00008493 HCallNew* call;
8494 if (FLAG_optimize_constructed_arrays &&
8495 !(expr->target().is_null()) &&
8496 *(expr->target()) == isolate()->global_context()->array_function()) {
8497 Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId());
8498 ASSERT(feedback->IsSmi());
8499 Handle<JSGlobalPropertyCell> cell =
8500 isolate()->factory()->NewJSGlobalPropertyCell(feedback);
8501 AddInstruction(new(zone()) HCheckFunction(constructor,
8502 Handle<JSFunction>(isolate()->global_context()->array_function())));
8503 call = new(zone()) HCallNewArray(context, constructor, argument_count,
8504 cell);
8505 } else {
8506 call = new(zone()) HCallNew(context, constructor, argument_count);
8507 }
ulan@chromium.org967e2702012-02-28 09:49:15 +00008508 Drop(argument_count);
8509 call->set_position(expr->position());
8510 return ast_context()->ReturnInstruction(call, expr->id());
8511 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008512}
8513
8514
8515// Support for generating inlined runtime functions.
8516
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008517// Lookup table for generators for runtime calls that are generated inline.
8518// Elements of the table are member pointers to functions of
8519// HOptimizedGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008520#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008521 &HOptimizedGraphBuilder::Generate##Name,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008522
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008523const HOptimizedGraphBuilder::InlineFunctionGenerator
8524 HOptimizedGraphBuilder::kInlineFunctionGenerators[] = {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008525 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
8526 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
8527};
8528#undef INLINE_FUNCTION_GENERATOR_ADDRESS
8529
8530
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008531void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008532 ASSERT(!HasStackOverflow());
8533 ASSERT(current_block() != NULL);
8534 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008535 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008536 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008537 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008538
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008539 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008540 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008541 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008542 ASSERT(expr->name()->length() > 0);
8543 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008544 // Call to an inline function.
8545 int lookup_index = static_cast<int>(function->function_id) -
8546 static_cast<int>(Runtime::kFirstInlineFunction);
8547 ASSERT(lookup_index >= 0);
8548 ASSERT(static_cast<size_t>(lookup_index) <
8549 ARRAY_SIZE(kInlineFunctionGenerators));
8550 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
8551
8552 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008553 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008554 } else {
8555 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008556 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008557
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008558 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008559 Handle<String> name = expr->name();
8560 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008561 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008562 new(zone()) HCallRuntime(context, name, function, argument_count);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00008563 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008564 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008565 }
8566}
8567
8568
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008569void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008570 ASSERT(!HasStackOverflow());
8571 ASSERT(current_block() != NULL);
8572 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008573 switch (expr->op()) {
8574 case Token::DELETE: return VisitDelete(expr);
8575 case Token::VOID: return VisitVoid(expr);
8576 case Token::TYPEOF: return VisitTypeof(expr);
8577 case Token::ADD: return VisitAdd(expr);
8578 case Token::SUB: return VisitSub(expr);
8579 case Token::BIT_NOT: return VisitBitNot(expr);
8580 case Token::NOT: return VisitNot(expr);
8581 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008582 }
8583}
8584
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008585void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008586 Property* prop = expr->expression()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008587 VariableProxy* proxy = expr->expression()->AsVariableProxy();
8588 if (prop != NULL) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00008589 CHECK_ALIVE(VisitForValue(prop->obj()));
8590 CHECK_ALIVE(VisitForValue(prop->key()));
8591 HValue* key = Pop();
8592 HValue* obj = Pop();
8593 HValue* context = environment()->LookupContext();
8594 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
8595 return ast_context()->ReturnInstruction(instr, expr->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008596 } else if (proxy != NULL) {
8597 Variable* var = proxy->var();
8598 if (var->IsUnallocated()) {
8599 Bailout("delete with global variable");
8600 } else if (var->IsStackAllocated() || var->IsContextSlot()) {
8601 // Result of deleting non-global variables is false. 'this' is not
8602 // really a variable, though we implement it as one. The
8603 // subexpression does not have side effects.
8604 HValue* value = var->is_this()
8605 ? graph()->GetConstantTrue()
8606 : graph()->GetConstantFalse();
8607 return ast_context()->ReturnValue(value);
8608 } else {
8609 Bailout("delete with non-global variable");
8610 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008611 } else {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008612 // Result of deleting non-property, non-variable reference is true.
8613 // Evaluate the subexpression for side effects.
8614 CHECK_ALIVE(VisitForEffect(expr->expression()));
8615 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008616 }
8617}
8618
8619
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008620void HOptimizedGraphBuilder::VisitVoid(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008621 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008622 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008623}
8624
8625
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008626void HOptimizedGraphBuilder::VisitTypeof(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008627 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
8628 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008629 HValue* context = environment()->LookupContext();
8630 HInstruction* instr = new(zone()) HTypeof(context, value);
8631 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008632}
8633
8634
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008635void HOptimizedGraphBuilder::VisitAdd(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008636 CHECK_ALIVE(VisitForValue(expr->expression()));
8637 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008638 HValue* context = environment()->LookupContext();
8639 HInstruction* instr =
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008640 HMul::New(zone(), context, value, graph()->GetConstant1());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008641 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008642}
8643
8644
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008645void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008646 CHECK_ALIVE(VisitForValue(expr->expression()));
8647 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008648 HValue* context = environment()->LookupContext();
8649 HInstruction* instr =
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008650 HMul::New(zone(), context, value, graph()->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008651 TypeInfo info = oracle()->UnaryType(expr);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008652 Representation rep = ToRepresentation(info);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008653 if (info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00008654 AddSoftDeoptimize();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008655 info = TypeInfo::Unknown();
8656 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008657 if (instr->IsBinaryOperation()) {
8658 HBinaryOperation::cast(instr)->set_observed_input_representation(rep, rep);
8659 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008660 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008661}
8662
8663
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008664void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008665 CHECK_ALIVE(VisitForValue(expr->expression()));
8666 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008667 TypeInfo info = oracle()->UnaryType(expr);
8668 if (info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00008669 AddSoftDeoptimize();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00008670 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008671 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008672 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008673}
8674
8675
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008676void HOptimizedGraphBuilder::VisitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008677 if (ast_context()->IsTest()) {
8678 TestContext* context = TestContext::cast(ast_context());
8679 VisitForControl(expr->expression(),
8680 context->if_false(),
8681 context->if_true());
8682 return;
8683 }
8684
8685 if (ast_context()->IsEffect()) {
8686 VisitForEffect(expr->expression());
8687 return;
8688 }
8689
8690 ASSERT(ast_context()->IsValue());
8691 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
8692 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
8693 CHECK_BAILOUT(VisitForControl(expr->expression(),
8694 materialize_false,
8695 materialize_true));
8696
8697 if (materialize_false->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008698 materialize_false->SetJoinId(expr->MaterializeFalseId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008699 set_current_block(materialize_false);
8700 Push(graph()->GetConstantFalse());
8701 } else {
8702 materialize_false = NULL;
8703 }
8704
8705 if (materialize_true->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008706 materialize_true->SetJoinId(expr->MaterializeTrueId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008707 set_current_block(materialize_true);
8708 Push(graph()->GetConstantTrue());
8709 } else {
8710 materialize_true = NULL;
8711 }
8712
8713 HBasicBlock* join =
8714 CreateJoin(materialize_false, materialize_true, expr->id());
8715 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008716 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00008717}
8718
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008719
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008720HInstruction* HOptimizedGraphBuilder::BuildIncrement(
8721 bool returns_original_input,
8722 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008723 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008724 TypeInfo info = oracle()->IncrementType(expr);
8725 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00008726 if (rep.IsTagged()) {
8727 rep = Representation::Integer32();
8728 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008729
8730 if (returns_original_input) {
8731 // We need an explicit HValue representing ToNumber(input). The
8732 // actual HChange instruction we need is (sometimes) added in a later
8733 // phase, so it is not available now to be used as an input to HAdd and
8734 // as the return value.
8735 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
8736 AddInstruction(number_input);
8737 Push(number_input);
8738 }
8739
8740 // The addition has no side effects, so we do not need
8741 // to simulate the expression stack after this instruction.
8742 // Any later failures deopt to the load of the input or earlier.
8743 HConstant* delta = (expr->op() == Token::INC)
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008744 ? graph()->GetConstant1()
8745 : graph()->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008746 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008747 HInstruction* instr = HAdd::New(zone(), context, Top(), delta);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008748 // We can't insert a simulate here, because it would break deoptimization,
8749 // so the HAdd must not have side effects, so we must freeze its
8750 // representation.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008751 instr->AssumeRepresentation(rep);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008752 instr->ClearAllSideEffects();
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008753 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008754 return instr;
8755}
8756
8757
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008758void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00008759 ASSERT(!HasStackOverflow());
8760 ASSERT(current_block() != NULL);
8761 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00008762 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008763 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008764 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008765 if (proxy == NULL && prop == NULL) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008766 return Bailout("invalid lhs in count operation");
8767 }
8768
8769 // Match the full code generator stack by simulating an extra stack
8770 // element for postfix operations in a non-effect context. The return
8771 // value is ToNumber(input).
8772 bool returns_original_input =
8773 expr->is_postfix() && !ast_context()->IsEffect();
8774 HValue* input = NULL; // ToNumber(original_input).
8775 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008776
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008777 if (proxy != NULL) {
8778 Variable* var = proxy->var();
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00008779 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008780 return Bailout("unsupported count operation with const");
8781 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008782 // Argument of the count operation is a variable, not a property.
8783 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00008784 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008785
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008786 after = BuildIncrement(returns_original_input, expr);
8787 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008788 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008789
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008790 switch (var->location()) {
8791 case Variable::UNALLOCATED:
8792 HandleGlobalVariableAssignment(var,
8793 after,
8794 expr->position(),
8795 expr->AssignmentId());
8796 break;
8797
8798 case Variable::PARAMETER:
8799 case Variable::LOCAL:
8800 Bind(var, after);
8801 break;
8802
8803 case Variable::CONTEXT: {
8804 // Bail out if we try to mutate a parameter value in a function
8805 // using the arguments object. We do not (yet) correctly handle the
8806 // arguments property of the function.
8807 if (info()->scope()->arguments() != NULL) {
8808 // Parameters will rewrite to context slots. We have no direct
8809 // way to detect that the variable is a parameter so we use a
8810 // linear search of the parameter list.
8811 int count = info()->scope()->num_parameters();
8812 for (int i = 0; i < count; ++i) {
8813 if (var == info()->scope()->parameter(i)) {
8814 return Bailout("assignment to parameter in arguments object");
8815 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008816 }
8817 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008818
8819 HValue* context = BuildContextChainWalk(var);
yangguo@chromium.org355cfd12012-08-29 15:32:24 +00008820 HStoreContextSlot::Mode mode = IsLexicalVariableMode(var->mode())
ricow@chromium.org7ad65222011-12-19 12:13:11 +00008821 ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008822 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00008823 new(zone()) HStoreContextSlot(context, var->index(), mode, after);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008824 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008825 if (instr->HasObservableSideEffects()) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008826 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00008827 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008828 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008829 }
8830
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00008831 case Variable::LOOKUP:
8832 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008833 }
8834
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008835 } else {
8836 // Argument of the count operation is a property.
8837 ASSERT(prop != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008838 prop->RecordTypeFeedback(oracle(), zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008839
8840 if (prop->key()->IsPropertyName()) {
8841 // Named property.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008842 if (returns_original_input) Push(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008843
danno@chromium.org160a7b02011-04-18 15:51:38 +00008844 CHECK_ALIVE(VisitForValue(prop->obj()));
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00008845 HValue* object = Top();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008846
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00008847 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00008848 Handle<Map> map;
8849 HInstruction* load;
erik.corry@gmail.com88767242012-08-08 14:43:45 +00008850 bool monomorphic = prop->IsMonomorphic();
8851 if (monomorphic) {
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00008852 map = prop->GetReceiverTypes()->first();
erik.corry@gmail.com88767242012-08-08 14:43:45 +00008853 if (map->is_dictionary_map()) monomorphic = false;
8854 }
8855 if (monomorphic) {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008856 Handle<JSFunction> getter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008857 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008858 if (LookupGetter(map, name, &getter, &holder)) {
8859 load = BuildCallGetter(object, map, getter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008860 } else {
8861 load = BuildLoadNamedMonomorphic(object, name, prop, map);
8862 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008863 } else {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00008864 load = BuildLoadNamedGeneric(object, name, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008865 }
8866 PushAndAdd(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008867 if (load->HasObservableSideEffects()) {
8868 AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
8869 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008870
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008871 after = BuildIncrement(returns_original_input, expr);
8872 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008873
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008874 HInstruction* store;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008875 if (!monomorphic || map->is_observed()) {
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00008876 // If we don't know the monomorphic type, do a generic store.
8877 CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, after));
8878 } else {
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008879 Handle<JSFunction> setter;
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008880 Handle<JSObject> holder;
verwaest@chromium.orgde64f722012-08-16 15:44:54 +00008881 if (LookupSetter(map, name, &setter, &holder)) {
8882 store = BuildCallSetter(object, after, map, setter, holder);
ulan@chromium.orgea52b5f2012-07-30 13:05:33 +00008883 } else {
8884 CHECK_ALIVE(store = BuildStoreNamedMonomorphic(object,
8885 name,
8886 after,
8887 map));
8888 }
verwaest@chromium.orgb6d052d2012-07-27 08:03:27 +00008889 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008890 AddInstruction(store);
8891
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008892 // Overwrite the receiver in the bailout environment with the result
8893 // of the operation, and the placeholder with the original value if
8894 // necessary.
8895 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008896 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008897 if (store->HasObservableSideEffects()) {
8898 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
8899 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008900
8901 } else {
8902 // Keyed property.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008903 if (returns_original_input) Push(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008904
danno@chromium.org160a7b02011-04-18 15:51:38 +00008905 CHECK_ALIVE(VisitForValue(prop->obj()));
8906 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008907 HValue* obj = environment()->ExpressionStackAt(1);
8908 HValue* key = environment()->ExpressionStackAt(0);
8909
whesse@chromium.org7b260152011-06-20 15:33:18 +00008910 bool has_side_effects = false;
8911 HValue* load = HandleKeyedElementAccess(
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00008912 obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
whesse@chromium.org7b260152011-06-20 15:33:18 +00008913 false, // is_store
8914 &has_side_effects);
8915 Push(load);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008916 if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008917
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008918 after = BuildIncrement(returns_original_input, expr);
8919 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008920
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008921 expr->RecordTypeFeedback(oracle(), zone());
whesse@chromium.org7b260152011-06-20 15:33:18 +00008922 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
8923 RelocInfo::kNoPosition,
8924 true, // is_store
8925 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008926
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00008927 // Drop the key from the bailout environment. Overwrite the receiver
8928 // with the result of the operation, and the placeholder with the
8929 // original value if necessary.
8930 Drop(1);
8931 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008932 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008933 ASSERT(has_side_effects); // Stores always have side effects.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00008934 AddSimulate(expr->AssignmentId(), REMOVABLE_SIMULATE);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008935 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008936 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00008937
8938 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00008939 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00008940}
8941
8942
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008943HInstruction* HOptimizedGraphBuilder::BuildStringCharCodeAt(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008944 HValue* context,
8945 HValue* string,
8946 HValue* index) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008947 if (string->IsConstant() && index->IsConstant()) {
8948 HConstant* c_string = HConstant::cast(string);
8949 HConstant* c_index = HConstant::cast(index);
8950 if (c_string->HasStringValue() && c_index->HasNumberValue()) {
8951 int32_t i = c_index->NumberValueAsInteger32();
8952 Handle<String> s = c_string->StringValue();
8953 if (i < 0 || i >= s->length()) {
8954 return new(zone()) HConstant(OS::nan_value(), Representation::Double());
8955 }
8956 return new(zone()) HConstant(s->Get(i), Representation::Integer32());
8957 }
8958 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00008959 AddInstruction(new(zone()) HCheckNonSmi(string));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00008960 AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00008961 HInstruction* length = HStringLength::New(zone(), string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008962 AddInstruction(length);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00008963 HInstruction* checked_index = AddBoundsCheck(index, length);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00008964 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00008965}
8966
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008967// Checks if the given shift amounts have form: (sa) and (32 - sa).
8968static bool ShiftAmountsAllowReplaceByRotate(HValue* sa,
8969 HValue* const32_minus_sa) {
8970 if (!const32_minus_sa->IsSub()) return false;
8971 HSub* sub = HSub::cast(const32_minus_sa);
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00008972 if (sa != sub->right()) return false;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008973 HValue* const32 = sub->left();
8974 if (!const32->IsConstant() ||
8975 HConstant::cast(const32)->Integer32Value() != 32) {
8976 return false;
8977 }
8978 return (sub->right() == sa);
8979}
8980
8981
8982// Checks if the left and the right are shift instructions with the oposite
8983// directions that can be replaced by one rotate right instruction or not.
8984// Returns the operand and the shift amount for the rotate instruction in the
8985// former case.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00008986bool HOptimizedGraphBuilder::MatchRotateRight(HValue* left,
8987 HValue* right,
8988 HValue** operand,
8989 HValue** shift_amount) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00008990 HShl* shl;
8991 HShr* shr;
8992 if (left->IsShl() && right->IsShr()) {
8993 shl = HShl::cast(left);
8994 shr = HShr::cast(right);
8995 } else if (left->IsShr() && right->IsShl()) {
8996 shl = HShl::cast(right);
8997 shr = HShr::cast(left);
8998 } else {
8999 return false;
9000 }
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00009001 if (shl->left() != shr->left()) return false;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009002
9003 if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) &&
9004 !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) {
9005 return false;
9006 }
9007 *operand= shr->left();
9008 *shift_amount = shr->right();
9009 return true;
9010}
9011
9012
9013bool CanBeZero(HValue *right) {
9014 if (right->IsConstant()) {
9015 HConstant* right_const = HConstant::cast(right);
9016 if (right_const->HasInteger32Value() &&
9017 (right_const->Integer32Value() & 0x1f) != 0) {
9018 return false;
9019 }
9020 }
9021 return true;
9022}
9023
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009024
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009025HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
9026 BinaryOperation* expr,
9027 HValue* left,
9028 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009029 HValue* context = environment()->LookupContext();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009030 TypeInfo left_info, right_info, result_info, combined_info;
9031 oracle()->BinaryType(expr, &left_info, &right_info, &result_info);
9032 Representation left_rep = ToRepresentation(left_info);
9033 Representation right_rep = ToRepresentation(right_info);
9034 Representation result_rep = ToRepresentation(result_info);
9035 if (left_info.IsUninitialized()) {
9036 // Can't have initialized one but not the other.
9037 ASSERT(right_info.IsUninitialized());
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00009038 AddSoftDeoptimize();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009039 left_info = right_info = TypeInfo::Unknown();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009040 }
9041 HInstruction* instr = NULL;
9042 switch (expr->op()) {
9043 case Token::ADD:
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009044 if (left_info.IsString() && right_info.IsString()) {
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009045 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009046 AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009047 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009048 AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009049 instr = HStringAdd::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009050 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009051 instr = HAdd::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009052 }
9053 break;
9054 case Token::SUB:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009055 instr = HSub::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009056 break;
9057 case Token::MUL:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009058 instr = HMul::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009059 break;
9060 case Token::MOD:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009061 instr = HMod::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009062 break;
9063 case Token::DIV:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009064 instr = HDiv::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009065 break;
9066 case Token::BIT_XOR:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009067 case Token::BIT_AND:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009068 instr = HBitwise::New(zone(), expr->op(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009069 break;
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009070 case Token::BIT_OR: {
9071 HValue* operand, *shift_amount;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009072 if (left_info.IsInteger32() && right_info.IsInteger32() &&
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009073 MatchRotateRight(left, right, &operand, &shift_amount)) {
9074 instr = new(zone()) HRor(context, operand, shift_amount);
9075 } else {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009076 instr = HBitwise::New(zone(), expr->op(), context, left, right);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009077 }
9078 break;
9079 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009080 case Token::SAR:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009081 instr = HSar::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009082 break;
9083 case Token::SHR:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009084 instr = HShr::New(zone(), context, left, right);
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009085 if (FLAG_opt_safe_uint32_operations && instr->IsShr() &&
9086 CanBeZero(right)) {
9087 graph()->RecordUint32Instruction(instr);
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00009088 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009089 break;
9090 case Token::SHL:
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009091 instr = HShl::New(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009092 break;
9093 default:
9094 UNREACHABLE();
9095 }
9096
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009097 if (instr->IsBinaryOperation()) {
9098 HBinaryOperation* binop = HBinaryOperation::cast(instr);
9099 binop->set_observed_input_representation(left_rep, right_rep);
9100 binop->initialize_output_representation(result_rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009101 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009102 return instr;
9103}
9104
9105
9106// Check for the form (%_ClassOf(foo) === 'BarClass').
9107static bool IsClassOfTest(CompareOperation* expr) {
9108 if (expr->op() != Token::EQ_STRICT) return false;
9109 CallRuntime* call = expr->left()->AsCallRuntime();
9110 if (call == NULL) return false;
9111 Literal* literal = expr->right()->AsLiteral();
9112 if (literal == NULL) return false;
9113 if (!literal->handle()->IsString()) return false;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00009114 if (!call->name()->IsOneByteEqualTo(STATIC_ASCII_VECTOR("_ClassOf"))) {
9115 return false;
9116 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009117 ASSERT(call->arguments()->length() == 1);
9118 return true;
9119}
9120
9121
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009122void HOptimizedGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009123 ASSERT(!HasStackOverflow());
9124 ASSERT(current_block() != NULL);
9125 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009126 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00009127 case Token::COMMA:
9128 return VisitComma(expr);
9129 case Token::OR:
9130 case Token::AND:
9131 return VisitLogicalExpression(expr);
9132 default:
9133 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009134 }
9135}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009136
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009137
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009138void HOptimizedGraphBuilder::VisitComma(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009139 CHECK_ALIVE(VisitForEffect(expr->left()));
9140 // Visit the right subexpression in the same AST context as the entire
9141 // expression.
9142 Visit(expr->right());
9143}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009144
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009145
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009146void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00009147 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009148 if (ast_context()->IsTest()) {
9149 TestContext* context = TestContext::cast(ast_context());
9150 // Translate left subexpression.
9151 HBasicBlock* eval_right = graph()->CreateBasicBlock();
9152 if (is_logical_and) {
9153 CHECK_BAILOUT(VisitForControl(expr->left(),
9154 eval_right,
9155 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009156 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009157 CHECK_BAILOUT(VisitForControl(expr->left(),
9158 context->if_true(),
9159 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009160 }
9161
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009162 // Translate right subexpression by visiting it in the same AST
9163 // context as the entire expression.
9164 if (eval_right->HasPredecessor()) {
9165 eval_right->SetJoinId(expr->RightId());
9166 set_current_block(eval_right);
9167 Visit(expr->right());
9168 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009169
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009170 } else if (ast_context()->IsValue()) {
9171 CHECK_ALIVE(VisitForValue(expr->left()));
9172 ASSERT(current_block() != NULL);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009173 HValue* left_value = Top();
9174
9175 if (left_value->IsConstant()) {
9176 HConstant* left_constant = HConstant::cast(left_value);
9177 if ((is_logical_and && left_constant->ToBoolean()) ||
9178 (!is_logical_and && !left_constant->ToBoolean())) {
9179 Drop(1); // left_value.
9180 CHECK_BAILOUT(VisitForValue(expr->right()));
9181 }
9182 return ast_context()->ReturnValue(Pop());
9183 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009184
9185 // We need an extra block to maintain edge-split form.
9186 HBasicBlock* empty_block = graph()->CreateBasicBlock();
9187 HBasicBlock* eval_right = graph()->CreateBasicBlock();
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009188 TypeFeedbackId test_id = expr->left()->test_id();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00009189 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009190 HBranch* test = is_logical_and
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009191 ? new(zone()) HBranch(left_value, eval_right, empty_block, expected)
9192 : new(zone()) HBranch(left_value, empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009193 current_block()->Finish(test);
9194
9195 set_current_block(eval_right);
9196 Drop(1); // Value of the left subexpression.
9197 CHECK_BAILOUT(VisitForValue(expr->right()));
9198
9199 HBasicBlock* join_block =
9200 CreateJoin(empty_block, current_block(), expr->id());
9201 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009202 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009203
9204 } else {
9205 ASSERT(ast_context()->IsEffect());
9206 // In an effect context, we don't need the value of the left subexpression,
9207 // only its control flow and side effects. We need an extra block to
9208 // maintain edge-split form.
9209 HBasicBlock* empty_block = graph()->CreateBasicBlock();
9210 HBasicBlock* right_block = graph()->CreateBasicBlock();
9211 if (is_logical_and) {
9212 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
9213 } else {
9214 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
9215 }
9216
9217 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
9218 // actually two empty blocks (one here and one inserted by
9219 // TestContext::BuildBranch, and that they both have an HSimulate though the
9220 // second one is not a merge node, and that we really have no good AST ID to
9221 // put on that first HSimulate.
9222
9223 if (empty_block->HasPredecessor()) {
9224 empty_block->SetJoinId(expr->id());
9225 } else {
9226 empty_block = NULL;
9227 }
9228
9229 if (right_block->HasPredecessor()) {
9230 right_block->SetJoinId(expr->RightId());
9231 set_current_block(right_block);
9232 CHECK_BAILOUT(VisitForEffect(expr->right()));
9233 right_block = current_block();
9234 } else {
9235 right_block = NULL;
9236 }
9237
9238 HBasicBlock* join_block =
9239 CreateJoin(empty_block, right_block, expr->id());
9240 set_current_block(join_block);
9241 // We did not materialize any value in the predecessor environments,
9242 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009243 }
9244}
9245
9246
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009247void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009248 CHECK_ALIVE(VisitForValue(expr->left()));
9249 CHECK_ALIVE(VisitForValue(expr->right()));
9250 HValue* right = Pop();
9251 HValue* left = Pop();
9252 HInstruction* instr = BuildBinaryOperation(expr, left, right);
9253 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009254 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009255}
9256
9257
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009258Representation HOptimizedGraphBuilder::ToRepresentation(TypeInfo info) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009259 if (info.IsUninitialized()) return Representation::None();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009260 if (info.IsSmi()) return Representation::Integer32();
9261 if (info.IsInteger32()) return Representation::Integer32();
9262 if (info.IsDouble()) return Representation::Double();
9263 if (info.IsNumber()) return Representation::Double();
9264 return Representation::Tagged();
9265}
9266
9267
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009268void HOptimizedGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
9269 HTypeof* typeof_expr,
9270 Handle<String> check) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009271 // Note: The HTypeof itself is removed during canonicalization, if possible.
9272 HValue* value = typeof_expr->value();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009273 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
9274 instr->set_position(expr->position());
9275 return ast_context()->ReturnControl(instr, expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00009276}
9277
9278
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009279static bool MatchLiteralCompareNil(HValue* left,
9280 Token::Value op,
9281 HValue* right,
9282 Handle<Object> nil,
9283 HValue** expr) {
9284 if (left->IsConstant() &&
9285 HConstant::cast(left)->handle().is_identical_to(nil) &&
9286 Token::IsEqualityOp(op)) {
9287 *expr = right;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009288 return true;
9289 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009290 return false;
ager@chromium.org04921a82011-06-27 13:21:41 +00009291}
9292
9293
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009294static bool MatchLiteralCompareTypeof(HValue* left,
9295 Token::Value op,
9296 HValue* right,
9297 HTypeof** typeof_expr,
9298 Handle<String>* check) {
9299 if (left->IsTypeof() &&
9300 Token::IsEqualityOp(op) &&
9301 right->IsConstant() &&
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00009302 HConstant::cast(right)->handle()->IsString()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009303 *typeof_expr = HTypeof::cast(left);
9304 *check = Handle<String>::cast(HConstant::cast(right)->handle());
9305 return true;
9306 }
9307 return false;
9308}
9309
9310
9311static bool IsLiteralCompareTypeof(HValue* left,
9312 Token::Value op,
9313 HValue* right,
9314 HTypeof** typeof_expr,
9315 Handle<String>* check) {
9316 return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) ||
9317 MatchLiteralCompareTypeof(right, op, left, typeof_expr, check);
9318}
9319
9320
9321static bool IsLiteralCompareNil(HValue* left,
9322 Token::Value op,
9323 HValue* right,
9324 Handle<Object> nil,
9325 HValue** expr) {
9326 return MatchLiteralCompareNil(left, op, right, nil, expr) ||
9327 MatchLiteralCompareNil(right, op, left, nil, expr);
9328}
9329
9330
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00009331static bool IsLiteralCompareBool(HValue* left,
9332 Token::Value op,
9333 HValue* right) {
9334 return op == Token::EQ_STRICT &&
9335 ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) ||
9336 (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean()));
9337}
9338
9339
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009340void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009341 ASSERT(!HasStackOverflow());
9342 ASSERT(current_block() != NULL);
9343 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009344 if (IsClassOfTest(expr)) {
9345 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009346 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009347 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009348 HValue* value = Pop();
9349 Literal* literal = expr->right()->AsLiteral();
9350 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009351 HClassOfTestAndBranch* instr =
9352 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009353 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009354 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009355 }
9356
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009357 TypeInfo left_type, right_type, overall_type_info;
9358 oracle()->CompareType(expr, &left_type, &right_type, &overall_type_info);
9359 Representation combined_rep = ToRepresentation(overall_type_info);
9360 Representation left_rep = ToRepresentation(left_type);
9361 Representation right_rep = ToRepresentation(right_type);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009362 // Check if this expression was ever executed according to type feedback.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009363 // Note that for the special typeof/null/undefined cases we get unknown here.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009364 if (overall_type_info.IsUninitialized()) {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00009365 AddSoftDeoptimize();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009366 overall_type_info = left_type = right_type = TypeInfo::Unknown();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00009367 }
9368
danno@chromium.org160a7b02011-04-18 15:51:38 +00009369 CHECK_ALIVE(VisitForValue(expr->left()));
9370 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009371
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009372 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009373 HValue* right = Pop();
9374 HValue* left = Pop();
9375 Token::Value op = expr->op();
9376
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009377 HTypeof* typeof_expr = NULL;
9378 Handle<String> check;
9379 if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) {
9380 return HandleLiteralCompareTypeof(expr, typeof_expr, check);
9381 }
9382 HValue* sub_expr = NULL;
9383 Factory* f = graph()->isolate()->factory();
9384 if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) {
9385 return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
9386 }
9387 if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) {
9388 return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
9389 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +00009390 if (IsLiteralCompareBool(left, op, right)) {
9391 HCompareObjectEqAndBranch* result =
9392 new(zone()) HCompareObjectEqAndBranch(left, right);
9393 result->set_position(expr->position());
9394 return ast_context()->ReturnControl(result, expr->id());
9395 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009396
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009397 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009398 // Check to see if the rhs of the instanceof is a global function not
9399 // residing in new space. If it is we assume that the function will stay the
9400 // same.
9401 Handle<JSFunction> target = Handle<JSFunction>::null();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009402 VariableProxy* proxy = expr->right()->AsVariableProxy();
9403 bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009404 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009405 info()->has_global_object() &&
9406 !info()->global_object()->IsAccessCheckNeeded()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009407 Handle<String> name = proxy->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00009408 Handle<GlobalObject> global(info()->global_object());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00009409 LookupResult lookup(isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009410 global->Lookup(*name, &lookup);
yangguo@chromium.orgde0db002012-06-22 13:44:28 +00009411 if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009412 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
9413 // If the function is in new space we assume it's more likely to
9414 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009415 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009416 target = candidate;
9417 }
9418 }
9419 }
9420
9421 // If the target is not null we have found a known global function that is
9422 // assumed to stay the same for this instanceof.
9423 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009424 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
9425 result->set_position(expr->position());
9426 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009427 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009428 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009429 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009430 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009431 result->set_position(expr->position());
9432 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00009433 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009434 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009435 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009436 result->set_position(expr->position());
9437 return ast_context()->ReturnInstruction(result, expr->id());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009438 } else if (overall_type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009439 switch (op) {
9440 case Token::EQ:
9441 case Token::EQ_STRICT: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00009442 // Can we get away with map check and not instance type check?
9443 Handle<Map> map = oracle()->GetCompareMap(expr);
9444 if (!map.is_null()) {
verwaest@chromium.orge4ee6de2012-11-06 12:13:00 +00009445 AddCheckMapsWithTransitions(left, map);
9446 AddCheckMapsWithTransitions(right, map);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00009447 HCompareObjectEqAndBranch* result =
9448 new(zone()) HCompareObjectEqAndBranch(left, right);
9449 result->set_position(expr->position());
9450 return ast_context()->ReturnControl(result, expr->id());
9451 } else {
9452 AddInstruction(new(zone()) HCheckNonSmi(left));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009453 AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00009454 AddInstruction(new(zone()) HCheckNonSmi(right));
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009455 AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00009456 HCompareObjectEqAndBranch* result =
9457 new(zone()) HCompareObjectEqAndBranch(left, right);
9458 result->set_position(expr->position());
9459 return ast_context()->ReturnControl(result, expr->id());
9460 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009461 }
9462 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00009463 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009464 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009465 } else if (overall_type_info.IsInternalizedString() &&
9466 Token::IsEqualityOp(op)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00009467 AddInstruction(new(zone()) HCheckNonSmi(left));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009468 AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone()));
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00009469 AddInstruction(new(zone()) HCheckNonSmi(right));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009470 AddInstruction(HCheckInstanceType::NewIsInternalizedString(right, zone()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009471 HCompareObjectEqAndBranch* result =
9472 new(zone()) HCompareObjectEqAndBranch(left, right);
9473 result->set_position(expr->position());
9474 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009475 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009476 if (combined_rep.IsTagged() || combined_rep.IsNone()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009477 HCompareGeneric* result =
9478 new(zone()) HCompareGeneric(context, left, right, op);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009479 result->set_observed_input_representation(left_rep, right_rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009480 result->set_position(expr->position());
9481 return ast_context()->ReturnInstruction(result, expr->id());
9482 } else {
9483 HCompareIDAndBranch* result =
9484 new(zone()) HCompareIDAndBranch(left, right, op);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009485 result->set_observed_input_representation(left_rep, right_rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009486 result->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009487 return ast_context()->ReturnControl(result, expr->id());
9488 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009489 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009490}
9491
9492
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009493void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
9494 HValue* value,
9495 NilValue nil) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009496 ASSERT(!HasStackOverflow());
9497 ASSERT(current_block() != NULL);
9498 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009499 EqualityKind kind =
9500 expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
9501 HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
9502 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009503 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009504}
9505
9506
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009507HInstruction* HOptimizedGraphBuilder::BuildThisFunction() {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00009508 // If we share optimized code between different closures, the
9509 // this-function is not a constant, except inside an inlined body.
9510 if (function_state()->outer() != NULL) {
9511 return new(zone()) HConstant(
9512 function_state()->compilation_info()->closure(),
9513 Representation::Tagged());
9514 } else {
9515 return new(zone()) HThisFunction;
9516 }
9517}
9518
9519
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009520void HOptimizedGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009521 ASSERT(!HasStackOverflow());
9522 ASSERT(current_block() != NULL);
9523 ASSERT(current_block()->HasPredecessor());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00009524 HInstruction* instr = BuildThisFunction();
9525 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009526}
9527
9528
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009529void HOptimizedGraphBuilder::VisitDeclarations(
9530 ZoneList<Declaration*>* declarations) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009531 ASSERT(globals_.is_empty());
9532 AstVisitor::VisitDeclarations(declarations);
9533 if (!globals_.is_empty()) {
yangguo@chromium.org56454712012-02-16 15:33:53 +00009534 Handle<FixedArray> array =
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009535 isolate()->factory()->NewFixedArray(globals_.length(), TENURED);
9536 for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i));
yangguo@chromium.org56454712012-02-16 15:33:53 +00009537 int flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
9538 DeclareGlobalsNativeFlag::encode(info()->is_native()) |
9539 DeclareGlobalsLanguageMode::encode(info()->language_mode());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009540 HInstruction* result = new(zone()) HDeclareGlobals(
9541 environment()->LookupContext(), array, flags);
yangguo@chromium.org56454712012-02-16 15:33:53 +00009542 AddInstruction(result);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009543 globals_.Clear();
yangguo@chromium.org56454712012-02-16 15:33:53 +00009544 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009545}
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00009546
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009547
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009548void HOptimizedGraphBuilder::VisitVariableDeclaration(
9549 VariableDeclaration* declaration) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009550 VariableProxy* proxy = declaration->proxy();
9551 VariableMode mode = declaration->mode();
9552 Variable* variable = proxy->var();
9553 bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
9554 switch (variable->location()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009555 case Variable::UNALLOCATED:
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009556 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009557 globals_.Add(variable->binding_needs_init()
9558 ? isolate()->factory()->the_hole_value()
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009559 : isolate()->factory()->undefined_value(), zone());
yangguo@chromium.org56454712012-02-16 15:33:53 +00009560 return;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009561 case Variable::PARAMETER:
9562 case Variable::LOCAL:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009563 if (hole_init) {
9564 HValue* value = graph()->GetConstantHole();
9565 environment()->Bind(variable, value);
9566 }
9567 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009568 case Variable::CONTEXT:
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009569 if (hole_init) {
9570 HValue* value = graph()->GetConstantHole();
9571 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009572 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009573 context, variable->index(), HStoreContextSlot::kNoCheck, value);
9574 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009575 if (store->HasObservableSideEffects()) {
9576 AddSimulate(proxy->id(), REMOVABLE_SIMULATE);
9577 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009578 }
9579 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00009580 case Variable::LOOKUP:
fschneider@chromium.org1805e212011-09-05 10:49:12 +00009581 return Bailout("unsupported lookup slot in declaration");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00009582 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009583}
9584
9585
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009586void HOptimizedGraphBuilder::VisitFunctionDeclaration(
9587 FunctionDeclaration* declaration) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009588 VariableProxy* proxy = declaration->proxy();
9589 Variable* variable = proxy->var();
9590 switch (variable->location()) {
9591 case Variable::UNALLOCATED: {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009592 globals_.Add(variable->name(), zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009593 Handle<SharedFunctionInfo> function =
9594 Compiler::BuildFunctionInfo(declaration->fun(), info()->script());
9595 // Check for stack-overflow exception.
9596 if (function.is_null()) return SetStackOverflow();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009597 globals_.Add(function, zone());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009598 return;
9599 }
9600 case Variable::PARAMETER:
9601 case Variable::LOCAL: {
9602 CHECK_ALIVE(VisitForValue(declaration->fun()));
9603 HValue* value = Pop();
9604 environment()->Bind(variable, value);
9605 break;
9606 }
9607 case Variable::CONTEXT: {
9608 CHECK_ALIVE(VisitForValue(declaration->fun()));
9609 HValue* value = Pop();
9610 HValue* context = environment()->LookupContext();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009611 HStoreContextSlot* store = new(zone()) HStoreContextSlot(
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009612 context, variable->index(), HStoreContextSlot::kNoCheck, value);
9613 AddInstruction(store);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00009614 if (store->HasObservableSideEffects()) {
9615 AddSimulate(proxy->id(), REMOVABLE_SIMULATE);
9616 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009617 break;
9618 }
9619 case Variable::LOOKUP:
9620 return Bailout("unsupported lookup slot in declaration");
9621 }
9622}
9623
9624
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009625void HOptimizedGraphBuilder::VisitModuleDeclaration(
9626 ModuleDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00009627 UNREACHABLE();
9628}
9629
9630
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009631void HOptimizedGraphBuilder::VisitImportDeclaration(
9632 ImportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00009633 UNREACHABLE();
9634}
9635
9636
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009637void HOptimizedGraphBuilder::VisitExportDeclaration(
9638 ExportDeclaration* declaration) {
ulan@chromium.org812308e2012-02-29 15:58:45 +00009639 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009640}
9641
9642
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009643void HOptimizedGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009644 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009645}
9646
9647
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009648void HOptimizedGraphBuilder::VisitModuleVariable(ModuleVariable* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009649 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009650}
9651
9652
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009653void HOptimizedGraphBuilder::VisitModulePath(ModulePath* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009654 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009655}
9656
9657
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009658void HOptimizedGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00009659 UNREACHABLE();
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00009660}
9661
9662
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009663void HOptimizedGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
ulan@chromium.org8e8d8822012-11-23 14:36:46 +00009664 UNREACHABLE();
9665}
9666
9667
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009668// Generators for inline runtime functions.
9669// Support for types.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009670void HOptimizedGraphBuilder::GenerateIsSmi(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009671 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009672 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009673 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009674 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
9675 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009676}
9677
9678
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009679void HOptimizedGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009680 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009681 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009682 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009683 HHasInstanceTypeAndBranch* result =
9684 new(zone()) HHasInstanceTypeAndBranch(value,
9685 FIRST_SPEC_OBJECT_TYPE,
9686 LAST_SPEC_OBJECT_TYPE);
9687 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009688}
9689
9690
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009691void HOptimizedGraphBuilder::GenerateIsSymbol(CallRuntime* call) {
9692 ASSERT(call->arguments()->length() == 1);
9693 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9694 HValue* value = Pop();
9695 HHasInstanceTypeAndBranch* result =
9696 new(zone()) HHasInstanceTypeAndBranch(value, SYMBOL_TYPE);
9697 return ast_context()->ReturnControl(result, call->id());
9698}
9699
9700
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009701void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009702 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009703 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009704 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009705 HHasInstanceTypeAndBranch* result =
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00009706 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009707 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009708}
9709
9710
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009711void HOptimizedGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009712 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009713 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009714 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009715 HHasCachedArrayIndexAndBranch* result =
9716 new(zone()) HHasCachedArrayIndexAndBranch(value);
9717 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009718}
9719
9720
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009721void HOptimizedGraphBuilder::GenerateIsArray(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009722 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009723 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009724 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009725 HHasInstanceTypeAndBranch* result =
9726 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
9727 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009728}
9729
9730
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009731void HOptimizedGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009732 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009733 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009734 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009735 HHasInstanceTypeAndBranch* result =
9736 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
9737 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00009738}
9739
9740
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009741void HOptimizedGraphBuilder::GenerateIsObject(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009742 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009743 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009744 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009745 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
9746 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009747}
9748
9749
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009750void HOptimizedGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009751 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009752}
9753
9754
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009755void HOptimizedGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00009756 ASSERT(call->arguments()->length() == 1);
9757 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9758 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009759 HIsUndetectableAndBranch* result =
9760 new(zone()) HIsUndetectableAndBranch(value);
9761 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009762}
9763
9764
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009765void HOptimizedGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009766 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00009767 return Bailout(
9768 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009769}
9770
9771
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00009772// Support for construct call checks.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009773void HOptimizedGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009774 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009775 if (function_state()->outer() != NULL) {
ulan@chromium.org967e2702012-02-28 09:49:15 +00009776 // We are generating graph for inlined function.
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +00009777 HValue* value = function_state()->inlining_kind() == CONSTRUCT_CALL_RETURN
ulan@chromium.org967e2702012-02-28 09:49:15 +00009778 ? graph()->GetConstantTrue()
9779 : graph()->GetConstantFalse();
9780 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009781 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009782 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
9783 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009784 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009785}
9786
9787
9788// Support for arguments.length and arguments[?].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009789void HOptimizedGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00009790 // Our implementation of arguments (based on this stack frame or an
9791 // adapter below it) does not work for inlined functions. This runtime
9792 // function is blacklisted by AstNode::IsInlineable.
9793 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009794 ASSERT(call->arguments()->length() == 0);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00009795 HInstruction* elements = AddInstruction(
9796 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009797 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009798 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009799}
9800
9801
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009802void HOptimizedGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00009803 // Our implementation of arguments (based on this stack frame or an
9804 // adapter below it) does not work for inlined functions. This runtime
9805 // function is blacklisted by AstNode::IsInlineable.
9806 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009807 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009808 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009809 HValue* index = Pop();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00009810 HInstruction* elements = AddInstruction(
9811 new(zone()) HArgumentsElements(false));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009812 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00009813 HInstruction* checked_index = AddBoundsCheck(index, length);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009814 HAccessArgumentsAt* result =
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +00009815 new(zone()) HAccessArgumentsAt(elements, length, checked_index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009816 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009817}
9818
9819
9820// Support for accessing the class and value fields of an object.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009821void HOptimizedGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009822 // The special form detected by IsClassOfTest is detected before we get here
9823 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00009824 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009825}
9826
9827
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009828void HOptimizedGraphBuilder::GenerateValueOf(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009829 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009830 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009831 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009832 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009833 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009834}
9835
9836
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009837void HOptimizedGraphBuilder::GenerateDateField(CallRuntime* call) {
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00009838 ASSERT(call->arguments()->length() == 2);
9839 ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
9840 Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
9841 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9842 HValue* date = Pop();
9843 HDateField* result = new(zone()) HDateField(date, index);
9844 return ast_context()->ReturnInstruction(result, call->id());
9845}
9846
9847
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009848void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +00009849 CallRuntime* call) {
9850 ASSERT(call->arguments()->length() == 3);
9851 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9852 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
9853 CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
9854 HValue* value = Pop();
9855 HValue* index = Pop();
9856 HValue* string = Pop();
9857 HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
9858 String::ONE_BYTE_ENCODING, string, index, value);
9859 return ast_context()->ReturnInstruction(result, call->id());
9860}
9861
9862
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009863void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +00009864 CallRuntime* call) {
9865 ASSERT(call->arguments()->length() == 3);
9866 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9867 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
9868 CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
9869 HValue* value = Pop();
9870 HValue* index = Pop();
9871 HValue* string = Pop();
9872 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009873 HInstruction* char_code = BuildStringCharCodeAt(context, string, index);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +00009874 AddInstruction(char_code);
9875 HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
9876 String::TWO_BYTE_ENCODING, string, index, value);
9877 return ast_context()->ReturnInstruction(result, call->id());
9878}
9879
9880
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009881void HOptimizedGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org2c456792011-11-11 12:00:53 +00009882 ASSERT(call->arguments()->length() == 2);
9883 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9884 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
9885 HValue* value = Pop();
9886 HValue* object = Pop();
9887 // Check if object is a not a smi.
9888 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object);
9889 HBasicBlock* if_smi = graph()->CreateBasicBlock();
9890 HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
9891 HBasicBlock* join = graph()->CreateBasicBlock();
9892 smicheck->SetSuccessorAt(0, if_smi);
9893 smicheck->SetSuccessorAt(1, if_heap_object);
9894 current_block()->Finish(smicheck);
9895 if_smi->Goto(join);
9896
9897 // Check if object is a JSValue.
9898 set_current_block(if_heap_object);
9899 HHasInstanceTypeAndBranch* typecheck =
9900 new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE);
9901 HBasicBlock* if_js_value = graph()->CreateBasicBlock();
9902 HBasicBlock* not_js_value = graph()->CreateBasicBlock();
9903 typecheck->SetSuccessorAt(0, if_js_value);
9904 typecheck->SetSuccessorAt(1, not_js_value);
9905 current_block()->Finish(typecheck);
9906 not_js_value->Goto(join);
9907
9908 // Create in-object property store to kValueOffset.
9909 set_current_block(if_js_value);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00009910 Handle<String> name = isolate()->factory()->undefined_string();
mmassi@chromium.org7028c052012-06-13 11:51:58 +00009911 AddInstruction(new(zone()) HStoreNamedField(object,
9912 name,
9913 value,
9914 true, // in-object store.
9915 JSValue::kValueOffset));
danno@chromium.org2c456792011-11-11 12:00:53 +00009916 if_js_value->Goto(join);
9917 join->SetJoinId(call->id());
9918 set_current_block(join);
9919 return ast_context()->ReturnValue(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009920}
9921
9922
9923// Fast support for charCodeAt(n).
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009924void HOptimizedGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009925 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009926 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9927 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00009928 HValue* index = Pop();
9929 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009930 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009931 HInstruction* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009932 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009933}
9934
9935
9936// Fast support for string.charAt(n) and string[n].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009937void HOptimizedGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009938 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009939 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009940 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009941 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009942 HInstruction* result = HStringCharFromCode::New(zone(), context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009943 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009944}
9945
9946
9947// Fast support for string.charAt(n) and string[n].
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009948void HOptimizedGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009949 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009950 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9951 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009952 HValue* index = Pop();
9953 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00009954 HValue* context = environment()->LookupContext();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009955 HInstruction* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00009956 AddInstruction(char_code);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00009957 HInstruction* result = HStringCharFromCode::New(zone(), context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009958 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009959}
9960
9961
9962// Fast support for object equality testing.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009963void HOptimizedGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009964 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00009965 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
9966 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009967 HValue* right = Pop();
9968 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009969 HCompareObjectEqAndBranch* result =
9970 new(zone()) HCompareObjectEqAndBranch(left, right);
9971 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009972}
9973
9974
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009975void HOptimizedGraphBuilder::GenerateLog(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009976 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009977 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009978}
9979
9980
9981// Fast support for Math.random().
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009982void HOptimizedGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00009983 HValue* context = environment()->LookupContext();
9984 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
9985 AddInstruction(global_object);
9986 HRandom* result = new(zone()) HRandom(global_object);
9987 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009988}
9989
9990
9991// Fast support for StringAdd.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00009992void HOptimizedGraphBuilder::GenerateStringAdd(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009993 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00009994 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00009995 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00009996 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00009997 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00009998 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00009999}
10000
10001
10002// Fast support for SubString.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010003void HOptimizedGraphBuilder::GenerateSubString(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010004 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010005 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010006 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010007 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010008 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010009 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010010}
10011
10012
10013// Fast support for StringCompare.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010014void HOptimizedGraphBuilder::GenerateStringCompare(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010015 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010016 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010017 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010018 HCallStub* result =
10019 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010020 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010021 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010022}
10023
10024
10025// Support for direct calls from JavaScript to native RegExp code.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010026void HOptimizedGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010027 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010028 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010029 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010030 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010031 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010032 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010033}
10034
10035
10036// Construct a RegExp exec result with two in-object properties.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010037void HOptimizedGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010038 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010039 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010040 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010041 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010042 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010043 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010044 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010045}
10046
10047
10048// Support for fast native caches.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010049void HOptimizedGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010050 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010051}
10052
10053
10054// Fast support for number to string.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010055void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010056 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010057 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010058 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010059 HCallStub* result =
10060 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010061 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010062 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010063}
10064
10065
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010066// Fast call for custom callbacks.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010067void HOptimizedGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010068 // 1 ~ The function to call is not itself an argument to the call.
10069 int arg_count = call->arguments()->length() - 1;
10070 ASSERT(arg_count >= 1); // There's always at least a receiver.
10071
10072 for (int i = 0; i < arg_count; ++i) {
10073 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
10074 }
10075 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
danno@chromium.orgc612e022011-11-10 11:38:15 +000010076
danno@chromium.org160a7b02011-04-18 15:51:38 +000010077 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010078 HValue* context = environment()->LookupContext();
danno@chromium.orgc612e022011-11-10 11:38:15 +000010079
10080 // Branch for function proxies, or other non-functions.
10081 HHasInstanceTypeAndBranch* typecheck =
10082 new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
10083 HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
10084 HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
10085 HBasicBlock* join = graph()->CreateBasicBlock();
10086 typecheck->SetSuccessorAt(0, if_jsfunction);
10087 typecheck->SetSuccessorAt(1, if_nonfunction);
10088 current_block()->Finish(typecheck);
10089
10090 set_current_block(if_jsfunction);
10091 HInstruction* invoke_result = AddInstruction(
10092 new(zone()) HInvokeFunction(context, function, arg_count));
danno@chromium.org160a7b02011-04-18 15:51:38 +000010093 Drop(arg_count);
danno@chromium.orgc612e022011-11-10 11:38:15 +000010094 Push(invoke_result);
10095 if_jsfunction->Goto(join);
10096
10097 set_current_block(if_nonfunction);
10098 HInstruction* call_result = AddInstruction(
10099 new(zone()) HCallFunction(context, function, arg_count));
10100 Drop(arg_count);
10101 Push(call_result);
10102 if_nonfunction->Goto(join);
10103
10104 set_current_block(join);
10105 join->SetJoinId(call->id());
10106 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010107}
10108
10109
10110// Fast call to math functions.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010111void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010112 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010113 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
10114 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010115 HValue* right = Pop();
10116 HValue* left = Pop();
ulan@chromium.org2e04b582013-02-21 14:06:02 +000010117 HInstruction* result = HPower::New(zone(), left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010118 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010119}
10120
10121
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010122void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010123 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010124 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010125 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010126 HCallStub* result =
10127 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010128 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010129 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010130 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010131}
10132
10133
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010134void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010135 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010136 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010137 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010138 HCallStub* result =
10139 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010140 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010141 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010142 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010143}
10144
10145
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010146void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000010147 ASSERT_EQ(1, call->arguments()->length());
10148 CHECK_ALIVE(VisitArgumentList(call->arguments()));
10149 HValue* context = environment()->LookupContext();
10150 HCallStub* result =
10151 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
10152 result->set_transcendental_type(TranscendentalCache::TAN);
10153 Drop(1);
10154 return ast_context()->ReturnInstruction(result, call->id());
10155}
10156
10157
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010158void HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010159 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +000010160 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010161 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010162 HCallStub* result =
10163 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000010164 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010165 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010166 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010167}
10168
10169
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010170void HOptimizedGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010171 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010172}
10173
10174
10175// Check whether two RegExps are equivalent
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010176void HOptimizedGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010177 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010178}
10179
10180
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010181void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010182 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +000010183 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +000010184 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010185 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +000010186 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010187}
10188
10189
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010190void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +000010191 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010192}
10193
10194
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010195#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +000010196#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010197
10198
10199HEnvironment::HEnvironment(HEnvironment* outer,
10200 Scope* scope,
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010201 Handle<JSFunction> closure,
10202 Zone* zone)
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010203 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010204 values_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000010205 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010206 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010207 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010208 local_count_(0),
10209 outer_(outer),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000010210 entry_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010211 pop_count_(0),
10212 push_count_(0),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010213 ast_id_(BailoutId::None()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010214 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010215 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
10216}
10217
10218
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000010219HEnvironment::HEnvironment(Zone* zone, int parameter_count)
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010220 : values_(0, zone),
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010221 frame_type_(STUB),
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000010222 parameter_count_(parameter_count),
10223 specials_count_(1),
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010224 local_count_(0),
10225 outer_(NULL),
10226 entry_(NULL),
10227 pop_count_(0),
10228 push_count_(0),
10229 ast_id_(BailoutId::None()),
10230 zone_(zone) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +000010231 Initialize(parameter_count, 0, 0);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010232}
10233
10234
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010235HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone)
10236 : values_(0, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000010237 frame_type_(JS_FUNCTION),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010238 parameter_count_(0),
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000010239 specials_count_(0),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010240 local_count_(0),
10241 outer_(NULL),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000010242 entry_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010243 pop_count_(0),
10244 push_count_(0),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010245 ast_id_(other->ast_id()),
10246 zone_(zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010247 Initialize(other);
10248}
10249
10250
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010251HEnvironment::HEnvironment(HEnvironment* outer,
10252 Handle<JSFunction> closure,
ulan@chromium.org967e2702012-02-28 09:49:15 +000010253 FrameType frame_type,
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010254 int arguments,
10255 Zone* zone)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010256 : closure_(closure),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010257 values_(arguments, zone),
ulan@chromium.org967e2702012-02-28 09:49:15 +000010258 frame_type_(frame_type),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010259 parameter_count_(arguments),
10260 local_count_(0),
10261 outer_(outer),
ulan@chromium.org56c14af2012-09-20 12:51:09 +000010262 entry_(NULL),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010263 pop_count_(0),
10264 push_count_(0),
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010265 ast_id_(BailoutId::None()),
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010266 zone_(zone) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010267}
10268
10269
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010270void HEnvironment::Initialize(int parameter_count,
10271 int local_count,
10272 int stack_height) {
10273 parameter_count_ = parameter_count;
10274 local_count_ = local_count;
10275
10276 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010277 int total = parameter_count + specials_count_ + local_count + stack_height;
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010278 values_.Initialize(total + 4, zone());
10279 for (int i = 0; i < total; ++i) values_.Add(NULL, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010280}
10281
10282
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010283void HEnvironment::Initialize(const HEnvironment* other) {
10284 closure_ = other->closure();
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010285 values_.AddAll(other->values_, zone());
jkummerow@chromium.org59297c72013-01-09 16:32:23 +000010286 assigned_variables_.Union(other->assigned_variables_, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +000010287 frame_type_ = other->frame_type_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010288 parameter_count_ = other->parameter_count_;
10289 local_count_ = other->local_count_;
10290 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
ulan@chromium.org56c14af2012-09-20 12:51:09 +000010291 entry_ = other->entry_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010292 pop_count_ = other->pop_count_;
10293 push_count_ = other->push_count_;
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000010294 specials_count_ = other->specials_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010295 ast_id_ = other->ast_id_;
10296}
10297
10298
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010299void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
10300 ASSERT(!block->IsLoopHeader());
10301 ASSERT(values_.length() == other->values_.length());
10302
10303 int length = values_.length();
10304 for (int i = 0; i < length; ++i) {
10305 HValue* value = values_[i];
10306 if (value != NULL && value->IsPhi() && value->block() == block) {
10307 // There is already a phi for the i'th value.
10308 HPhi* phi = HPhi::cast(value);
10309 // Assert index is correct and that we haven't missed an incoming edge.
10310 ASSERT(phi->merged_index() == i);
10311 ASSERT(phi->OperandCount() == block->predecessors()->length());
10312 phi->AddInput(other->values_[i]);
10313 } else if (values_[i] != other->values_[i]) {
10314 // There is a fresh value on the incoming edge, a phi is needed.
10315 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010316 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010317 HValue* old_value = values_[i];
10318 for (int j = 0; j < block->predecessors()->length(); j++) {
10319 phi->AddInput(old_value);
10320 }
10321 phi->AddInput(other->values_[i]);
10322 this->values_[i] = phi;
10323 block->AddPhi(phi);
10324 }
10325 }
10326}
10327
10328
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010329void HEnvironment::Bind(int index, HValue* value) {
10330 ASSERT(value != NULL);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +000010331 assigned_variables_.Add(index, zone());
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010332 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010333}
10334
10335
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010336bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010337 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010338}
10339
10340
10341bool HEnvironment::ExpressionStackIsEmpty() const {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +000010342 ASSERT(length() >= first_expression_index());
10343 return length() == first_expression_index();
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010344}
10345
10346
10347void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
10348 int count = index_from_top + 1;
10349 int index = values_.length() - count;
10350 ASSERT(HasExpressionAt(index));
10351 // The push count must include at least the element in question or else
10352 // the new value will not be included in this environment's history.
10353 if (push_count_ < count) {
10354 // This is the same effect as popping then re-pushing 'count' elements.
10355 pop_count_ += (count - push_count_);
10356 push_count_ = count;
10357 }
10358 values_[index] = value;
10359}
10360
10361
10362void HEnvironment::Drop(int count) {
10363 for (int i = 0; i < count; ++i) {
10364 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010365 }
10366}
10367
10368
10369HEnvironment* HEnvironment::Copy() const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010370 return new(zone()) HEnvironment(this, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010371}
10372
10373
10374HEnvironment* HEnvironment::CopyWithoutHistory() const {
10375 HEnvironment* result = Copy();
10376 result->ClearHistory();
10377 return result;
10378}
10379
10380
10381HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
10382 HEnvironment* new_env = Copy();
10383 for (int i = 0; i < values_.length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010384 HPhi* phi = new(zone()) HPhi(i, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010385 phi->AddInput(values_[i]);
10386 new_env->values_[i] = phi;
10387 loop_header->AddPhi(phi);
10388 }
10389 new_env->ClearHistory();
10390 return new_env;
10391}
10392
10393
ulan@chromium.org967e2702012-02-28 09:49:15 +000010394HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
10395 Handle<JSFunction> target,
10396 FrameType frame_type,
10397 int arguments) const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010398 HEnvironment* new_env =
10399 new(zone()) HEnvironment(outer, target, frame_type,
10400 arguments + 1, zone());
ulan@chromium.org967e2702012-02-28 09:49:15 +000010401 for (int i = 0; i <= arguments; ++i) { // Include receiver.
10402 new_env->Push(ExpressionStackAt(arguments - i));
10403 }
10404 new_env->ClearHistory();
10405 return new_env;
10406}
10407
10408
danno@chromium.org40cb8782011-05-25 07:58:50 +000010409HEnvironment* HEnvironment::CopyForInlining(
10410 Handle<JSFunction> target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010411 int arguments,
danno@chromium.org40cb8782011-05-25 07:58:50 +000010412 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +000010413 HConstant* undefined,
yangguo@chromium.org003650e2013-01-24 16:31:08 +000010414 InliningKind inlining_kind,
10415 bool undefined_receiver) const {
ulan@chromium.org967e2702012-02-28 09:49:15 +000010416 ASSERT(frame_type() == JS_FUNCTION);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010417
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010418 // Outer environment is a copy of this one without the arguments.
10419 int arity = function->scope()->num_parameters();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010420
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010421 HEnvironment* outer = Copy();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010422 outer->Drop(arguments + 1); // Including receiver.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010423 outer->ClearHistory();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010424
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010425 if (inlining_kind == CONSTRUCT_CALL_RETURN) {
ulan@chromium.org967e2702012-02-28 09:49:15 +000010426 // Create artificial constructor stub environment. The receiver should
10427 // actually be the constructor function, but we pass the newly allocated
10428 // object instead, DoComputeConstructStubFrame() relies on that.
10429 outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +000010430 } else if (inlining_kind == GETTER_CALL_RETURN) {
10431 // We need an additional StackFrame::INTERNAL frame for restoring the
10432 // correct context.
10433 outer = CreateStubEnvironment(outer, target, JS_GETTER, arguments);
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010434 } else if (inlining_kind == SETTER_CALL_RETURN) {
10435 // We need an additional StackFrame::INTERNAL frame for temporarily saving
10436 // the argument of the setter, see StoreStubCompiler::CompileStoreViaSetter.
10437 outer = CreateStubEnvironment(outer, target, JS_SETTER, arguments);
ulan@chromium.org967e2702012-02-28 09:49:15 +000010438 }
10439
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010440 if (arity != arguments) {
10441 // Create artificial arguments adaptation environment.
ulan@chromium.org967e2702012-02-28 09:49:15 +000010442 outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010443 }
10444
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000010445 HEnvironment* inner =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +000010446 new(zone()) HEnvironment(outer, function->scope(), target, zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010447 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +000010448 for (int i = 0; i <= arity; ++i) { // Include receiver.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010449 HValue* push = (i <= arguments) ?
10450 ExpressionStackAt(arguments - i) : undefined;
vegorov@chromium.org3cf47312011-06-29 13:20:01 +000010451 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010452 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000010453 // If the function we are inlining is a strict mode function or a
10454 // builtin function, pass undefined as the receiver for function
10455 // calls (instead of the global receiver).
yangguo@chromium.org003650e2013-01-24 16:31:08 +000010456 if (undefined_receiver) {
danno@chromium.org40cb8782011-05-25 07:58:50 +000010457 inner->SetValueAt(0, undefined);
10458 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010459 inner->SetValueAt(arity + 1, LookupContext());
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010460 for (int i = arity + 2; i < inner->length(); ++i) {
10461 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010462 }
10463
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +000010464 inner->set_ast_id(BailoutId::FunctionEntry());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010465 return inner;
10466}
10467
10468
10469void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +000010470 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010471 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010472 if (i == parameter_count()) stream->Add("specials\n");
10473 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
10474 if (i == parameter_count() + specials_count() + local_count()) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010475 stream->Add("expressions\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010476 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010477 HValue* val = values_.at(i);
10478 stream->Add("%d: ", i);
10479 if (val != NULL) {
10480 val->PrintNameTo(stream);
10481 } else {
10482 stream->Add("NULL");
10483 }
10484 stream->Add("\n");
10485 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +000010486 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010487}
10488
10489
10490void HEnvironment::PrintToStd() {
10491 HeapStringAllocator string_allocator;
10492 StringStream trace(&string_allocator);
10493 PrintTo(&trace);
10494 PrintF("%s", *trace.ToCString());
10495}
10496
10497
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010498void HTracer::TraceCompilation(CompilationInfo* info) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010499 Tag tag(this, "compilation");
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000010500 if (info->IsOptimizing()) {
10501 Handle<String> name = info->function()->debug_name();
10502 PrintStringProperty("name", *name->ToCString());
10503 PrintStringProperty("method", *name->ToCString());
10504 } else {
10505 CodeStub::Major major_key = info->code_stub()->MajorKey();
10506 PrintStringProperty("name", CodeStub::MajorName(major_key, false));
10507 PrintStringProperty("method", "stub");
10508 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010509 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
10510}
10511
10512
jkummerow@chromium.org28583c92012-07-16 11:31:55 +000010513void HTracer::TraceLithium(const char* name, LChunk* chunk) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000010514 AllowHandleDereference allow_handle_deref(chunk->graph()->isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010515 Trace(name, chunk->graph(), chunk);
10516}
10517
10518
10519void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000010520 AllowHandleDereference allow_handle_deref(graph->isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010521 Trace(name, graph, NULL);
10522}
10523
10524
jkummerow@chromium.org28583c92012-07-16 11:31:55 +000010525void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010526 Tag tag(this, "cfg");
10527 PrintStringProperty("name", name);
10528 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
10529 for (int i = 0; i < blocks->length(); i++) {
10530 HBasicBlock* current = blocks->at(i);
10531 Tag block_tag(this, "block");
10532 PrintBlockProperty("name", current->block_id());
10533 PrintIntProperty("from_bci", -1);
10534 PrintIntProperty("to_bci", -1);
10535
10536 if (!current->predecessors()->is_empty()) {
10537 PrintIndent();
10538 trace_.Add("predecessors");
10539 for (int j = 0; j < current->predecessors()->length(); ++j) {
10540 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
10541 }
10542 trace_.Add("\n");
10543 } else {
10544 PrintEmptyProperty("predecessors");
10545 }
10546
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000010547 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010548 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000010549 } else {
10550 PrintIndent();
10551 trace_.Add("successors");
10552 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
10553 trace_.Add(" \"B%d\"", it.Current()->block_id());
10554 }
10555 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010556 }
10557
10558 PrintEmptyProperty("xhandlers");
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +000010559 const char* flags = current->IsLoopSuccessorDominator()
10560 ? "dom-loop-succ"
10561 : "";
10562 PrintStringProperty("flags", flags);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010563
10564 if (current->dominator() != NULL) {
10565 PrintBlockProperty("dominator", current->dominator()->block_id());
10566 }
10567
kmillikin@chromium.org83e16822011-09-13 08:21:47 +000010568 PrintIntProperty("loop_depth", current->LoopNestingDepth());
10569
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010570 if (chunk != NULL) {
10571 int first_index = current->first_instruction_index();
10572 int last_index = current->last_instruction_index();
10573 PrintIntProperty(
10574 "first_lir_id",
10575 LifetimePosition::FromInstructionIndex(first_index).Value());
10576 PrintIntProperty(
10577 "last_lir_id",
10578 LifetimePosition::FromInstructionIndex(last_index).Value());
10579 }
10580
10581 {
10582 Tag states_tag(this, "states");
10583 Tag locals_tag(this, "locals");
10584 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010585 PrintIntProperty("size", current->phis()->length());
10586 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010587 for (int j = 0; j < total; ++j) {
10588 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010589 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010590 trace_.Add("%d ", phi->merged_index());
10591 phi->PrintNameTo(&trace_);
10592 trace_.Add(" ");
10593 phi->PrintTo(&trace_);
10594 trace_.Add("\n");
10595 }
10596 }
10597
10598 {
10599 Tag HIR_tag(this, "HIR");
10600 HInstruction* instruction = current->first();
10601 while (instruction != NULL) {
10602 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +000010603 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010604 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010605 trace_.Add("%d %d ", bci, uses);
10606 instruction->PrintNameTo(&trace_);
10607 trace_.Add(" ");
10608 instruction->PrintTo(&trace_);
10609 trace_.Add(" <|@\n");
10610 instruction = instruction->next();
10611 }
10612 }
10613
10614
10615 if (chunk != NULL) {
10616 Tag LIR_tag(this, "LIR");
10617 int first_index = current->first_instruction_index();
10618 int last_index = current->last_instruction_index();
10619 if (first_index != -1 && last_index != -1) {
10620 const ZoneList<LInstruction*>* instructions = chunk->instructions();
10621 for (int i = first_index; i <= last_index; ++i) {
10622 LInstruction* linstr = instructions->at(i);
10623 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010624 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010625 trace_.Add("%d ",
10626 LifetimePosition::FromInstructionIndex(i).Value());
10627 linstr->PrintTo(&trace_);
10628 trace_.Add(" <|@\n");
10629 }
10630 }
10631 }
10632 }
10633 }
10634}
10635
10636
10637void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
10638 Tag tag(this, "intervals");
10639 PrintStringProperty("name", name);
10640
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010641 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010642 for (int i = 0; i < fixed_d->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010643 TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010644 }
10645
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010646 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010647 for (int i = 0; i < fixed->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010648 TraceLiveRange(fixed->at(i), "fixed", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010649 }
10650
10651 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
10652 for (int i = 0; i < live_ranges->length(); ++i) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010653 TraceLiveRange(live_ranges->at(i), "object", allocator->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010654 }
10655}
10656
10657
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010658void HTracer::TraceLiveRange(LiveRange* range, const char* type,
10659 Zone* zone) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010660 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +000010661 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010662 trace_.Add("%d %s", range->id(), type);
10663 if (range->HasRegisterAssigned()) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +000010664 LOperand* op = range->CreateAssignedOperand(zone);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010665 int assigned_reg = op->index();
10666 if (op->IsDoubleRegister()) {
10667 trace_.Add(" \"%s\"",
10668 DoubleRegister::AllocationIndexToString(assigned_reg));
10669 } else {
10670 ASSERT(op->IsRegister());
10671 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
10672 }
10673 } else if (range->IsSpilled()) {
10674 LOperand* op = range->TopLevel()->GetSpillOperand();
10675 if (op->IsDoubleStackSlot()) {
10676 trace_.Add(" \"double_stack:%d\"", op->index());
10677 } else {
10678 ASSERT(op->IsStackSlot());
10679 trace_.Add(" \"stack:%d\"", op->index());
10680 }
10681 }
10682 int parent_index = -1;
10683 if (range->IsChild()) {
10684 parent_index = range->parent()->id();
10685 } else {
10686 parent_index = range->id();
10687 }
10688 LOperand* op = range->FirstHint();
10689 int hint_index = -1;
danno@chromium.orgfa458e42012-02-01 10:48:36 +000010690 if (op != NULL && op->IsUnallocated()) {
10691 hint_index = LUnallocated::cast(op)->virtual_register();
10692 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010693 trace_.Add(" %d %d", parent_index, hint_index);
10694 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010695 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010696 trace_.Add(" [%d, %d[",
10697 cur_interval->start().Value(),
10698 cur_interval->end().Value());
10699 cur_interval = cur_interval->next();
10700 }
10701
10702 UsePosition* current_pos = range->first_pos();
10703 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000010704 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010705 trace_.Add(" %d M", current_pos->pos().Value());
10706 }
10707 current_pos = current_pos->next();
10708 }
10709
10710 trace_.Add(" \"\"\n");
10711 }
10712}
10713
10714
10715void HTracer::FlushToFile() {
10716 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
10717 trace_.Reset();
10718}
10719
10720
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010721void HStatistics::Initialize(CompilationInfo* info) {
yangguo@chromium.org003650e2013-01-24 16:31:08 +000010722 if (info->shared_info().is_null()) return;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010723 source_size_ += info->shared_info()->SourceSize();
10724}
10725
10726
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010727void HStatistics::Print() {
10728 PrintF("Timing results:\n");
10729 int64_t sum = 0;
10730 for (int i = 0; i < timing_.length(); ++i) {
10731 sum += timing_[i];
10732 }
10733
10734 for (int i = 0; i < names_.length(); ++i) {
10735 PrintF("%30s", names_[i]);
10736 double ms = static_cast<double>(timing_[i]) / 1000;
10737 double percent = static_cast<double>(timing_[i]) * 100 / sum;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010738 PrintF(" - %8.3f ms / %4.1f %% ", ms, percent);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010739
10740 unsigned size = sizes_[i];
10741 double size_percent = static_cast<double>(size) * 100 / total_size_;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010742 PrintF(" %9u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010743 }
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010744
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010745 PrintF("----------------------------------------"
10746 "---------------------------------------\n");
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010747 int64_t total = create_graph_ + optimize_graph_ + generate_code_;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010748 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010749 "Create graph",
10750 static_cast<double>(create_graph_) / 1000,
10751 static_cast<double>(create_graph_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010752 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010753 "Optimize graph",
10754 static_cast<double>(optimize_graph_) / 1000,
10755 static_cast<double>(optimize_graph_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010756 PrintF("%30s - %8.3f ms / %4.1f %% \n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010757 "Generate and install code",
10758 static_cast<double>(generate_code_) / 1000,
10759 static_cast<double>(generate_code_) * 100 / total);
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010760 PrintF("----------------------------------------"
10761 "---------------------------------------\n");
10762 PrintF("%30s - %8.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010763 "Total",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010764 static_cast<double>(total) / 1000,
10765 static_cast<double>(total) / full_code_gen_);
10766
10767 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
10768 double normalized_time = source_size_in_kb > 0
10769 ? (static_cast<double>(total) / 1000) / source_size_in_kb
10770 : 0;
10771 double normalized_size_in_kb = source_size_in_kb > 0
10772 ? total_size_ / 1024 / source_size_in_kb
10773 : 0;
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010774 PrintF("%30s - %8.3f ms %7.3f kB allocated\n",
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000010775 "Average per kB source",
10776 normalized_time, normalized_size_in_kb);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010777}
10778
10779
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010780void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010781 if (name == HPhase::kFullCodeGen) {
10782 full_code_gen_ += ticks;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010783 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010784 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010785 for (int i = 0; i < names_.length(); ++i) {
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +000010786 if (strcmp(names_[i], name) == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010787 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010788 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010789 return;
10790 }
10791 }
10792 names_.Add(name);
10793 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010794 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010795 }
10796}
10797
10798
10799const char* const HPhase::kFullCodeGen = "Full code generator";
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010800
10801void HPhase::Begin(const char* name,
10802 HGraph* graph,
jkummerow@chromium.org28583c92012-07-16 11:31:55 +000010803 LChunk* chunk,
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010804 LAllocator* allocator) {
10805 name_ = name;
10806 graph_ = graph;
10807 chunk_ = chunk;
10808 allocator_ = allocator;
10809 if (allocator != NULL && chunk_ == NULL) {
10810 chunk_ = allocator->chunk();
10811 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010812 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010813 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010814}
10815
10816
10817void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000010818 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010819 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000010820 unsigned size = Zone::allocation_size_ - start_allocation_size_;
10821 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010822 }
10823
fschneider@chromium.org35814e52012-03-01 15:43:35 +000010824 // Produce trace output if flag is set so that the first letter of the
10825 // phase name matches the command line parameter FLAG_trace_phase.
10826 if (FLAG_trace_hydrogen &&
10827 OS::StrChr(const_cast<char*>(FLAG_trace_phase), name_[0]) != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010828 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
10829 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
10830 if (allocator_ != NULL) {
10831 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
10832 }
10833 }
10834
10835#ifdef DEBUG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000010836 if (graph_ != NULL) graph_->Verify(false); // No full verify.
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010837 if (allocator_ != NULL) allocator_->Verify();
10838#endif
10839}
10840
10841} } // namespace v8::internal