blob: fdfadfaba77fe6bb87e12988979a7ee6709eba9b [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),
58 phis_(4),
59 first_(NULL),
60 last_(NULL),
61 end_(NULL),
62 loop_information_(NULL),
63 predecessors_(2),
64 dominator_(NULL),
65 dominated_blocks_(4),
66 last_environment_(NULL),
67 argument_count_(-1),
68 first_instruction_index_(-1),
69 last_instruction_index_(-1),
70 deleted_phis_(4),
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),
73 is_deoptimizing_(false) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000074
75
76void HBasicBlock::AttachLoopInformation() {
77 ASSERT(!IsLoopHeader());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000078 loop_information_ = new(zone()) HLoopInformation(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000079}
80
81
82void HBasicBlock::DetachLoopInformation() {
83 ASSERT(IsLoopHeader());
84 loop_information_ = NULL;
85}
86
87
88void HBasicBlock::AddPhi(HPhi* phi) {
89 ASSERT(!IsStartBlock());
90 phis_.Add(phi);
91 phi->SetBlock(this);
92}
93
94
95void HBasicBlock::RemovePhi(HPhi* phi) {
96 ASSERT(phi->block() == this);
97 ASSERT(phis_.Contains(phi));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000098 ASSERT(phi->HasNoUses() || !phi->is_live());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000099 phi->ClearOperands();
100 phis_.RemoveElement(phi);
101 phi->SetBlock(NULL);
102}
103
104
105void HBasicBlock::AddInstruction(HInstruction* instr) {
106 ASSERT(!IsStartBlock() || !IsFinished());
107 ASSERT(!instr->IsLinked());
108 ASSERT(!IsFinished());
109 if (first_ == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000110 HBlockEntry* entry = new(zone()) HBlockEntry();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000111 entry->InitializeAsFirst(this);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000112 first_ = last_ = entry;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000113 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000114 instr->InsertAfter(last_);
115 last_ = instr;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000116}
117
118
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000119HDeoptimize* HBasicBlock::CreateDeoptimize(
120 HDeoptimize::UseEnvironment has_uses) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000121 ASSERT(HasEnvironment());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000122 if (has_uses == HDeoptimize::kNoUses) return new(zone()) HDeoptimize(0);
123
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000124 HEnvironment* environment = last_environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000125 HDeoptimize* instr = new(zone()) HDeoptimize(environment->length());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000126 for (int i = 0; i < environment->length(); i++) {
127 HValue* val = environment->values()->at(i);
128 instr->AddEnvironmentValue(val);
129 }
130
131 return instr;
132}
133
134
ager@chromium.org04921a82011-06-27 13:21:41 +0000135HSimulate* HBasicBlock::CreateSimulate(int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000136 ASSERT(HasEnvironment());
137 HEnvironment* environment = last_environment();
ager@chromium.org04921a82011-06-27 13:21:41 +0000138 ASSERT(ast_id == AstNode::kNoNumber ||
139 environment->closure()->shared()->VerifyBailoutId(ast_id));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000140
141 int push_count = environment->push_count();
142 int pop_count = environment->pop_count();
143
ager@chromium.org04921a82011-06-27 13:21:41 +0000144 HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000145 for (int i = push_count - 1; i >= 0; --i) {
146 instr->AddPushedValue(environment->ExpressionStackAt(i));
147 }
148 for (int i = 0; i < environment->assigned_variables()->length(); ++i) {
149 int index = environment->assigned_variables()->at(i);
150 instr->AddAssignedValue(index, environment->Lookup(index));
151 }
152 environment->ClearHistory();
153 return instr;
154}
155
156
157void HBasicBlock::Finish(HControlInstruction* end) {
158 ASSERT(!IsFinished());
159 AddInstruction(end);
160 end_ = end;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000161 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
162 it.Current()->RegisterPredecessor(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000163 }
164}
165
166
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000167void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000168 if (block->IsInlineReturnTarget()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000169 AddInstruction(new(zone()) HLeaveInlined);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000170 last_environment_ = last_environment()->DiscardInlined(drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000171 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000172 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000173 HGoto* instr = new(zone()) HGoto(block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000174 Finish(instr);
175}
176
177
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000178void HBasicBlock::AddLeaveInlined(HValue* return_value,
179 HBasicBlock* target,
180 bool drop_extra) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000181 ASSERT(target->IsInlineReturnTarget());
182 ASSERT(return_value != NULL);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +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 last_environment()->Push(return_value);
186 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000187 HGoto* instr = new(zone()) HGoto(target);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000188 Finish(instr);
189}
190
191
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000192void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
193 ASSERT(!HasEnvironment());
194 ASSERT(first() == NULL);
195 UpdateEnvironment(env);
196}
197
198
ager@chromium.org04921a82011-06-27 13:21:41 +0000199void HBasicBlock::SetJoinId(int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000200 int length = predecessors_.length();
201 ASSERT(length > 0);
202 for (int i = 0; i < length; i++) {
203 HBasicBlock* predecessor = predecessors_[i];
204 ASSERT(predecessor->end()->IsGoto());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000205 HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000206 // We only need to verify the ID once.
207 ASSERT(i != 0 ||
208 predecessor->last_environment()->closure()->shared()
ager@chromium.org04921a82011-06-27 13:21:41 +0000209 ->VerifyBailoutId(ast_id));
210 simulate->set_ast_id(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000211 }
212}
213
214
215bool HBasicBlock::Dominates(HBasicBlock* other) const {
216 HBasicBlock* current = other->dominator();
217 while (current != NULL) {
218 if (current == this) return true;
219 current = current->dominator();
220 }
221 return false;
222}
223
224
kmillikin@chromium.org83e16822011-09-13 08:21:47 +0000225int HBasicBlock::LoopNestingDepth() const {
226 const HBasicBlock* current = this;
227 int result = (current->IsLoopHeader()) ? 1 : 0;
228 while (current->parent_loop_header() != NULL) {
229 current = current->parent_loop_header();
230 result++;
231 }
232 return result;
233}
234
235
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000236void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
237 ASSERT(IsLoopHeader());
238
239 SetJoinId(stmt->EntryId());
240 if (predecessors()->length() == 1) {
241 // This is a degenerated loop.
242 DetachLoopInformation();
243 return;
244 }
245
246 // Only the first entry into the loop is from outside the loop. All other
247 // entries must be back edges.
248 for (int i = 1; i < predecessors()->length(); ++i) {
249 loop_information()->RegisterBackEdge(predecessors()->at(i));
250 }
251}
252
253
254void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
danno@chromium.org160a7b02011-04-18 15:51:38 +0000255 if (HasPredecessor()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000256 // Only loop header blocks can have a predecessor added after
257 // instructions have been added to the block (they have phis for all
258 // values in the environment, these phis may be eliminated later).
259 ASSERT(IsLoopHeader() || first_ == NULL);
260 HEnvironment* incoming_env = pred->last_environment();
261 if (IsLoopHeader()) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000262 ASSERT(phis()->length() == incoming_env->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000263 for (int i = 0; i < phis_.length(); ++i) {
264 phis_[i]->AddInput(incoming_env->values()->at(i));
265 }
266 } else {
267 last_environment()->AddIncomingEdge(this, pred->last_environment());
268 }
269 } else if (!HasEnvironment() && !IsFinished()) {
270 ASSERT(!IsLoopHeader());
271 SetInitialEnvironment(pred->last_environment()->Copy());
272 }
273
274 predecessors_.Add(pred);
275}
276
277
278void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
279 ASSERT(!dominated_blocks_.Contains(block));
280 // Keep the list of dominated blocks sorted such that if there is two
281 // succeeding block in this list, the predecessor is before the successor.
282 int index = 0;
283 while (index < dominated_blocks_.length() &&
284 dominated_blocks_[index]->block_id() < block->block_id()) {
285 ++index;
286 }
287 dominated_blocks_.InsertAt(index, block);
288}
289
290
291void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
292 if (dominator_ == NULL) {
293 dominator_ = other;
294 other->AddDominatedBlock(this);
295 } else if (other->dominator() != NULL) {
296 HBasicBlock* first = dominator_;
297 HBasicBlock* second = other;
298
299 while (first != second) {
300 if (first->block_id() > second->block_id()) {
301 first = first->dominator();
302 } else {
303 second = second->dominator();
304 }
305 ASSERT(first != NULL && second != NULL);
306 }
307
308 if (dominator_ != first) {
309 ASSERT(dominator_->dominated_blocks_.Contains(this));
310 dominator_->dominated_blocks_.RemoveElement(this);
311 dominator_ = first;
312 first->AddDominatedBlock(this);
313 }
314 }
315}
316
317
318int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
319 for (int i = 0; i < predecessors_.length(); ++i) {
320 if (predecessors_[i] == predecessor) return i;
321 }
322 UNREACHABLE();
323 return -1;
324}
325
326
327#ifdef DEBUG
328void HBasicBlock::Verify() {
329 // Check that every block is finished.
330 ASSERT(IsFinished());
331 ASSERT(block_id() >= 0);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000332
333 // Check that the incoming edges are in edge split form.
334 if (predecessors_.length() > 1) {
335 for (int i = 0; i < predecessors_.length(); ++i) {
336 ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
337 }
338 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000339}
340#endif
341
342
343void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
344 this->back_edges_.Add(block);
345 AddBlock(block);
346}
347
348
349HBasicBlock* HLoopInformation::GetLastBackEdge() const {
350 int max_id = -1;
351 HBasicBlock* result = NULL;
352 for (int i = 0; i < back_edges_.length(); ++i) {
353 HBasicBlock* cur = back_edges_[i];
354 if (cur->block_id() > max_id) {
355 max_id = cur->block_id();
356 result = cur;
357 }
358 }
359 return result;
360}
361
362
363void HLoopInformation::AddBlock(HBasicBlock* block) {
364 if (block == loop_header()) return;
365 if (block->parent_loop_header() == loop_header()) return;
366 if (block->parent_loop_header() != NULL) {
367 AddBlock(block->parent_loop_header());
368 } else {
369 block->set_parent_loop_header(loop_header());
370 blocks_.Add(block);
371 for (int i = 0; i < block->predecessors()->length(); ++i) {
372 AddBlock(block->predecessors()->at(i));
373 }
374 }
375}
376
377
378#ifdef DEBUG
379
380// Checks reachability of the blocks in this graph and stores a bit in
381// the BitVector "reachable()" for every block that can be reached
382// from the start block of the graph. If "dont_visit" is non-null, the given
383// block is treated as if it would not be part of the graph. "visited_count()"
384// returns the number of reachable blocks.
385class ReachabilityAnalyzer BASE_EMBEDDED {
386 public:
387 ReachabilityAnalyzer(HBasicBlock* entry_block,
388 int block_count,
389 HBasicBlock* dont_visit)
390 : visited_count_(0),
391 stack_(16),
392 reachable_(block_count),
393 dont_visit_(dont_visit) {
394 PushBlock(entry_block);
395 Analyze();
396 }
397
398 int visited_count() const { return visited_count_; }
399 const BitVector* reachable() const { return &reachable_; }
400
401 private:
402 void PushBlock(HBasicBlock* block) {
403 if (block != NULL && block != dont_visit_ &&
404 !reachable_.Contains(block->block_id())) {
405 reachable_.Add(block->block_id());
406 stack_.Add(block);
407 visited_count_++;
408 }
409 }
410
411 void Analyze() {
412 while (!stack_.is_empty()) {
413 HControlInstruction* end = stack_.RemoveLast()->end();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000414 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
415 PushBlock(it.Current());
416 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000417 }
418 }
419
420 int visited_count_;
421 ZoneList<HBasicBlock*> stack_;
422 BitVector reachable_;
423 HBasicBlock* dont_visit_;
424};
425
426
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000427void HGraph::Verify(bool do_full_verify) const {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000428 for (int i = 0; i < blocks_.length(); i++) {
429 HBasicBlock* block = blocks_.at(i);
430
431 block->Verify();
432
433 // Check that every block contains at least one node and that only the last
434 // node is a control instruction.
435 HInstruction* current = block->first();
436 ASSERT(current != NULL && current->IsBlockEntry());
437 while (current != NULL) {
438 ASSERT((current->next() == NULL) == current->IsControlInstruction());
439 ASSERT(current->block() == block);
440 current->Verify();
441 current = current->next();
442 }
443
444 // Check that successors are correctly set.
445 HBasicBlock* first = block->end()->FirstSuccessor();
446 HBasicBlock* second = block->end()->SecondSuccessor();
447 ASSERT(second == NULL || first != NULL);
448
449 // Check that the predecessor array is correct.
450 if (first != NULL) {
451 ASSERT(first->predecessors()->Contains(block));
452 if (second != NULL) {
453 ASSERT(second->predecessors()->Contains(block));
454 }
455 }
456
457 // Check that phis have correct arguments.
458 for (int j = 0; j < block->phis()->length(); j++) {
459 HPhi* phi = block->phis()->at(j);
460 phi->Verify();
461 }
462
463 // Check that all join blocks have predecessors that end with an
464 // unconditional goto and agree on their environment node id.
465 if (block->predecessors()->length() >= 2) {
466 int id = block->predecessors()->first()->last_environment()->ast_id();
467 for (int k = 0; k < block->predecessors()->length(); k++) {
468 HBasicBlock* predecessor = block->predecessors()->at(k);
469 ASSERT(predecessor->end()->IsGoto());
470 ASSERT(predecessor->last_environment()->ast_id() == id);
471 }
472 }
473 }
474
475 // Check special property of first block to have no predecessors.
476 ASSERT(blocks_.at(0)->predecessors()->is_empty());
477
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000478 if (do_full_verify) {
479 // Check that the graph is fully connected.
480 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
481 ASSERT(analyzer.visited_count() == blocks_.length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000482
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000483 // Check that entry block dominator is NULL.
484 ASSERT(entry_block_->dominator() == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000485
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000486 // Check dominators.
487 for (int i = 0; i < blocks_.length(); ++i) {
488 HBasicBlock* block = blocks_.at(i);
489 if (block->dominator() == NULL) {
490 // Only start block may have no dominator assigned to.
491 ASSERT(i == 0);
492 } else {
493 // Assert that block is unreachable if dominator must not be visited.
494 ReachabilityAnalyzer dominator_analyzer(entry_block_,
495 blocks_.length(),
496 block->dominator());
497 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
498 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000499 }
500 }
501}
502
503#endif
504
505
506HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
507 Object* value) {
508 if (!pointer->is_set()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000509 HConstant* constant = new(zone()) HConstant(Handle<Object>(value),
510 Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000511 constant->InsertAfter(GetConstantUndefined());
512 pointer->set(constant);
513 }
514 return pointer->get();
515}
516
517
518HConstant* HGraph::GetConstant1() {
519 return GetConstant(&constant_1_, Smi::FromInt(1));
520}
521
522
523HConstant* HGraph::GetConstantMinus1() {
524 return GetConstant(&constant_minus1_, Smi::FromInt(-1));
525}
526
527
528HConstant* HGraph::GetConstantTrue() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000529 return GetConstant(&constant_true_, isolate()->heap()->true_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000530}
531
532
533HConstant* HGraph::GetConstantFalse() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000534 return GetConstant(&constant_false_, isolate()->heap()->false_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000535}
536
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000537
538HConstant* HGraph::GetConstantHole() {
539 return GetConstant(&constant_hole_, isolate()->heap()->the_hole_value());
540}
541
542
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000543HGraphBuilder::HGraphBuilder(CompilationInfo* info,
544 TypeFeedbackOracle* oracle)
545 : function_state_(NULL),
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000546 initial_function_state_(this, info, oracle, false),
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000547 ast_context_(NULL),
548 break_scope_(NULL),
549 graph_(NULL),
550 current_block_(NULL),
551 inlined_count_(0),
552 zone_(info->isolate()->zone()),
553 inline_bailout_(false) {
554 // This is not initialized in the initializer list because the
555 // constructor for the initial state relies on function_state_ == NULL
556 // to know it's the initial state.
557 function_state_= &initial_function_state_;
558}
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000559
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000560HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
561 HBasicBlock* second,
562 int join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000563 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000564 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000565 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000566 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000567 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000568 HBasicBlock* join_block = graph_->CreateBasicBlock();
569 first->Goto(join_block);
570 second->Goto(join_block);
571 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000572 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000573 }
574}
575
576
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000577HBasicBlock* HGraphBuilder::JoinContinue(IterationStatement* statement,
578 HBasicBlock* exit_block,
579 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000580 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000581 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000582 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000583 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000584 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000585 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000586}
587
588
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000589HBasicBlock* HGraphBuilder::CreateLoop(IterationStatement* statement,
590 HBasicBlock* loop_entry,
591 HBasicBlock* body_exit,
592 HBasicBlock* loop_successor,
593 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +0000594 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000595 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000596 if (break_block != NULL) {
597 if (loop_successor != NULL) loop_successor->Goto(break_block);
598 break_block->SetJoinId(statement->ExitId());
599 return break_block;
600 }
601 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000602}
603
604
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000605void HBasicBlock::FinishExit(HControlInstruction* instruction) {
606 Finish(instruction);
607 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000608}
609
610
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000611HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000612 : isolate_(info->isolate()),
613 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000614 entry_block_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000615 blocks_(8),
616 values_(16),
617 phi_list_(NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000618 start_environment_ =
619 new(zone()) HEnvironment(NULL, info->scope(), info->closure());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000620 start_environment_->set_ast_id(AstNode::kFunctionEntryId);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000621 entry_block_ = CreateBasicBlock();
622 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000623}
624
625
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000626Handle<Code> HGraph::Compile(CompilationInfo* info) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000627 int values = GetMaximumValueID();
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000628 if (values > LUnallocated::kMaxVirtualRegisters) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000629 if (FLAG_trace_bailout) {
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000630 PrintF("Not enough virtual registers for (values).\n");
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000631 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000632 return Handle<Code>::null();
633 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000634 LAllocator allocator(values, this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000635 LChunkBuilder builder(info, this, &allocator);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000636 LChunk* chunk = builder.Build();
637 if (chunk == NULL) return Handle<Code>::null();
638
rossberg@chromium.org994edf62012-02-06 10:12:55 +0000639 if (!allocator.Allocate(chunk)) {
640 if (FLAG_trace_bailout) {
641 PrintF("Not enough virtual registers (regalloc).\n");
642 }
643 return Handle<Code>::null();
644 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000645
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000646 MacroAssembler assembler(info->isolate(), NULL, 0);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000647 LCodeGen generator(chunk, &assembler, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000648
649 if (FLAG_eliminate_empty_blocks) {
650 chunk->MarkEmptyBlocks();
651 }
652
653 if (generator.GenerateCode()) {
654 if (FLAG_trace_codegen) {
655 PrintF("Crankshaft Compiler - ");
656 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000657 CodeGenerator::MakeCodePrologue(info);
lrn@chromium.org34e60782011-09-15 07:25:40 +0000658 Code::Flags flags = Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000659 Handle<Code> code =
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000660 CodeGenerator::MakeCodeEpilogue(&assembler, flags, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000661 generator.FinishCode(code);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000662 CodeGenerator::PrintCode(code, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000663 return code;
664 }
665 return Handle<Code>::null();
666}
667
668
669HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000670 HBasicBlock* result = new(zone()) HBasicBlock(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000671 blocks_.Add(result);
672 return result;
673}
674
675
676void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000677 if (!FLAG_use_canonicalizing) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000678 HPhase phase("Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000679 for (int i = 0; i < blocks()->length(); ++i) {
680 HInstruction* instr = blocks()->at(i)->first();
681 while (instr != NULL) {
682 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000683 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000684 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000685 }
686 }
687}
688
689
690void HGraph::OrderBlocks() {
691 HPhase phase("Block ordering");
692 BitVector visited(blocks_.length());
693
694 ZoneList<HBasicBlock*> reverse_result(8);
695 HBasicBlock* start = blocks_[0];
696 Postorder(start, &visited, &reverse_result, NULL);
697
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000698 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000699 int index = 0;
700 for (int i = reverse_result.length() - 1; i >= 0; --i) {
701 HBasicBlock* b = reverse_result[i];
702 blocks_.Add(b);
703 b->set_block_id(index++);
704 }
705}
706
707
708void HGraph::PostorderLoopBlocks(HLoopInformation* loop,
709 BitVector* visited,
710 ZoneList<HBasicBlock*>* order,
711 HBasicBlock* loop_header) {
712 for (int i = 0; i < loop->blocks()->length(); ++i) {
713 HBasicBlock* b = loop->blocks()->at(i);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000714 for (HSuccessorIterator it(b->end()); !it.Done(); it.Advance()) {
715 Postorder(it.Current(), visited, order, loop_header);
716 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000717 if (b->IsLoopHeader() && b != loop->loop_header()) {
718 PostorderLoopBlocks(b->loop_information(), visited, order, loop_header);
719 }
720 }
721}
722
723
724void HGraph::Postorder(HBasicBlock* block,
725 BitVector* visited,
726 ZoneList<HBasicBlock*>* order,
727 HBasicBlock* loop_header) {
728 if (block == NULL || visited->Contains(block->block_id())) return;
729 if (block->parent_loop_header() != loop_header) return;
730 visited->Add(block->block_id());
731 if (block->IsLoopHeader()) {
732 PostorderLoopBlocks(block->loop_information(), visited, order, loop_header);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000733 for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
734 Postorder(it.Current(), visited, order, block);
735 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000736 } else {
erikcorry0ad885c2011-11-21 13:51:57 +0000737 ASSERT(block->IsFinished());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000738 for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
739 Postorder(it.Current(), visited, order, loop_header);
740 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000741 }
742 ASSERT(block->end()->FirstSuccessor() == NULL ||
743 order->Contains(block->end()->FirstSuccessor()) ||
744 block->end()->FirstSuccessor()->IsLoopHeader());
745 ASSERT(block->end()->SecondSuccessor() == NULL ||
746 order->Contains(block->end()->SecondSuccessor()) ||
747 block->end()->SecondSuccessor()->IsLoopHeader());
748 order->Add(block);
749}
750
751
752void HGraph::AssignDominators() {
753 HPhase phase("Assign dominators", this);
754 for (int i = 0; i < blocks_.length(); ++i) {
755 if (blocks_[i]->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000756 // Only the first predecessor of a loop header is from outside the loop.
757 // All others are back edges, and thus cannot dominate the loop header.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000758 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
759 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000760 for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000761 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
762 }
763 }
764 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000765}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000766
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000767// Mark all blocks that are dominated by an unconditional soft deoptimize to
768// prevent code motion across those blocks.
769void HGraph::PropagateDeoptimizingMark() {
770 HPhase phase("Propagate deoptimizing mark", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000771 MarkAsDeoptimizingRecursively(entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000772}
773
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000774void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
775 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
776 HBasicBlock* dominated = block->dominated_blocks()->at(i);
777 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
778 MarkAsDeoptimizingRecursively(dominated);
779 }
780}
781
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000782void HGraph::EliminateRedundantPhis() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000783 HPhase phase("Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000784
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000785 // Worklist of phis that can potentially be eliminated. Initialized with
786 // all phi nodes. When elimination of a phi node modifies another phi node
787 // the modified phi node is added to the worklist.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000788 ZoneList<HPhi*> worklist(blocks_.length());
789 for (int i = 0; i < blocks_.length(); ++i) {
790 worklist.AddAll(*blocks_[i]->phis());
791 }
792
793 while (!worklist.is_empty()) {
794 HPhi* phi = worklist.RemoveLast();
795 HBasicBlock* block = phi->block();
796
797 // Skip phi node if it was already replaced.
798 if (block == NULL) continue;
799
800 // Get replacement value if phi is redundant.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000801 HValue* replacement = phi->GetRedundantReplacement();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000802
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000803 if (replacement != NULL) {
804 // Iterate through the uses and replace them all.
805 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
806 HValue* value = it.value();
807 value->SetOperandAt(it.index(), replacement);
808 if (value->IsPhi()) worklist.Add(HPhi::cast(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000809 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000810 block->RemovePhi(phi);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000811 }
812 }
813}
814
815
816void HGraph::EliminateUnreachablePhis() {
817 HPhase phase("Unreachable phi elimination", this);
818
819 // Initialize worklist.
820 ZoneList<HPhi*> phi_list(blocks_.length());
821 ZoneList<HPhi*> worklist(blocks_.length());
822 for (int i = 0; i < blocks_.length(); ++i) {
823 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
824 HPhi* phi = blocks_[i]->phis()->at(j);
825 phi_list.Add(phi);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +0000826 // We can't eliminate phis in the receiver position in the environment
827 // because in case of throwing an error we need this value to
828 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000829 if (phi->HasRealUses() || phi->IsReceiver()) {
830 phi->set_is_live(true);
831 worklist.Add(phi);
832 }
833 }
834 }
835
836 // Iteratively mark live phis.
837 while (!worklist.is_empty()) {
838 HPhi* phi = worklist.RemoveLast();
839 for (int i = 0; i < phi->OperandCount(); i++) {
840 HValue* operand = phi->OperandAt(i);
841 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
842 HPhi::cast(operand)->set_is_live(true);
843 worklist.Add(HPhi::cast(operand));
844 }
845 }
846 }
847
848 // Remove unreachable phis.
849 for (int i = 0; i < phi_list.length(); i++) {
850 HPhi* phi = phi_list[i];
851 if (!phi->is_live()) {
852 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000853 block->RemovePhi(phi);
854 block->RecordDeletedPhi(phi->merged_index());
855 }
856 }
857}
858
859
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000860bool HGraph::CheckArgumentsPhiUses() {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +0000861 int block_count = blocks_.length();
862 for (int i = 0; i < block_count; ++i) {
863 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
864 HPhi* phi = blocks_[i]->phis()->at(j);
865 // We don't support phi uses of arguments for now.
866 if (phi->CheckFlag(HValue::kIsArguments)) return false;
867 }
868 }
869 return true;
870}
871
872
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000873bool HGraph::CheckConstPhiUses() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000874 int block_count = blocks_.length();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000875 for (int i = 0; i < block_count; ++i) {
876 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
877 HPhi* phi = blocks_[i]->phis()->at(j);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000878 // Check for the hole value (from an uninitialized const).
879 for (int k = 0; k < phi->OperandCount(); k++) {
880 if (phi->OperandAt(k) == GetConstantHole()) return false;
881 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000882 }
883 }
884 return true;
885}
886
887
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000888void HGraph::CollectPhis() {
889 int block_count = blocks_.length();
890 phi_list_ = new ZoneList<HPhi*>(block_count);
891 for (int i = 0; i < block_count; ++i) {
892 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
893 HPhi* phi = blocks_[i]->phis()->at(j);
894 phi_list_->Add(phi);
895 }
896 }
897}
898
899
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000900void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
901 BitVector in_worklist(GetMaximumValueID());
902 for (int i = 0; i < worklist->length(); ++i) {
903 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
904 in_worklist.Add(worklist->at(i)->id());
905 }
906
907 while (!worklist->is_empty()) {
908 HValue* current = worklist->RemoveLast();
909 in_worklist.Remove(current->id());
910 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000911 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
912 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000913 if (!in_worklist.Contains(use->id())) {
914 in_worklist.Add(use->id());
915 worklist->Add(use);
916 }
917 }
918 }
919 }
920}
921
922
923class HRangeAnalysis BASE_EMBEDDED {
924 public:
925 explicit HRangeAnalysis(HGraph* graph) : graph_(graph), changed_ranges_(16) {}
926
927 void Analyze();
928
929 private:
930 void TraceRange(const char* msg, ...);
931 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000932 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
933 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000934 void InferRange(HValue* value);
935 void RollBackTo(int index);
936 void AddRange(HValue* value, Range* range);
937
938 HGraph* graph_;
939 ZoneList<HValue*> changed_ranges_;
940};
941
942
943void HRangeAnalysis::TraceRange(const char* msg, ...) {
944 if (FLAG_trace_range) {
945 va_list arguments;
946 va_start(arguments, msg);
947 OS::VPrint(msg, arguments);
948 va_end(arguments);
949 }
950}
951
952
953void HRangeAnalysis::Analyze() {
954 HPhase phase("Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000955 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000956}
957
958
959void HRangeAnalysis::Analyze(HBasicBlock* block) {
960 TraceRange("Analyzing block B%d\n", block->block_id());
961
962 int last_changed_range = changed_ranges_.length() - 1;
963
964 // Infer range based on control flow.
965 if (block->predecessors()->length() == 1) {
966 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000967 if (pred->end()->IsCompareIDAndBranch()) {
968 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000969 }
970 }
971
972 // Process phi instructions.
973 for (int i = 0; i < block->phis()->length(); ++i) {
974 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000975 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000976 }
977
978 // Go through all instructions of the current block.
979 HInstruction* instr = block->first();
980 while (instr != block->end()) {
981 InferRange(instr);
982 instr = instr->next();
983 }
984
985 // Continue analysis in all dominated blocks.
986 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
987 Analyze(block->dominated_blocks()->at(i));
988 }
989
990 RollBackTo(last_changed_range);
991}
992
993
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000994void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
995 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000996 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000997 if (test->GetInputRepresentation().IsInteger32()) {
998 Token::Value op = test->token();
999 if (test->SecondSuccessor() == dest) {
1000 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001001 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001002 Token::Value inverted_op = Token::InvertCompareOp(op);
1003 UpdateControlFlowRange(op, test->left(), test->right());
1004 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001005 }
1006}
1007
1008
1009// We know that value [op] other. Use this information to update the range on
1010// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001011void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
1012 HValue* value,
1013 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001014 Range temp_range;
1015 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001016 Range* new_range = NULL;
1017
1018 TraceRange("Control flow range infer %d %s %d\n",
1019 value->id(),
1020 Token::Name(op),
1021 other->id());
1022
1023 if (op == Token::EQ || op == Token::EQ_STRICT) {
1024 // The same range has to apply for value.
1025 new_range = range->Copy();
1026 } else if (op == Token::LT || op == Token::LTE) {
1027 new_range = range->CopyClearLower();
1028 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001029 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001030 }
1031 } else if (op == Token::GT || op == Token::GTE) {
1032 new_range = range->CopyClearUpper();
1033 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001034 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001035 }
1036 }
1037
1038 if (new_range != NULL && !new_range->IsMostGeneric()) {
1039 AddRange(value, new_range);
1040 }
1041}
1042
1043
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001044void HRangeAnalysis::InferRange(HValue* value) {
1045 ASSERT(!value->HasRange());
1046 if (!value->representation().IsNone()) {
1047 value->ComputeInitialRange();
1048 Range* range = value->range();
1049 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1050 value->id(),
1051 value->Mnemonic(),
1052 range->lower(),
1053 range->upper());
1054 }
1055}
1056
1057
1058void HRangeAnalysis::RollBackTo(int index) {
1059 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1060 changed_ranges_[i]->RemoveLastAddedRange();
1061 }
1062 changed_ranges_.Rewind(index + 1);
1063}
1064
1065
1066void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1067 Range* original_range = value->range();
1068 value->AddNewRange(range);
1069 changed_ranges_.Add(value);
1070 Range* new_range = value->range();
1071 TraceRange("Updated range of %d set to [%d,%d]\n",
1072 value->id(),
1073 new_range->lower(),
1074 new_range->upper());
1075 if (original_range != NULL) {
1076 TraceRange("Original range was [%d,%d]\n",
1077 original_range->lower(),
1078 original_range->upper());
1079 }
1080 TraceRange("New information was [%d,%d]\n",
1081 range->lower(),
1082 range->upper());
1083}
1084
1085
1086void TraceGVN(const char* msg, ...) {
1087 if (FLAG_trace_gvn) {
1088 va_list arguments;
1089 va_start(arguments, msg);
1090 OS::VPrint(msg, arguments);
1091 va_end(arguments);
1092 }
1093}
1094
1095
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001096HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001097 : array_size_(other->array_size_),
1098 lists_size_(other->lists_size_),
1099 count_(other->count_),
1100 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001101 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
1102 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001103 free_list_head_(other->free_list_head_) {
1104 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
1105 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
1106}
1107
1108
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001109void HValueMap::Kill(GVNFlagSet flags) {
1110 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags);
1111 if (!present_flags_.ContainsAnyOf(depends_flags)) return;
1112 present_flags_.RemoveAll();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001113 for (int i = 0; i < array_size_; ++i) {
1114 HValue* value = array_[i].value;
1115 if (value != NULL) {
1116 // Clear list of collisions first, so we know if it becomes empty.
1117 int kept = kNil; // List of kept elements.
1118 int next;
1119 for (int current = array_[i].next; current != kNil; current = next) {
1120 next = lists_[current].next;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001121 HValue* value = lists_[current].value;
1122 if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001123 // Drop it.
1124 count_--;
1125 lists_[current].next = free_list_head_;
1126 free_list_head_ = current;
1127 } else {
1128 // Keep it.
1129 lists_[current].next = kept;
1130 kept = current;
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001131 present_flags_.Add(value->gvn_flags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001132 }
1133 }
1134 array_[i].next = kept;
1135
1136 // Now possibly drop directly indexed element.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001137 value = array_[i].value;
1138 if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001139 count_--;
1140 int head = array_[i].next;
1141 if (head == kNil) {
1142 array_[i].value = NULL;
1143 } else {
1144 array_[i].value = lists_[head].value;
1145 array_[i].next = lists_[head].next;
1146 lists_[head].next = free_list_head_;
1147 free_list_head_ = head;
1148 }
1149 } else {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001150 present_flags_.Add(value->gvn_flags()); // Keep it.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001151 }
1152 }
1153 }
1154}
1155
1156
1157HValue* HValueMap::Lookup(HValue* value) const {
1158 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
1159 uint32_t pos = Bound(hash);
1160 if (array_[pos].value != NULL) {
1161 if (array_[pos].value->Equals(value)) return array_[pos].value;
1162 int next = array_[pos].next;
1163 while (next != kNil) {
1164 if (lists_[next].value->Equals(value)) return lists_[next].value;
1165 next = lists_[next].next;
1166 }
1167 }
1168 return NULL;
1169}
1170
1171
1172void HValueMap::Resize(int new_size) {
1173 ASSERT(new_size > count_);
1174 // Hashing the values into the new array has no more collisions than in the
1175 // old hash map, so we can use the existing lists_ array, if we are careful.
1176
1177 // Make sure we have at least one free element.
1178 if (free_list_head_ == kNil) {
1179 ResizeLists(lists_size_ << 1);
1180 }
1181
1182 HValueMapListElement* new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001183 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001184 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
1185
1186 HValueMapListElement* old_array = array_;
1187 int old_size = array_size_;
1188
1189 int old_count = count_;
1190 count_ = 0;
1191 // Do not modify present_flags_. It is currently correct.
1192 array_size_ = new_size;
1193 array_ = new_array;
1194
1195 if (old_array != NULL) {
1196 // Iterate over all the elements in lists, rehashing them.
1197 for (int i = 0; i < old_size; ++i) {
1198 if (old_array[i].value != NULL) {
1199 int current = old_array[i].next;
1200 while (current != kNil) {
1201 Insert(lists_[current].value);
1202 int next = lists_[current].next;
1203 lists_[current].next = free_list_head_;
1204 free_list_head_ = current;
1205 current = next;
1206 }
1207 // Rehash the directly stored value.
1208 Insert(old_array[i].value);
1209 }
1210 }
1211 }
1212 USE(old_count);
1213 ASSERT(count_ == old_count);
1214}
1215
1216
1217void HValueMap::ResizeLists(int new_size) {
1218 ASSERT(new_size > lists_size_);
1219
1220 HValueMapListElement* new_lists =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001221 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001222 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
1223
1224 HValueMapListElement* old_lists = lists_;
1225 int old_size = lists_size_;
1226
1227 lists_size_ = new_size;
1228 lists_ = new_lists;
1229
1230 if (old_lists != NULL) {
1231 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
1232 }
1233 for (int i = old_size; i < lists_size_; ++i) {
1234 lists_[i].next = free_list_head_;
1235 free_list_head_ = i;
1236 }
1237}
1238
1239
1240void HValueMap::Insert(HValue* value) {
1241 ASSERT(value != NULL);
1242 // Resizing when half of the hashtable is filled up.
1243 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1);
1244 ASSERT(count_ < array_size_);
1245 count_++;
1246 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
1247 if (array_[pos].value == NULL) {
1248 array_[pos].value = value;
1249 array_[pos].next = kNil;
1250 } else {
1251 if (free_list_head_ == kNil) {
1252 ResizeLists(lists_size_ << 1);
1253 }
1254 int new_element_pos = free_list_head_;
1255 ASSERT(new_element_pos != kNil);
1256 free_list_head_ = lists_[free_list_head_].next;
1257 lists_[new_element_pos].value = value;
1258 lists_[new_element_pos].next = array_[pos].next;
1259 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
1260 array_[pos].next = new_element_pos;
1261 }
1262}
1263
1264
1265class HStackCheckEliminator BASE_EMBEDDED {
1266 public:
1267 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
1268
1269 void Process();
1270
1271 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001272 HGraph* graph_;
1273};
1274
1275
1276void HStackCheckEliminator::Process() {
1277 // For each loop block walk the dominator tree from the backwards branch to
1278 // the loop header. If a call instruction is encountered the backwards branch
1279 // is dominated by a call and the stack check in the backwards branch can be
1280 // removed.
1281 for (int i = 0; i < graph_->blocks()->length(); i++) {
1282 HBasicBlock* block = graph_->blocks()->at(i);
1283 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001284 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
1285 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00001286 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001287 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001288 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001289 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00001290 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001291 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001292 }
1293 instr = instr->next();
1294 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00001295
1296 // Done when the loop header is processed.
1297 if (dominator == block) break;
1298
1299 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001300 dominator = dominator->dominator();
1301 }
1302 }
1303 }
1304}
1305
1306
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001307// Simple sparse set with O(1) add, contains, and clear.
1308class SparseSet {
1309 public:
1310 SparseSet(Zone* zone, int capacity)
1311 : capacity_(capacity),
1312 length_(0),
1313 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001314 sparse_(zone->NewArray<int>(capacity)) {
1315#ifndef NVALGRIND
1316 // Initialize the sparse array to make valgrind happy.
1317 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
1318#endif
1319 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001320
1321 bool Contains(int n) const {
1322 ASSERT(0 <= n && n < capacity_);
1323 int d = sparse_[n];
1324 return 0 <= d && d < length_ && dense_[d] == n;
1325 }
1326
1327 bool Add(int n) {
1328 if (Contains(n)) return false;
1329 dense_[length_] = n;
1330 sparse_[n] = length_;
1331 ++length_;
1332 return true;
1333 }
1334
1335 void Clear() { length_ = 0; }
1336
1337 private:
1338 int capacity_;
1339 int length_;
1340 int* dense_;
1341 int* sparse_;
1342
1343 DISALLOW_COPY_AND_ASSIGN(SparseSet);
1344};
1345
1346
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001347class HGlobalValueNumberer BASE_EMBEDDED {
1348 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001349 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001350 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001351 info_(info),
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001352 removed_side_effects_(false),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001353 block_side_effects_(graph->blocks()->length()),
1354 loop_side_effects_(graph->blocks()->length()),
1355 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001356 ASSERT(info->isolate()->heap()->allow_allocation(false));
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001357 block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length());
1358 loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001359 }
1360 ~HGlobalValueNumberer() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001361 ASSERT(!info_->isolate()->heap()->allow_allocation(true));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001362 }
1363
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001364 // Returns true if values with side effects are removed.
1365 bool Analyze();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001366
1367 private:
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001368 GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
1369 HBasicBlock* dominator,
1370 HBasicBlock* dominated);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001371 void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
1372 void ComputeBlockSideEffects();
1373 void LoopInvariantCodeMotion();
1374 void ProcessLoopBlock(HBasicBlock* block,
1375 HBasicBlock* before_loop,
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001376 GVNFlagSet loop_kills);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001377 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001378 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
1379
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001380 HGraph* graph() { return graph_; }
1381 CompilationInfo* info() { return info_; }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001382 Zone* zone() { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001383
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001384 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001385 CompilationInfo* info_;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001386 bool removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001387
1388 // A map of block IDs to their side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001389 ZoneList<GVNFlagSet> block_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001390
1391 // A map of loop header block IDs to their loop's side effects.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001392 ZoneList<GVNFlagSet> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001393
1394 // Used when collecting side effects on paths from dominator to
1395 // dominated.
1396 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001397};
1398
1399
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001400bool HGlobalValueNumberer::Analyze() {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001401 ComputeBlockSideEffects();
1402 if (FLAG_loop_invariant_code_motion) {
1403 LoopInvariantCodeMotion();
1404 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001405 HValueMap* map = new(zone()) HValueMap();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001406 AnalyzeBlock(graph_->entry_block(), map);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001407 return removed_side_effects_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001408}
1409
1410
1411void HGlobalValueNumberer::ComputeBlockSideEffects() {
1412 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1413 // Compute side effects for the block.
1414 HBasicBlock* block = graph_->blocks()->at(i);
1415 HInstruction* instr = block->first();
1416 int id = block->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001417 GVNFlagSet side_effects;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001418 while (instr != NULL) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001419 side_effects.Add(instr->ChangesFlags());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001420 instr = instr->next();
1421 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001422 block_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001423
1424 // Loop headers are part of their loop.
1425 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001426 loop_side_effects_[id].Add(side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001427 }
1428
1429 // Propagate loop side effects upwards.
1430 if (block->HasParentLoopHeader()) {
1431 int header_id = block->parent_loop_header()->block_id();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001432 loop_side_effects_[header_id].Add(block->IsLoopHeader()
1433 ? loop_side_effects_[id]
1434 : side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001435 }
1436 }
1437}
1438
1439
1440void HGlobalValueNumberer::LoopInvariantCodeMotion() {
1441 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1442 HBasicBlock* block = graph_->blocks()->at(i);
1443 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001444 GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001445 TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n",
1446 block->block_id(),
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001447 side_effects.ToIntegral());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001448
1449 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
1450 for (int j = block->block_id(); j <= last->block_id(); ++j) {
1451 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
1452 }
1453 }
1454 }
1455}
1456
1457
1458void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
1459 HBasicBlock* loop_header,
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001460 GVNFlagSet loop_kills) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001461 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001462 GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001463 TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
1464 block->block_id(),
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001465 depends_flags.ToIntegral());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001466 HInstruction* instr = block->first();
1467 while (instr != NULL) {
1468 HInstruction* next = instr->next();
1469 if (instr->CheckFlag(HValue::kUseGVN) &&
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001470 !instr->gvn_flags().ContainsAnyOf(depends_flags)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001471 TraceGVN("Checking instruction %d (%s)\n",
1472 instr->id(),
1473 instr->Mnemonic());
1474 bool inputs_loop_invariant = true;
1475 for (int i = 0; i < instr->OperandCount(); ++i) {
1476 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
1477 inputs_loop_invariant = false;
1478 }
1479 }
1480
1481 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
1482 TraceGVN("Found loop invariant instruction %d\n", instr->id());
1483 // Move the instruction out of the loop.
1484 instr->Unlink();
1485 instr->InsertBefore(pre_header->end());
1486 }
1487 }
1488 instr = next;
1489 }
1490}
1491
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00001492
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001493bool HGlobalValueNumberer::AllowCodeMotion() {
1494 return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount;
1495}
1496
1497
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001498bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
1499 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001500 // If we've disabled code motion or we're in a block that unconditionally
1501 // deoptimizes, don't move any instructions.
1502 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001503}
1504
1505
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001506GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001507 HBasicBlock* dominator, HBasicBlock* dominated) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001508 GVNFlagSet side_effects;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001509 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
1510 HBasicBlock* block = dominated->predecessors()->at(i);
1511 if (dominator->block_id() < block->block_id() &&
1512 block->block_id() < dominated->block_id() &&
1513 visited_on_paths_.Add(block->block_id())) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001514 side_effects.Add(block_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001515 if (block->IsLoopHeader()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001516 side_effects.Add(loop_side_effects_[block->block_id()]);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001517 }
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001518 side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
1519 dominator, block));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001520 }
1521 }
1522 return side_effects;
1523}
1524
1525
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001526void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001527 TraceGVN("Analyzing block B%d%s\n",
1528 block->block_id(),
1529 block->IsLoopHeader() ? " (loop header)" : "");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001530
1531 // If this is a loop header kill everything killed by the loop.
1532 if (block->IsLoopHeader()) {
1533 map->Kill(loop_side_effects_[block->block_id()]);
1534 }
1535
1536 // Go through all instructions of the current block.
1537 HInstruction* instr = block->first();
1538 while (instr != NULL) {
1539 HInstruction* next = instr->next();
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001540 GVNFlagSet flags = instr->ChangesFlags();
1541 if (!flags.IsEmpty()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001542 // Clear all instructions in the map that are affected by side effects.
1543 map->Kill(flags);
1544 TraceGVN("Instruction %d kills\n", instr->id());
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001545 }
1546 if (instr->CheckFlag(HValue::kUseGVN)) {
1547 ASSERT(!instr->HasObservableSideEffects());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001548 HValue* other = map->Lookup(instr);
1549 if (other != NULL) {
1550 ASSERT(instr->Equals(other) && other->Equals(instr));
1551 TraceGVN("Replacing value %d (%s) with value %d (%s)\n",
1552 instr->id(),
1553 instr->Mnemonic(),
1554 other->id(),
1555 other->Mnemonic());
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001556 if (instr->HasSideEffects()) removed_side_effects_ = true;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001557 instr->DeleteAndReplaceWith(other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001558 } else {
1559 map->Add(instr);
1560 }
1561 }
1562 instr = next;
1563 }
1564
1565 // Recursively continue analysis for all immediately dominated blocks.
1566 int length = block->dominated_blocks()->length();
1567 for (int i = 0; i < length; ++i) {
1568 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1569 // No need to copy the map for the last child in the dominator tree.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001570 HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001571
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001572 // Kill everything killed on any path between this block and the
1573 // dominated block.
1574 // We don't have to traverse these paths if the value map is
1575 // already empty.
1576 // If the range of block ids (block_id, dominated_id) is empty
1577 // there are no such paths.
1578 if (!successor_map->IsEmpty() &&
1579 block->block_id() + 1 < dominated->block_id()) {
1580 visited_on_paths_.Clear();
1581 successor_map->Kill(CollectSideEffectsOnPathsToDominatedBlock(block,
1582 dominated));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001583 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001584 AnalyzeBlock(dominated, successor_map);
1585 }
1586}
1587
1588
1589class HInferRepresentation BASE_EMBEDDED {
1590 public:
1591 explicit HInferRepresentation(HGraph* graph)
1592 : graph_(graph), worklist_(8), in_worklist_(graph->GetMaximumValueID()) {}
1593
1594 void Analyze();
1595
1596 private:
1597 Representation TryChange(HValue* current);
1598 void AddToWorklist(HValue* current);
1599 void InferBasedOnInputs(HValue* current);
1600 void AddDependantsToWorklist(HValue* current);
1601 void InferBasedOnUses(HValue* current);
1602
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001603 Zone* zone() { return graph_->zone(); }
1604
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001605 HGraph* graph_;
1606 ZoneList<HValue*> worklist_;
1607 BitVector in_worklist_;
1608};
1609
1610
1611void HInferRepresentation::AddToWorklist(HValue* current) {
1612 if (current->representation().IsSpecialization()) return;
1613 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
1614 if (in_worklist_.Contains(current->id())) return;
1615 worklist_.Add(current);
1616 in_worklist_.Add(current->id());
1617}
1618
1619
1620// This method tries to specialize the representation type of the value
1621// given as a parameter. The value is asked to infer its representation type
1622// based on its inputs. If the inferred type is more specialized, then this
1623// becomes the new representation type of the node.
1624void HInferRepresentation::InferBasedOnInputs(HValue* current) {
1625 Representation r = current->representation();
1626 if (r.IsSpecialization()) return;
1627 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
1628 Representation inferred = current->InferredRepresentation();
1629 if (inferred.IsSpecialization()) {
1630 current->ChangeRepresentation(inferred);
1631 AddDependantsToWorklist(current);
1632 }
1633}
1634
1635
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001636void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
1637 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1638 AddToWorklist(it.value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001639 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001640 for (int i = 0; i < value->OperandCount(); ++i) {
1641 AddToWorklist(value->OperandAt(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001642 }
1643}
1644
1645
1646// This method calculates whether specializing the representation of the value
1647// given as the parameter has a benefit in terms of less necessary type
1648// conversions. If there is a benefit, then the representation of the value is
1649// specialized.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001650void HInferRepresentation::InferBasedOnUses(HValue* value) {
1651 Representation r = value->representation();
1652 if (r.IsSpecialization() || value->HasNoUses()) return;
1653 ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
1654 Representation new_rep = TryChange(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001655 if (!new_rep.IsNone()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001656 if (!value->representation().Equals(new_rep)) {
1657 value->ChangeRepresentation(new_rep);
1658 AddDependantsToWorklist(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001659 }
1660 }
1661}
1662
1663
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001664Representation HInferRepresentation::TryChange(HValue* value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001665 // Array of use counts for each representation.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001666 int use_count[Representation::kNumRepresentations] = { 0 };
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001667
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001668 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1669 HValue* use = it.value();
1670 Representation rep = use->RequiredInputRepresentation(it.index());
1671 if (rep.IsNone()) continue;
1672 if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00001673 use_count[rep.kind()] += use->LoopWeight();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001674 }
1675 int tagged_count = use_count[Representation::kTagged];
1676 int double_count = use_count[Representation::kDouble];
1677 int int32_count = use_count[Representation::kInteger32];
1678 int non_tagged_count = double_count + int32_count;
1679
1680 // If a non-loop phi has tagged uses, don't convert it to untagged.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001681 if (value->IsPhi() && !value->block()->IsLoopHeader() && tagged_count > 0) {
1682 return Representation::None();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001683 }
1684
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001685 // Prefer unboxing over boxing, the latter is more expensive.
ricow@chromium.org27bf2882011-11-17 08:34:43 +00001686 if (tagged_count > non_tagged_count) return Representation::None();
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001687
1688 // Prefer Integer32 over Double, if possible.
1689 if (int32_count > 0 && value->IsConvertibleToInteger()) {
1690 return Representation::Integer32();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001691 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001692
1693 if (double_count > 0) return Representation::Double();
1694
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001695 return Representation::None();
1696}
1697
1698
1699void HInferRepresentation::Analyze() {
1700 HPhase phase("Infer representations", graph_);
1701
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001702 // (1) Initialize bit vectors and count real uses. Each phi gets a
1703 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001704 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001705 int phi_count = phi_list->length();
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001706 ZoneList<BitVector*> connected_phis(phi_count);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001707 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001708 phi_list->at(i)->InitRealUses(i);
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001709 BitVector* connected_set = new(zone()) BitVector(phi_count);
1710 connected_set->Add(i);
1711 connected_phis.Add(connected_set);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001712 }
1713
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001714 // (2) Do a fixed point iteration to find the set of connected phis. A
1715 // phi is connected to another phi if its value is used either directly or
1716 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001717 bool change = true;
1718 while (change) {
1719 change = false;
ricow@chromium.org55ee8072011-09-08 16:33:10 +00001720 // We normally have far more "forward edges" than "backward edges",
1721 // so we terminate faster when we walk backwards.
1722 for (int i = phi_count - 1; i >= 0; --i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001723 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001724 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1725 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001726 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001727 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00001728 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
1729 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001730 }
1731 }
1732 }
1733 }
1734
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001735 // (3) Use the phi reachability information from step 2 to
1736 // (a) sum up the non-phi use counts of all connected phis.
1737 // (b) push information about values which can't be converted to integer
1738 // without deoptimization through the phi use-def chains, avoiding
1739 // unnecessary deoptimizations later.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001740 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001741 HPhi* phi = phi_list->at(i);
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001742 bool cti = phi->AllOperandsConvertibleToInteger();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001743 for (BitVector::Iterator it(connected_phis.at(i));
1744 !it.Done();
1745 it.Advance()) {
1746 int index = it.Current();
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001747 HPhi* it_use = phi_list->at(it.Current());
1748 if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice!
1749 if (!cti) it_use->set_is_convertible_to_integer(false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001750 }
1751 }
1752
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001753 // Initialize work list
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001754 for (int i = 0; i < graph_->blocks()->length(); ++i) {
1755 HBasicBlock* block = graph_->blocks()->at(i);
1756 const ZoneList<HPhi*>* phis = block->phis();
1757 for (int j = 0; j < phis->length(); ++j) {
1758 AddToWorklist(phis->at(j));
1759 }
1760
1761 HInstruction* current = block->first();
1762 while (current != NULL) {
1763 AddToWorklist(current);
1764 current = current->next();
1765 }
1766 }
1767
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001768 // Do a fixed point iteration, trying to improve representations
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001769 while (!worklist_.is_empty()) {
1770 HValue* current = worklist_.RemoveLast();
1771 in_worklist_.Remove(current->id());
1772 InferBasedOnInputs(current);
1773 InferBasedOnUses(current);
1774 }
1775}
1776
1777
1778void HGraph::InitializeInferredTypes() {
1779 HPhase phase("Inferring types", this);
1780 InitializeInferredTypes(0, this->blocks_.length() - 1);
1781}
1782
1783
1784void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
1785 for (int i = from_inclusive; i <= to_inclusive; ++i) {
1786 HBasicBlock* block = blocks_[i];
1787
1788 const ZoneList<HPhi*>* phis = block->phis();
1789 for (int j = 0; j < phis->length(); j++) {
1790 phis->at(j)->UpdateInferredType();
1791 }
1792
1793 HInstruction* current = block->first();
1794 while (current != NULL) {
1795 current->UpdateInferredType();
1796 current = current->next();
1797 }
1798
1799 if (block->IsLoopHeader()) {
1800 HBasicBlock* last_back_edge =
1801 block->loop_information()->GetLastBackEdge();
1802 InitializeInferredTypes(i + 1, last_back_edge->block_id());
1803 // Skip all blocks already processed by the recursive call.
1804 i = last_back_edge->block_id();
1805 // Update phis of the loop header now after the whole loop body is
1806 // guaranteed to be processed.
1807 ZoneList<HValue*> worklist(block->phis()->length());
1808 for (int j = 0; j < block->phis()->length(); ++j) {
1809 worklist.Add(block->phis()->at(j));
1810 }
1811 InferTypes(&worklist);
1812 }
1813 }
1814}
1815
1816
1817void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
1818 HValue* current = value;
1819 while (current != NULL) {
1820 if (visited->Contains(current->id())) return;
1821
1822 // For phis, we must propagate the check to all of its inputs.
1823 if (current->IsPhi()) {
1824 visited->Add(current->id());
1825 HPhi* phi = HPhi::cast(current);
1826 for (int i = 0; i < phi->OperandCount(); ++i) {
1827 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
1828 }
1829 break;
1830 }
1831
1832 // For multiplication and division, we must propagate to the left and
1833 // the right side.
1834 if (current->IsMul()) {
1835 HMul* mul = HMul::cast(current);
1836 mul->EnsureAndPropagateNotMinusZero(visited);
1837 PropagateMinusZeroChecks(mul->left(), visited);
1838 PropagateMinusZeroChecks(mul->right(), visited);
1839 } else if (current->IsDiv()) {
1840 HDiv* div = HDiv::cast(current);
1841 div->EnsureAndPropagateNotMinusZero(visited);
1842 PropagateMinusZeroChecks(div->left(), visited);
1843 PropagateMinusZeroChecks(div->right(), visited);
1844 }
1845
1846 current = current->EnsureAndPropagateNotMinusZero(visited);
1847 }
1848}
1849
1850
1851void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001852 HValue* use_value,
1853 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001854 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001855 // Insert the representation change right before its use. For phi-uses we
1856 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001857 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001858 if (use_value->IsPhi()) {
1859 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001860 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001861 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001862 }
1863
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001864 // For constants we try to make the representation change at compile
1865 // time. When a representation change is not possible without loss of
1866 // information we treat constants like normal instructions and insert the
1867 // change instructions for them.
1868 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001869 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001870 bool deoptimize_on_undefined =
1871 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001872 if (value->IsConstant()) {
1873 HConstant* constant = HConstant::cast(value);
1874 // Try to create a new copy of the constant with the new representation.
1875 new_value = is_truncating
1876 ? constant->CopyToTruncatedInt32()
1877 : constant->CopyToRepresentation(to);
1878 }
1879
1880 if (new_value == NULL) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001881 new_value = new(zone()) HChange(value, to,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001882 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001883 }
1884
1885 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001886 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001887}
1888
1889
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001890void HGraph::InsertRepresentationChangesForValue(HValue* value) {
1891 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001892 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001893 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001894
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001895 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1896 HValue* use_value = it.value();
1897 int use_index = it.index();
1898 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001899 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001900 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001901 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001902 if (value->HasNoUses()) {
1903 ASSERT(value->IsConstant());
1904 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001905 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00001906
1907 // The only purpose of a HForceRepresentation is to represent the value
1908 // after the (possible) HChange instruction. We make it disappear.
1909 if (value->IsForceRepresentation()) {
1910 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
1911 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001912}
1913
1914
1915void HGraph::InsertRepresentationChanges() {
1916 HPhase phase("Insert representation changes", this);
1917
1918
1919 // Compute truncation flag for phis: Initially assume that all
1920 // int32-phis allow truncation and iteratively remove the ones that
1921 // are used in an operation that does not allow a truncating
1922 // conversion.
1923 // TODO(fschneider): Replace this with a worklist-based iteration.
1924 for (int i = 0; i < phi_list()->length(); i++) {
1925 HPhi* phi = phi_list()->at(i);
1926 if (phi->representation().IsInteger32()) {
1927 phi->SetFlag(HValue::kTruncatingToInt32);
1928 }
1929 }
1930 bool change = true;
1931 while (change) {
1932 change = false;
1933 for (int i = 0; i < phi_list()->length(); i++) {
1934 HPhi* phi = phi_list()->at(i);
1935 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001936 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1937 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001938 if (!use->CheckFlag(HValue::kTruncatingToInt32)) {
1939 phi->ClearFlag(HValue::kTruncatingToInt32);
1940 change = true;
1941 break;
1942 }
1943 }
1944 }
1945 }
1946
1947 for (int i = 0; i < blocks_.length(); ++i) {
1948 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001949 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
1950 for (int j = 0; j < phis->length(); j++) {
1951 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001952 }
1953
1954 // Process normal instructions.
1955 HInstruction* current = blocks_[i]->first();
1956 while (current != NULL) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001957 InsertRepresentationChangesForValue(current);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001958 current = current->next();
1959 }
1960 }
1961}
1962
1963
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001964void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
1965 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
1966 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
1967 for (int i = 0; i < phi->OperandCount(); ++i) {
1968 HValue* input = phi->OperandAt(i);
1969 if (input->IsPhi()) {
1970 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
1971 }
1972 }
1973}
1974
1975
1976void HGraph::MarkDeoptimizeOnUndefined() {
1977 HPhase phase("MarkDeoptimizeOnUndefined", this);
1978 // Compute DeoptimizeOnUndefined flag for phis.
1979 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001980 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001981 // double input representation, has this flag set.
1982 // The flag is used by HChange tagged->double, which must deoptimize
1983 // if one of its uses has this flag set.
1984 for (int i = 0; i < phi_list()->length(); i++) {
1985 HPhi* phi = phi_list()->at(i);
1986 if (phi->representation().IsDouble()) {
1987 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1988 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
1989 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
1990 break;
1991 }
1992 }
1993 }
1994 }
1995}
1996
1997
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001998void HGraph::ComputeMinusZeroChecks() {
1999 BitVector visited(GetMaximumValueID());
2000 for (int i = 0; i < blocks_.length(); ++i) {
2001 for (HInstruction* current = blocks_[i]->first();
2002 current != NULL;
2003 current = current->next()) {
2004 if (current->IsChange()) {
2005 HChange* change = HChange::cast(current);
2006 // Propagate flags for negative zero checks upwards from conversions
2007 // int32-to-tagged and int32-to-double.
2008 Representation from = change->value()->representation();
2009 ASSERT(from.Equals(change->from()));
2010 if (from.IsInteger32()) {
2011 ASSERT(change->to().IsTagged() || change->to().IsDouble());
2012 ASSERT(visited.IsEmpty());
2013 PropagateMinusZeroChecks(change->value(), &visited);
2014 visited.Clear();
2015 }
2016 }
2017 }
2018 }
2019}
2020
2021
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002022// Implementation of utility class to encapsulate the translation state for
2023// a (possibly inlined) function.
2024FunctionState::FunctionState(HGraphBuilder* owner,
2025 CompilationInfo* info,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002026 TypeFeedbackOracle* oracle,
2027 bool drop_extra)
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002028 : owner_(owner),
2029 compilation_info_(info),
2030 oracle_(oracle),
2031 call_context_(NULL),
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002032 drop_extra_(drop_extra),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002033 function_return_(NULL),
2034 test_context_(NULL),
2035 outer_(owner->function_state()) {
2036 if (outer_ != NULL) {
2037 // State for an inline function.
2038 if (owner->ast_context()->IsTest()) {
2039 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
2040 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
2041 if_true->MarkAsInlineReturnTarget();
2042 if_false->MarkAsInlineReturnTarget();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002043 Expression* cond = TestContext::cast(owner->ast_context())->condition();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002044 // The AstContext constructor pushed on the context stack. This newed
2045 // instance is the reason that AstContext can't be BASE_EMBEDDED.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002046 test_context_ = new TestContext(owner, cond, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002047 } else {
2048 function_return_ = owner->graph()->CreateBasicBlock();
2049 function_return()->MarkAsInlineReturnTarget();
2050 }
2051 // Set this after possibly allocating a new TestContext above.
2052 call_context_ = owner->ast_context();
2053 }
2054
2055 // Push on the state stack.
2056 owner->set_function_state(this);
2057}
2058
2059
2060FunctionState::~FunctionState() {
2061 delete test_context_;
2062 owner_->set_function_state(outer_);
2063}
2064
2065
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002066// Implementation of utility classes to represent an expression's context in
2067// the AST.
2068AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002069 : owner_(owner),
2070 kind_(kind),
2071 outer_(owner->ast_context()),
2072 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002073 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002074#ifdef DEBUG
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002075 ASSERT(!owner->environment()->is_arguments_adaptor());
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002076 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002077#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002078}
2079
2080
2081AstContext::~AstContext() {
2082 owner_->set_ast_context(outer_); // Pop.
2083}
2084
2085
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002086EffectContext::~EffectContext() {
2087 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002088 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002089 (owner()->environment()->length() == original_length_ &&
2090 !owner()->environment()->is_arguments_adaptor()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002091}
2092
2093
2094ValueContext::~ValueContext() {
2095 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002096 owner()->current_block() == NULL ||
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00002097 (owner()->environment()->length() == original_length_ + 1 &&
2098 !owner()->environment()->is_arguments_adaptor()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002099}
2100
2101
2102void EffectContext::ReturnValue(HValue* value) {
2103 // The value is simply ignored.
2104}
2105
2106
2107void ValueContext::ReturnValue(HValue* value) {
2108 // The value is tracked in the bailout environment, and communicated
2109 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002110 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
2111 owner()->Bailout("bad value context for arguments value");
2112 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002113 owner()->Push(value);
2114}
2115
2116
2117void TestContext::ReturnValue(HValue* value) {
2118 BuildBranch(value);
2119}
2120
2121
2122void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002123 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002124 owner()->AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002125 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002126}
2127
2128
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002129void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002130 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002131 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2132 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2133 instr->SetSuccessorAt(0, empty_true);
2134 instr->SetSuccessorAt(1, empty_false);
2135 owner()->current_block()->Finish(instr);
2136 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
2137 owner()->set_current_block(join);
2138}
2139
2140
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002141void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002142 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002143 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002144 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002145 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002146 owner()->AddInstruction(instr);
2147 owner()->Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002148 if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002149}
2150
2151
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002152void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002153 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002154 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
2155 return owner()->Bailout("bad value context for arguments object value");
2156 }
2157 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
2158 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
2159 instr->SetSuccessorAt(0, materialize_true);
2160 instr->SetSuccessorAt(1, materialize_false);
2161 owner()->current_block()->Finish(instr);
2162 owner()->set_current_block(materialize_true);
2163 owner()->Push(owner()->graph()->GetConstantTrue());
2164 owner()->set_current_block(materialize_false);
2165 owner()->Push(owner()->graph()->GetConstantFalse());
2166 HBasicBlock* join =
2167 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
2168 owner()->set_current_block(join);
2169}
2170
2171
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002172void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002173 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002174 HGraphBuilder* builder = owner();
2175 builder->AddInstruction(instr);
2176 // We expect a simulate after every expression with side effects, though
2177 // this one isn't actually needed (and wouldn't work if it were targeted).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002178 if (instr->HasObservableSideEffects()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002179 builder->Push(instr);
2180 builder->AddSimulate(ast_id);
2181 builder->Pop();
2182 }
2183 BuildBranch(instr);
2184}
2185
2186
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002187void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002188 ASSERT(!instr->HasObservableSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002189 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2190 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2191 instr->SetSuccessorAt(0, empty_true);
2192 instr->SetSuccessorAt(1, empty_false);
2193 owner()->current_block()->Finish(instr);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002194 empty_true->Goto(if_true(), owner()->function_state()->drop_extra());
2195 empty_false->Goto(if_false(), owner()->function_state()->drop_extra());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002196 owner()->set_current_block(NULL);
2197}
2198
2199
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002200void TestContext::BuildBranch(HValue* value) {
2201 // We expect the graph to be in edge-split form: there is no edge that
2202 // connects a branch node to a join node. We conservatively ensure that
2203 // property by always adding an empty block on the outgoing edges of this
2204 // branch.
2205 HGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002206 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002207 builder->Bailout("arguments object value in a test context");
2208 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002209 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
2210 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002211 unsigned test_id = condition()->test_id();
2212 ToBooleanStub::Types expected(builder->oracle()->ToBooleanTypes(test_id));
2213 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002214 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002215
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002216 empty_true->Goto(if_true(), owner()->function_state()->drop_extra());
2217 empty_false->Goto(if_false(), owner()->function_state()->drop_extra());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002218 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002219}
2220
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002221
2222// HGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002223#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002224 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002225 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002226 if (HasStackOverflow()) return; \
2227 } while (false)
2228
2229
danno@chromium.org160a7b02011-04-18 15:51:38 +00002230#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002231 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002232 call; \
2233 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002234 } while (false)
2235
2236
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002237void HGraphBuilder::Bailout(const char* reason) {
2238 if (FLAG_trace_bailout) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002239 SmartArrayPointer<char> name(
2240 info()->shared_info()->DebugName()->ToCString());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002241 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002242 }
2243 SetStackOverflow();
2244}
2245
2246
2247void HGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002248 EffectContext for_effect(this);
2249 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002250}
2251
2252
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002253void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
2254 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002255 Visit(expr);
2256}
2257
2258
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002259void HGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002260 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002261 for_value.set_for_typeof(true);
2262 Visit(expr);
2263}
2264
2265
2266
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002267void HGraphBuilder::VisitForControl(Expression* expr,
2268 HBasicBlock* true_block,
2269 HBasicBlock* false_block) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002270 TestContext for_test(this, expr, true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002271 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002272}
2273
2274
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00002275HValue* HGraphBuilder::VisitArgument(Expression* expr) {
2276 VisitForValue(expr);
2277 if (HasStackOverflow() || current_block() == NULL) return NULL;
2278 HValue* value = Pop();
2279 Push(AddInstruction(new(zone()) HPushArgument(value)));
2280 return value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002281}
2282
2283
2284void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
2285 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002286 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002287 }
2288}
2289
2290
2291void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
2292 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002293 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002294 }
2295}
2296
2297
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002298HGraph* HGraphBuilder::CreateGraph() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002299 graph_ = new(zone()) HGraph(info());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002300 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
2301
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002302 {
2303 HPhase phase("Block building");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002304 current_block_ = graph()->entry_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002305
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002306 Scope* scope = info()->scope();
2307 if (scope->HasIllegalRedeclaration()) {
2308 Bailout("function with illegal redeclaration");
2309 return NULL;
2310 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00002311 SetUpScope(scope);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002312
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002313 // Add an edge to the body entry. This is warty: the graph's start
2314 // environment will be used by the Lithium translation as the initial
2315 // environment on graph entry, but it has now been mutated by the
2316 // Hydrogen translation of the instructions in the start block. This
2317 // environment uses values which have not been defined yet. These
2318 // Hydrogen instructions will then be replayed by the Lithium
2319 // translation, so they cannot have an environment effect. The edge to
2320 // the body's entry block (along with some special logic for the start
2321 // block in HInstruction::InsertAfter) seals the start block from
2322 // getting unwanted instructions inserted.
2323 //
2324 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
2325 // Make the Hydrogen instructions in the initial block into Hydrogen
2326 // values (but not instructions), present in the initial environment and
2327 // not replayed by the Lithium translation.
2328 HEnvironment* initial_env = environment()->CopyWithoutHistory();
2329 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
2330 current_block()->Goto(body_entry);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002331 body_entry->SetJoinId(AstNode::kFunctionEntryId);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002332 set_current_block(body_entry);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00002333
2334 // Handle implicit declaration of the function name in named function
2335 // expressions before other declarations.
2336 if (scope->is_function_scope() && scope->function() != NULL) {
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00002337 HandleDeclaration(scope->function(), CONST, NULL);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00002338 }
2339 VisitDeclarations(scope->declarations());
2340 AddSimulate(AstNode::kDeclarationsId);
2341
2342 HValue* context = environment()->LookupContext();
2343 AddInstruction(
2344 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
2345
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002346 VisitStatements(info()->function()->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002347 if (HasStackOverflow()) return NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002348
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002349 if (current_block() != NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002350 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002351 current_block()->FinishExit(instr);
2352 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002353 }
2354 }
2355
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002356 graph()->OrderBlocks();
2357 graph()->AssignDominators();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002358
2359#ifdef DEBUG
2360 // Do a full verify after building the graph and computing dominators.
2361 graph()->Verify(true);
2362#endif
2363
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002364 graph()->PropagateDeoptimizingMark();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002365 if (!graph()->CheckConstPhiUses()) {
2366 Bailout("Unsupported phi use of const variable");
2367 return NULL;
2368 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002369 graph()->EliminateRedundantPhis();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002370 if (!graph()->CheckArgumentsPhiUses()) {
2371 Bailout("Unsupported phi use of arguments");
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00002372 return NULL;
2373 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002374 if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002375 graph()->CollectPhis();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002376
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002377 HInferRepresentation rep(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002378 rep.Analyze();
2379
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002380 graph()->MarkDeoptimizeOnUndefined();
2381 graph()->InsertRepresentationChanges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002382
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002383 graph()->InitializeInferredTypes();
2384 graph()->Canonicalize();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002385
2386 // Perform common subexpression elimination and loop-invariant code motion.
2387 if (FLAG_use_gvn) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002388 HPhase phase("Global value numbering", graph());
2389 HGlobalValueNumberer gvn(graph(), info());
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002390 bool removed_side_effects = gvn.Analyze();
2391 // Trigger a second analysis pass to further eliminate duplicate values that
2392 // could only be discovered by removing side-effect-generating instructions
2393 // during the first pass.
2394 if (FLAG_smi_only_arrays && removed_side_effects) {
2395 gvn.Analyze();
2396 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002397 }
2398
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002399 if (FLAG_use_range) {
2400 HRangeAnalysis rangeAnalysis(graph());
2401 rangeAnalysis.Analyze();
2402 }
2403 graph()->ComputeMinusZeroChecks();
2404
2405 // Eliminate redundant stack checks on backwards branches.
2406 HStackCheckEliminator sce(graph());
2407 sce.Process();
2408
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002409 // Replace the results of check instructions with the original value, if the
2410 // result is used. This is safe now, since we don't do code motion after this
2411 // point. It enables better register allocation since the value produced by
2412 // check instructions is really a copy of the original value.
2413 graph()->ReplaceCheckedValues();
2414
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002415 return graph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002416}
2417
2418
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002419void HGraph::ReplaceCheckedValues() {
2420 HPhase phase("Replace checked values", this);
2421 for (int i = 0; i < blocks()->length(); ++i) {
2422 HInstruction* instr = blocks()->at(i)->first();
2423 while (instr != NULL) {
2424 if (instr->IsBoundsCheck()) {
2425 // Replace all uses of the checked value with the original input.
2426 ASSERT(instr->UseCount() > 0);
2427 instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index());
2428 }
2429 instr = instr->next();
2430 }
2431 }
2432}
2433
2434
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002435HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002436 ASSERT(current_block() != NULL);
2437 current_block()->AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002438 return instr;
2439}
2440
2441
ager@chromium.org04921a82011-06-27 13:21:41 +00002442void HGraphBuilder::AddSimulate(int ast_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002443 ASSERT(current_block() != NULL);
ager@chromium.org04921a82011-06-27 13:21:41 +00002444 current_block()->AddSimulate(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002445}
2446
2447
2448void HGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002449 ASSERT(current_block() != NULL);
2450 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002451}
2452
2453
2454void HGraphBuilder::PushAndAdd(HInstruction* instr) {
2455 Push(instr);
2456 AddInstruction(instr);
2457}
2458
2459
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002460template <int V>
2461HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002462 int count = call->argument_count();
2463 ZoneList<HValue*> arguments(count);
2464 for (int i = 0; i < count; ++i) {
2465 arguments.Add(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002466 }
2467
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002468 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002469 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002470 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002471 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002472}
2473
2474
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00002475void HGraphBuilder::SetUpScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002476 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002477 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002478 AddInstruction(undefined_constant);
2479 graph_->set_undefined_constant(undefined_constant);
2480
2481 // Set the initial values of parameters including "this". "This" has
2482 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002483 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
2484
2485 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002486 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002487 environment()->Bind(i, parameter);
2488 }
2489
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002490 // First special is HContext.
2491 HInstruction* context = AddInstruction(new(zone()) HContext);
2492 environment()->BindContext(context);
2493
2494 // Initialize specials and locals to undefined.
2495 for (int i = environment()->parameter_count() + 1;
2496 i < environment()->length();
2497 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002498 environment()->Bind(i, undefined_constant);
2499 }
2500
2501 // Handle the arguments and arguments shadow variables specially (they do
2502 // not have declarations).
2503 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00002504 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002505 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002506 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002507 HArgumentsObject* object = new(zone()) HArgumentsObject;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002508 AddInstruction(object);
2509 graph()->SetArgumentsObject(object);
2510 environment()->Bind(scope->arguments(), object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002511 }
2512}
2513
2514
2515void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
2516 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002517 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002518 }
2519}
2520
2521
2522HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
2523 HBasicBlock* b = graph()->CreateBasicBlock();
2524 b->SetInitialEnvironment(env);
2525 return b;
2526}
2527
2528
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002529HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002530 HBasicBlock* header = graph()->CreateBasicBlock();
2531 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
2532 header->SetInitialEnvironment(entry_env);
2533 header->AttachLoopInformation();
2534 return header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002535}
2536
2537
2538void HGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002539 ASSERT(!HasStackOverflow());
2540 ASSERT(current_block() != NULL);
2541 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002542 if (stmt->block_scope() != NULL) {
2543 return Bailout("ScopedBlock");
2544 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002545 BreakAndContinueInfo break_info(stmt);
2546 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002547 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002548 }
2549 HBasicBlock* break_block = break_info.break_block();
2550 if (break_block != NULL) {
2551 if (current_block() != NULL) current_block()->Goto(break_block);
2552 break_block->SetJoinId(stmt->ExitId());
2553 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002554 }
2555}
2556
2557
2558void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002559 ASSERT(!HasStackOverflow());
2560 ASSERT(current_block() != NULL);
2561 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002562 VisitForEffect(stmt->expression());
2563}
2564
2565
2566void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002567 ASSERT(!HasStackOverflow());
2568 ASSERT(current_block() != NULL);
2569 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002570}
2571
2572
2573void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002574 ASSERT(!HasStackOverflow());
2575 ASSERT(current_block() != NULL);
2576 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002577 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002578 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002579 Visit(stmt->then_statement());
2580 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002581 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002582 Visit(stmt->else_statement());
2583 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002584 HBasicBlock* cond_true = graph()->CreateBasicBlock();
2585 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002586 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002587
danno@chromium.org160a7b02011-04-18 15:51:38 +00002588 if (cond_true->HasPredecessor()) {
2589 cond_true->SetJoinId(stmt->ThenId());
2590 set_current_block(cond_true);
2591 CHECK_BAILOUT(Visit(stmt->then_statement()));
2592 cond_true = current_block();
2593 } else {
2594 cond_true = NULL;
2595 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002596
danno@chromium.org160a7b02011-04-18 15:51:38 +00002597 if (cond_false->HasPredecessor()) {
2598 cond_false->SetJoinId(stmt->ElseId());
2599 set_current_block(cond_false);
2600 CHECK_BAILOUT(Visit(stmt->else_statement()));
2601 cond_false = current_block();
2602 } else {
2603 cond_false = NULL;
2604 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002605
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002606 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002607 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002608 }
2609}
2610
2611
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002612HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
2613 BreakableStatement* stmt,
2614 BreakType type) {
2615 BreakAndContinueScope* current = this;
2616 while (current != NULL && current->info()->target() != stmt) {
2617 current = current->next();
2618 }
2619 ASSERT(current != NULL); // Always found (unless stack is malformed).
2620 HBasicBlock* block = NULL;
2621 switch (type) {
2622 case BREAK:
2623 block = current->info()->break_block();
2624 if (block == NULL) {
2625 block = current->owner()->graph()->CreateBasicBlock();
2626 current->info()->set_break_block(block);
2627 }
2628 break;
2629
2630 case CONTINUE:
2631 block = current->info()->continue_block();
2632 if (block == NULL) {
2633 block = current->owner()->graph()->CreateBasicBlock();
2634 current->info()->set_continue_block(block);
2635 }
2636 break;
2637 }
2638
2639 return block;
2640}
2641
2642
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002643void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002644 ASSERT(!HasStackOverflow());
2645 ASSERT(current_block() != NULL);
2646 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002647 HBasicBlock* continue_block = break_scope()->Get(stmt->target(), CONTINUE);
2648 current_block()->Goto(continue_block);
2649 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002650}
2651
2652
2653void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002654 ASSERT(!HasStackOverflow());
2655 ASSERT(current_block() != NULL);
2656 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002657 HBasicBlock* break_block = break_scope()->Get(stmt->target(), BREAK);
2658 current_block()->Goto(break_block);
2659 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002660}
2661
2662
2663void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002664 ASSERT(!HasStackOverflow());
2665 ASSERT(current_block() != NULL);
2666 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002667 AstContext* context = call_context();
2668 if (context == NULL) {
2669 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002670 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002671 HValue* result = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002672 current_block()->FinishExit(new(zone()) HReturn(result));
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002673 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002674 } else {
2675 // Return from an inlined function, visit the subexpression in the
2676 // expression context of the call.
2677 if (context->IsTest()) {
2678 TestContext* test = TestContext::cast(context);
2679 VisitForControl(stmt->expression(),
2680 test->if_true(),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002681 test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002682 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002683 CHECK_ALIVE(VisitForEffect(stmt->expression()));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002684 current_block()->Goto(function_return(), function_state()->drop_extra());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002685 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002686 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00002687 CHECK_ALIVE(VisitForValue(stmt->expression()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002688 HValue* return_value = environment()->Pop();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002689 current_block()->AddLeaveInlined(return_value,
2690 function_return(),
2691 function_state()->drop_extra());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002692 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002693 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002694 }
2695}
2696
2697
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002698void HGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002699 ASSERT(!HasStackOverflow());
2700 ASSERT(current_block() != NULL);
2701 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002702 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002703}
2704
2705
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002706void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002707 ASSERT(!HasStackOverflow());
2708 ASSERT(current_block() != NULL);
2709 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002710 // We only optimize switch statements with smi-literal smi comparisons,
2711 // with a bounded number of clauses.
2712 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002713 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002714 int clause_count = clauses->length();
2715 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002716 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002717 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002718
erikcorry0ad885c2011-11-21 13:51:57 +00002719 HValue* context = environment()->LookupContext();
2720
danno@chromium.org160a7b02011-04-18 15:51:38 +00002721 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002722 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002723 HValue* tag_value = Pop();
2724 HBasicBlock* first_test_block = current_block();
2725
erikcorry0ad885c2011-11-21 13:51:57 +00002726 SwitchType switch_type = UNKNOWN_SWITCH;
2727
2728 // 1. Extract clause type
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002729 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002730 CaseClause* clause = clauses->at(i);
2731 if (clause->is_default()) continue;
erikcorry0ad885c2011-11-21 13:51:57 +00002732
2733 if (switch_type == UNKNOWN_SWITCH) {
2734 if (clause->label()->IsSmiLiteral()) {
2735 switch_type = SMI_SWITCH;
2736 } else if (clause->label()->IsStringLiteral()) {
2737 switch_type = STRING_SWITCH;
2738 } else {
2739 return Bailout("SwitchStatement: non-literal switch label");
2740 }
2741 } else if ((switch_type == STRING_SWITCH &&
2742 !clause->label()->IsStringLiteral()) ||
2743 (switch_type == SMI_SWITCH &&
2744 !clause->label()->IsSmiLiteral())) {
2745 return Bailout("SwitchStatemnt: mixed label types are not supported");
2746 }
2747 }
2748
2749 HUnaryControlInstruction* string_check = NULL;
2750 HBasicBlock* not_string_block = NULL;
2751
2752 // Test switch's tag value if all clauses are string literals
2753 if (switch_type == STRING_SWITCH) {
2754 string_check = new(zone()) HIsStringAndBranch(tag_value);
2755 first_test_block = graph()->CreateBasicBlock();
2756 not_string_block = graph()->CreateBasicBlock();
2757
2758 string_check->SetSuccessorAt(0, first_test_block);
2759 string_check->SetSuccessorAt(1, not_string_block);
2760 current_block()->Finish(string_check);
2761
2762 set_current_block(first_test_block);
2763 }
2764
2765 // 2. Build all the tests, with dangling true branches
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002766 int default_id = AstNode::kNoNumber;
erikcorry0ad885c2011-11-21 13:51:57 +00002767 for (int i = 0; i < clause_count; ++i) {
2768 CaseClause* clause = clauses->at(i);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002769 if (clause->is_default()) {
2770 default_id = clause->EntryId();
2771 continue;
2772 }
erikcorry0ad885c2011-11-21 13:51:57 +00002773 if (switch_type == SMI_SWITCH) {
2774 clause->RecordTypeFeedback(oracle());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002775 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002776
erikcorry0ad885c2011-11-21 13:51:57 +00002777 // Generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002778 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002779 HValue* label_value = Pop();
erikcorry0ad885c2011-11-21 13:51:57 +00002780
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002781 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
erikcorry0ad885c2011-11-21 13:51:57 +00002782 HBasicBlock* body_block = graph()->CreateBasicBlock();
2783
2784 HControlInstruction* compare;
2785
2786 if (switch_type == SMI_SWITCH) {
2787 if (!clause->IsSmiCompare()) {
2788 // Finish with deoptimize and add uses of enviroment values to
2789 // account for invisible uses.
2790 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
2791 set_current_block(NULL);
2792 break;
2793 }
2794
2795 HCompareIDAndBranch* compare_ =
2796 new(zone()) HCompareIDAndBranch(tag_value,
2797 label_value,
2798 Token::EQ_STRICT);
2799 compare_->SetInputRepresentation(Representation::Integer32());
2800 compare = compare_;
2801 } else {
2802 compare = new(zone()) HStringCompareAndBranch(context, tag_value,
2803 label_value,
2804 Token::EQ_STRICT);
2805 }
2806
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002807 compare->SetSuccessorAt(0, body_block);
2808 compare->SetSuccessorAt(1, next_test_block);
2809 current_block()->Finish(compare);
erikcorry0ad885c2011-11-21 13:51:57 +00002810
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002811 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002812 }
2813
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002814 // Save the current block to use for the default or to join with the
2815 // exit. This block is NULL if we deoptimized.
2816 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002817
erikcorry0ad885c2011-11-21 13:51:57 +00002818 if (not_string_block != NULL) {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002819 int join_id = (default_id != AstNode::kNoNumber)
2820 ? default_id
2821 : stmt->ExitId();
2822 last_block = CreateJoin(last_block, not_string_block, join_id);
erikcorry0ad885c2011-11-21 13:51:57 +00002823 }
2824
2825 // 3. Loop over the clauses and the linked list of tests in lockstep,
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002826 // translating the clause bodies.
2827 HBasicBlock* curr_test_block = first_test_block;
2828 HBasicBlock* fall_through_block = NULL;
erikcorry0ad885c2011-11-21 13:51:57 +00002829
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002830 BreakAndContinueInfo break_info(stmt);
2831 { BreakAndContinueScope push(&break_info, this);
2832 for (int i = 0; i < clause_count; ++i) {
2833 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002834
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002835 // Identify the block where normal (non-fall-through) control flow
2836 // goes to.
2837 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00002838 if (clause->is_default()) {
2839 if (last_block != NULL) {
2840 normal_block = last_block;
2841 last_block = NULL; // Cleared to indicate we've handled it.
2842 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002843 } else if (!curr_test_block->end()->IsDeoptimize()) {
2844 normal_block = curr_test_block->end()->FirstSuccessor();
2845 curr_test_block = curr_test_block->end()->SecondSuccessor();
2846 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002847
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002848 // Identify a block to emit the body into.
2849 if (normal_block == NULL) {
2850 if (fall_through_block == NULL) {
2851 // (a) Unreachable.
2852 if (clause->is_default()) {
2853 continue; // Might still be reachable clause bodies.
2854 } else {
2855 break;
2856 }
2857 } else {
2858 // (b) Reachable only as fall through.
2859 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002860 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002861 } else if (fall_through_block == NULL) {
2862 // (c) Reachable only normally.
2863 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002864 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002865 // (d) Reachable both ways.
2866 HBasicBlock* join = CreateJoin(fall_through_block,
2867 normal_block,
2868 clause->EntryId());
2869 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002870 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002871
danno@chromium.org160a7b02011-04-18 15:51:38 +00002872 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002873 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002874 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002875 }
2876
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002877 // Create an up-to-3-way join. Use the break block if it exists since
2878 // it's already a join block.
2879 HBasicBlock* break_block = break_info.break_block();
2880 if (break_block == NULL) {
2881 set_current_block(CreateJoin(fall_through_block,
2882 last_block,
2883 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002884 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002885 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
2886 if (last_block != NULL) last_block->Goto(break_block);
2887 break_block->SetJoinId(stmt->ExitId());
2888 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002889 }
2890}
2891
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002892
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002893bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002894 return statement->OsrEntryId() == info()->osr_ast_id();
2895}
2896
2897
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002898void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002899 if (!HasOsrEntryAt(statement)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002900
2901 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
2902 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
2903 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002904 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002905 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002906
2907 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
2908 non_osr_entry->Goto(loop_predecessor);
2909
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002910 set_current_block(osr_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002911 int osr_entry_id = statement->OsrEntryId();
2912 // We want the correct environment at the OsrEntry instruction. Build
2913 // it explicitly. The expression stack should be empty.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002914 ASSERT(environment()->ExpressionStackIsEmpty());
2915 for (int i = 0; i < environment()->length(); ++i) {
2916 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
2917 AddInstruction(osr_value);
2918 environment()->Bind(i, osr_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002919 }
2920
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002921 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002922 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002923 HContext* context = new(zone()) HContext;
2924 AddInstruction(context);
2925 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002926 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002927 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002928 set_current_block(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002929}
2930
2931
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002932void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
ager@chromium.org04921a82011-06-27 13:21:41 +00002933 HBasicBlock* loop_entry,
2934 BreakAndContinueInfo* break_info) {
2935 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002936 AddSimulate(stmt->StackCheckId());
2937 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00002938 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002939 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00002940 AddInstruction(stack_check);
2941 ASSERT(loop_entry->IsLoopHeader());
2942 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002943 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00002944}
2945
2946
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002947void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002948 ASSERT(!HasStackOverflow());
2949 ASSERT(current_block() != NULL);
2950 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002951 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002952 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002953 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00002954 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002955 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002956
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002957 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002958 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002959 HBasicBlock* body_exit =
2960 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002961 HBasicBlock* loop_successor = NULL;
2962 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002963 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002964 // The block for a true condition, the actual predecessor block of the
2965 // back edge.
2966 body_exit = graph()->CreateBasicBlock();
2967 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002968 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
2969 if (body_exit->HasPredecessor()) {
2970 body_exit->SetJoinId(stmt->BackEdgeId());
2971 } else {
2972 body_exit = NULL;
2973 }
2974 if (loop_successor->HasPredecessor()) {
2975 loop_successor->SetJoinId(stmt->ExitId());
2976 } else {
2977 loop_successor = NULL;
2978 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002979 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002980 HBasicBlock* loop_exit = CreateLoop(stmt,
2981 loop_entry,
2982 body_exit,
2983 loop_successor,
2984 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002985 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002986}
2987
2988
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002989void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002990 ASSERT(!HasStackOverflow());
2991 ASSERT(current_block() != NULL);
2992 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002993 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002994 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002995 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00002996 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002997 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002998
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002999 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003000 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003001 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003002 HBasicBlock* body_entry = graph()->CreateBasicBlock();
3003 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003004 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
3005 if (body_entry->HasPredecessor()) {
3006 body_entry->SetJoinId(stmt->BodyId());
3007 set_current_block(body_entry);
3008 }
3009 if (loop_successor->HasPredecessor()) {
3010 loop_successor->SetJoinId(stmt->ExitId());
3011 } else {
3012 loop_successor = NULL;
3013 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003014 }
3015
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003016 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003017 if (current_block() != NULL) {
3018 BreakAndContinueScope push(&break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003019 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003020 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003021 HBasicBlock* body_exit =
3022 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003023 HBasicBlock* loop_exit = CreateLoop(stmt,
3024 loop_entry,
3025 body_exit,
3026 loop_successor,
3027 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003028 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003029}
3030
3031
3032void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003033 ASSERT(!HasStackOverflow());
3034 ASSERT(current_block() != NULL);
3035 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003036 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003037 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003038 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003039 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00003040 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003041 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00003042 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003043 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003044
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003045 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003046 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003047 HBasicBlock* body_entry = graph()->CreateBasicBlock();
3048 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003049 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
3050 if (body_entry->HasPredecessor()) {
3051 body_entry->SetJoinId(stmt->BodyId());
3052 set_current_block(body_entry);
3053 }
3054 if (loop_successor->HasPredecessor()) {
3055 loop_successor->SetJoinId(stmt->ExitId());
3056 } else {
3057 loop_successor = NULL;
3058 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003059 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003060
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003061 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003062 if (current_block() != NULL) {
3063 BreakAndContinueScope push(&break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003064 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003065 }
3066 HBasicBlock* body_exit =
3067 JoinContinue(stmt, current_block(), break_info.continue_block());
3068
3069 if (stmt->next() != NULL && body_exit != NULL) {
3070 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003071 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003072 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003073 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003074
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003075 HBasicBlock* loop_exit = CreateLoop(stmt,
3076 loop_entry,
3077 body_exit,
3078 loop_successor,
3079 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003080 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003081}
3082
3083
3084void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003085 ASSERT(!HasStackOverflow());
3086 ASSERT(current_block() != NULL);
3087 ASSERT(current_block()->HasPredecessor());
3088 return Bailout("ForInStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003089}
3090
3091
3092void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003093 ASSERT(!HasStackOverflow());
3094 ASSERT(current_block() != NULL);
3095 ASSERT(current_block()->HasPredecessor());
3096 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003097}
3098
3099
3100void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003101 ASSERT(!HasStackOverflow());
3102 ASSERT(current_block() != NULL);
3103 ASSERT(current_block()->HasPredecessor());
3104 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003105}
3106
3107
3108void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003109 ASSERT(!HasStackOverflow());
3110 ASSERT(current_block() != NULL);
3111 ASSERT(current_block()->HasPredecessor());
3112 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003113}
3114
3115
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003116static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
3117 Code* unoptimized_code, FunctionLiteral* expr) {
3118 int start_position = expr->start_position();
3119 RelocIterator it(unoptimized_code);
3120 for (;!it.done(); it.next()) {
3121 RelocInfo* rinfo = it.rinfo();
3122 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
3123 Object* obj = rinfo->target_object();
3124 if (obj->IsSharedFunctionInfo()) {
3125 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
3126 if (shared->start_position() == start_position) {
3127 return Handle<SharedFunctionInfo>(shared);
3128 }
3129 }
3130 }
3131
3132 return Handle<SharedFunctionInfo>();
3133}
3134
3135
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003136void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003137 ASSERT(!HasStackOverflow());
3138 ASSERT(current_block() != NULL);
3139 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003140 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003141 SearchSharedFunctionInfo(info()->shared_info()->code(),
3142 expr);
3143 if (shared_info.is_null()) {
3144 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
3145 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00003146 // We also have a stack overflow if the recursive compilation did.
3147 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003148 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003149 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003150 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003151 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003152}
3153
3154
3155void HGraphBuilder::VisitSharedFunctionInfoLiteral(
3156 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003157 ASSERT(!HasStackOverflow());
3158 ASSERT(current_block() != NULL);
3159 ASSERT(current_block()->HasPredecessor());
3160 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003161}
3162
3163
3164void HGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003165 ASSERT(!HasStackOverflow());
3166 ASSERT(current_block() != NULL);
3167 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003168 HBasicBlock* cond_true = graph()->CreateBasicBlock();
3169 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003170 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003171
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003172 // Visit the true and false subexpressions in the same AST context as the
3173 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003174 if (cond_true->HasPredecessor()) {
3175 cond_true->SetJoinId(expr->ThenId());
3176 set_current_block(cond_true);
3177 CHECK_BAILOUT(Visit(expr->then_expression()));
3178 cond_true = current_block();
3179 } else {
3180 cond_true = NULL;
3181 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003182
danno@chromium.org160a7b02011-04-18 15:51:38 +00003183 if (cond_false->HasPredecessor()) {
3184 cond_false->SetJoinId(expr->ElseId());
3185 set_current_block(cond_false);
3186 CHECK_BAILOUT(Visit(expr->else_expression()));
3187 cond_false = current_block();
3188 } else {
3189 cond_false = NULL;
3190 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003191
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003192 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003193 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003194 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003195 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003196 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003197 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003198 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003199}
3200
3201
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003202HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
3203 Variable* var, LookupResult* lookup, bool is_store) {
3204 if (var->is_this() || !info()->has_global_object()) {
3205 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003206 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003207 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003208 global->Lookup(*var->name(), lookup);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003209 if (!lookup->IsFound() ||
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003210 lookup->type() != NORMAL ||
3211 (is_store && lookup->IsReadOnly()) ||
3212 lookup->holder() != *global) {
3213 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003214 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003215
3216 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003217}
3218
3219
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003220HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
3221 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003222 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003223 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003224 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003225 HInstruction* context_instruction = new(zone()) HOuterContext(context);
3226 AddInstruction(context_instruction);
3227 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003228 }
3229 return context;
3230}
3231
3232
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003233void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003234 ASSERT(!HasStackOverflow());
3235 ASSERT(current_block() != NULL);
3236 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003237 Variable* variable = expr->var();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003238 switch (variable->location()) {
3239 case Variable::UNALLOCATED: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003240 if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
3241 return Bailout("reference to global harmony declared variable");
3242 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003243 // Handle known global constants like 'undefined' specially to avoid a
3244 // load from a global cell for them.
3245 Handle<Object> constant_value =
3246 isolate()->factory()->GlobalConstantFor(variable->name());
3247 if (!constant_value.is_null()) {
3248 HConstant* instr =
3249 new(zone()) HConstant(constant_value, Representation::Tagged());
3250 return ast_context()->ReturnInstruction(instr, expr->id());
3251 }
3252
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003253 LookupResult lookup(isolate());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003254 GlobalPropertyAccess type =
3255 LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003256
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003257 if (type == kUseCell &&
3258 info()->global_object()->IsAccessCheckNeeded()) {
3259 type = kUseGeneric;
3260 }
3261
3262 if (type == kUseCell) {
3263 Handle<GlobalObject> global(info()->global_object());
3264 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003265 HLoadGlobalCell* instr =
3266 new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003267 return ast_context()->ReturnInstruction(instr, expr->id());
3268 } else {
3269 HValue* context = environment()->LookupContext();
3270 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
3271 AddInstruction(global_object);
3272 HLoadGlobalGeneric* instr =
3273 new(zone()) HLoadGlobalGeneric(context,
3274 global_object,
3275 variable->name(),
3276 ast_context()->is_for_typeof());
3277 instr->set_position(expr->position());
3278 return ast_context()->ReturnInstruction(instr, expr->id());
3279 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003280 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003281
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003282 case Variable::PARAMETER:
3283 case Variable::LOCAL: {
3284 HValue* value = environment()->Lookup(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003285 if (value == graph()->GetConstantHole()) {
3286 ASSERT(variable->mode() == CONST ||
3287 variable->mode() == CONST_HARMONY ||
3288 variable->mode() == LET);
3289 return Bailout("reference to uninitialized variable");
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003290 }
3291 return ast_context()->ReturnValue(value);
3292 }
3293
3294 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003295 HValue* context = BuildContextChainWalk(variable);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003296 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003297 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003298 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003299
3300 case Variable::LOOKUP:
3301 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003302 }
3303}
3304
3305
3306void HGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003307 ASSERT(!HasStackOverflow());
3308 ASSERT(current_block() != NULL);
3309 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003310 HConstant* instr =
3311 new(zone()) HConstant(expr->handle(), Representation::Tagged());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003312 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003313}
3314
3315
3316void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003317 ASSERT(!HasStackOverflow());
3318 ASSERT(current_block() != NULL);
3319 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003320 HValue* context = environment()->LookupContext();
3321
3322 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
3323 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003324 expr->flags(),
3325 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003326 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003327}
3328
3329
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003330// Determines whether the given object literal boilerplate satisfies all
3331// limits to be considered for fast deep-copying and computes the total
3332// size of all objects that are part of the graph.
3333static bool IsFastObjectLiteral(Handle<JSObject> boilerplate,
3334 int max_depth,
3335 int* max_properties,
3336 int* total_size) {
3337 if (max_depth <= 0) return false;
3338
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003339 Handle<FixedArrayBase> elements(boilerplate->elements());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003340 if (elements->length() > 0 &&
3341 elements->map() != HEAP->fixed_cow_array_map()) {
3342 return false;
3343 }
3344
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003345 Handle<FixedArray> properties(boilerplate->properties());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003346 if (properties->length() > 0) {
3347 return false;
3348 } else {
3349 int nof = boilerplate->map()->inobject_properties();
3350 for (int i = 0; i < nof; i++) {
3351 if ((*max_properties)-- <= 0) return false;
3352 Handle<Object> value(boilerplate->InObjectPropertyAt(i));
3353 if (value->IsJSObject()) {
3354 Handle<JSObject> value_object = Handle<JSObject>::cast(value);
3355 if (!IsFastObjectLiteral(value_object,
3356 max_depth - 1,
3357 max_properties,
3358 total_size)) {
3359 return false;
3360 }
3361 }
3362 }
3363 }
3364
3365 *total_size += boilerplate->map()->instance_size();
3366 return true;
3367}
3368
3369
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003370void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003371 ASSERT(!HasStackOverflow());
3372 ASSERT(current_block() != NULL);
3373 ASSERT(current_block()->HasPredecessor());
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003374 Handle<JSFunction> closure = function_state()->compilation_info()->closure();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003375 HValue* context = environment()->LookupContext();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003376 HInstruction* literal;
3377
3378 // Check whether to use fast or slow deep-copying for boilerplate.
3379 int total_size = 0;
3380 int max_properties = HObjectLiteralFast::kMaxObjectLiteralProperties;
3381 Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()));
3382 if (boilerplate->IsJSObject() &&
3383 IsFastObjectLiteral(Handle<JSObject>::cast(boilerplate),
3384 HObjectLiteralFast::kMaxObjectLiteralDepth,
3385 &max_properties,
3386 &total_size)) {
3387 Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
3388 literal = new(zone()) HObjectLiteralFast(context,
3389 boilerplate_object,
3390 total_size,
3391 expr->literal_index(),
3392 expr->depth());
3393 } else {
3394 literal = new(zone()) HObjectLiteralGeneric(context,
3395 expr->constant_properties(),
3396 expr->fast_elements(),
3397 expr->literal_index(),
3398 expr->depth(),
3399 expr->has_function());
3400 }
3401
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003402 // The object is expected in the bailout environment during computation
3403 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003404 PushAndAdd(literal);
3405
3406 expr->CalculateEmitStore();
3407
3408 for (int i = 0; i < expr->properties()->length(); i++) {
3409 ObjectLiteral::Property* property = expr->properties()->at(i);
3410 if (property->IsCompileTimeValue()) continue;
3411
3412 Literal* key = property->key();
3413 Expression* value = property->value();
3414
3415 switch (property->kind()) {
3416 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3417 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
3418 // Fall through.
3419 case ObjectLiteral::Property::COMPUTED:
3420 if (key->handle()->IsSymbol()) {
3421 if (property->emit_store()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003422 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003423 HValue* value = Pop();
3424 Handle<String> name = Handle<String>::cast(key->handle());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003425 HStoreNamedGeneric* store =
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003426 new(zone()) HStoreNamedGeneric(
3427 context,
3428 literal,
3429 name,
3430 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003431 function_strict_mode_flag());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003432 AddInstruction(store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003433 AddSimulate(key->id());
3434 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003435 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003436 }
3437 break;
3438 }
3439 // Fall through.
3440 case ObjectLiteral::Property::PROTOTYPE:
3441 case ObjectLiteral::Property::SETTER:
3442 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00003443 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003444 default: UNREACHABLE();
3445 }
3446 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003447
3448 if (expr->has_function()) {
3449 // Return the result of the transformation to fast properties
3450 // instead of the original since this operation changes the map
3451 // of the object. This makes sure that the original object won't
3452 // be used by other optimized code before it is transformed
3453 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003454 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003455 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003456 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003457 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003458 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003459 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003460}
3461
3462
3463void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003464 ASSERT(!HasStackOverflow());
3465 ASSERT(current_block() != NULL);
3466 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003467 ZoneList<Expression*>* subexprs = expr->values();
3468 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003469 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003470
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003471 Handle<FixedArray> literals(environment()->closure()->literals());
3472 Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
3473
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003474 if (raw_boilerplate->IsUndefined()) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003475 raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
3476 isolate(), literals, expr->constant_elements());
3477 if (raw_boilerplate.is_null()) {
3478 return Bailout("array boilerplate creation failed");
3479 }
3480 literals->set(expr->literal_index(), *raw_boilerplate);
3481 if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
3482 isolate()->heap()->fixed_cow_array_map()) {
3483 isolate()->counters()->cow_arrays_created_runtime()->Increment();
3484 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003485 }
3486
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003487 Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
3488 ElementsKind boilerplate_elements_kind =
3489 Handle<JSObject>::cast(boilerplate)->GetElementsKind();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003490
3491 HArrayLiteral* literal = new(zone()) HArrayLiteral(
3492 context,
3493 boilerplate,
3494 length,
3495 expr->literal_index(),
3496 expr->depth());
3497
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003498 // The array is expected in the bailout environment during computation
3499 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003500 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003501
3502 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003503
3504 for (int i = 0; i < length; i++) {
3505 Expression* subexpr = subexprs->at(i);
3506 // If the subexpression is a literal or a simple materialized literal it
3507 // is already set in the cloned array.
3508 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
3509
danno@chromium.org160a7b02011-04-18 15:51:38 +00003510 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003511 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003512 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003513
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003514 elements = new(zone()) HLoadElements(literal);
3515 AddInstruction(elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003516
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003517 HValue* key = AddInstruction(
3518 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
3519 Representation::Integer32()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003520
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003521 switch (boilerplate_elements_kind) {
3522 case FAST_SMI_ONLY_ELEMENTS:
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00003523 // Smi-only arrays need a smi check.
3524 AddInstruction(new(zone()) HCheckSmi(value));
3525 // Fall through.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003526 case FAST_ELEMENTS:
3527 AddInstruction(new(zone()) HStoreKeyedFastElement(
3528 elements,
3529 key,
3530 value,
3531 boilerplate_elements_kind));
3532 break;
3533 case FAST_DOUBLE_ELEMENTS:
3534 AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements,
3535 key,
3536 value));
3537 break;
3538 default:
3539 UNREACHABLE();
3540 break;
3541 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003542
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003543 AddSimulate(expr->GetIdForElement(i));
3544 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003545 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003546}
3547
3548
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003549// Sets the lookup result and returns true if the store can be inlined.
3550static bool ComputeStoredField(Handle<Map> type,
3551 Handle<String> name,
3552 LookupResult* lookup) {
3553 type->LookupInDescriptors(NULL, *name, lookup);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003554 if (!lookup->IsFound()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003555 if (lookup->type() == FIELD) return true;
3556 return (lookup->type() == MAP_TRANSITION) &&
3557 (type->unused_property_fields() > 0);
3558}
3559
3560
3561static int ComputeStoredFieldIndex(Handle<Map> type,
3562 Handle<String> name,
3563 LookupResult* lookup) {
3564 ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION);
3565 if (lookup->type() == FIELD) {
3566 return lookup->GetLocalFieldIndexFromMap(*type);
3567 } else {
3568 Map* transition = lookup->GetTransitionMapFromMap(*type);
3569 return transition->PropertyIndexFor(*name) - type->inobject_properties();
3570 }
3571}
3572
3573
3574HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
3575 Handle<String> name,
3576 HValue* value,
3577 Handle<Map> type,
3578 LookupResult* lookup,
3579 bool smi_and_map_check) {
3580 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003581 AddInstruction(new(zone()) HCheckNonSmi(object));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003582 AddInstruction(new(zone()) HCheckMap(object, type, NULL,
3583 ALLOW_ELEMENT_TRANSITION_MAPS));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003584 }
3585
3586 int index = ComputeStoredFieldIndex(type, name, lookup);
3587 bool is_in_object = index < 0;
3588 int offset = index * kPointerSize;
3589 if (index < 0) {
3590 // Negative property indices are in-object properties, indexed
3591 // from the end of the fixed part of the object.
3592 offset += type->instance_size();
3593 } else {
3594 offset += FixedArray::kHeaderSize;
3595 }
3596 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003597 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003598 if (lookup->type() == MAP_TRANSITION) {
3599 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
3600 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00003601 // TODO(fschneider): Record the new map type of the object in the IR to
3602 // enable elimination of redundant checks after the transition store.
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00003603 instr->SetGVNFlag(kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003604 }
3605 return instr;
3606}
3607
3608
3609HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
3610 Handle<String> name,
3611 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003612 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003613 return new(zone()) HStoreNamedGeneric(
3614 context,
3615 object,
3616 name,
3617 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003618 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003619}
3620
3621
3622HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
3623 HValue* value,
3624 Expression* expr) {
3625 Property* prop = (expr->AsProperty() != NULL)
3626 ? expr->AsProperty()
3627 : expr->AsAssignment()->target()->AsProperty();
3628 Literal* key = prop->key()->AsLiteral();
3629 Handle<String> name = Handle<String>::cast(key->handle());
3630 ASSERT(!name.is_null());
3631
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003632 LookupResult lookup(isolate());
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003633 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003634 bool is_monomorphic = expr->IsMonomorphic() &&
3635 ComputeStoredField(types->first(), name, &lookup);
3636
3637 return is_monomorphic
3638 ? BuildStoreNamedField(object, name, value, types->first(), &lookup,
3639 true) // Needs smi and map check.
3640 : BuildStoreNamedGeneric(object, name, value);
3641}
3642
3643
3644void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
3645 HValue* object,
3646 HValue* value,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003647 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003648 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003649 // TODO(ager): We should recognize when the prototype chains for different
3650 // maps are identical. In that case we can avoid repeatedly generating the
3651 // same prototype map checks.
3652 int count = 0;
3653 HBasicBlock* join = NULL;
3654 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003655 Handle<Map> map = types->at(i);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003656 LookupResult lookup(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003657 if (ComputeStoredField(map, name, &lookup)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003658 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003659 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003660 join = graph()->CreateBasicBlock();
3661 }
3662 ++count;
3663 HBasicBlock* if_true = graph()->CreateBasicBlock();
3664 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003665 HCompareMap* compare =
3666 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003667 current_block()->Finish(compare);
3668
3669 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003670 HInstruction* instr =
3671 BuildStoreNamedField(object, name, value, map, &lookup, false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003672 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003673 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003674 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003675 if (!ast_context()->IsEffect()) Push(value);
3676 current_block()->Goto(join);
3677
3678 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003679 }
3680 }
3681
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003682 // Finish up. Unconditionally deoptimize if we've handled all the maps we
3683 // know about and do not want to handle ones we've never seen. Otherwise
3684 // use a generic IC.
3685 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003686 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003687 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003688 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003689 instr->set_position(expr->position());
3690 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003691
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003692 if (join != NULL) {
3693 if (!ast_context()->IsEffect()) Push(value);
3694 current_block()->Goto(join);
3695 } else {
3696 // The HSimulate for the store should not see the stored value in
3697 // effect contexts (it is not materialized at expr->id() in the
3698 // unoptimized code).
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003699 if (instr->HasObservableSideEffects()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003700 if (ast_context()->IsEffect()) {
3701 AddSimulate(expr->id());
3702 } else {
3703 Push(value);
3704 AddSimulate(expr->id());
3705 Drop(1);
3706 }
3707 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003708 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00003709 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00003710 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003711
3712 ASSERT(join != NULL);
3713 join->SetJoinId(expr->id());
3714 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003715 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003716}
3717
3718
3719void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
3720 Property* prop = expr->target()->AsProperty();
3721 ASSERT(prop != NULL);
3722 expr->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003723 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003724
3725 HValue* value = NULL;
3726 HInstruction* instr = NULL;
3727
3728 if (prop->key()->IsPropertyName()) {
3729 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003730 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003731 value = Pop();
3732 HValue* object = Pop();
3733
3734 Literal* key = prop->key()->AsLiteral();
3735 Handle<String> name = Handle<String>::cast(key->handle());
3736 ASSERT(!name.is_null());
3737
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00003738 SmallMapList* types = expr->GetReceiverTypes();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003739 LookupResult lookup(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003740
3741 if (expr->IsMonomorphic()) {
3742 instr = BuildStoreNamed(object, value, expr);
3743
3744 } else if (types != NULL && types->length() > 1) {
3745 HandlePolymorphicStoreNamedField(expr, object, value, types, name);
3746 return;
3747
3748 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003749 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003750 }
3751
3752 } else {
3753 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003754 CHECK_ALIVE(VisitForValue(prop->key()));
3755 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003756 value = Pop();
3757 HValue* key = Pop();
3758 HValue* object = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00003759 bool has_side_effects = false;
3760 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
3761 expr->position(),
3762 true, // is_store
3763 &has_side_effects);
3764 Push(value);
3765 ASSERT(has_side_effects); // Stores always have side effects.
3766 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003767 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003768 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003769 Push(value);
3770 instr->set_position(expr->position());
3771 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003772 if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003773 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003774}
3775
3776
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003777// Because not every expression has a position and there is not common
3778// superclass of Assignment and CountOperation, we cannot just pass the
3779// owning expression instead of position and ast_id separately.
3780void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003781 HValue* value,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003782 int position,
3783 int ast_id) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003784 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003785 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
3786 if (type == kUseCell) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003787 Handle<GlobalObject> global(info()->global_object());
3788 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003789 HInstruction* instr =
3790 new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003791 instr->set_position(position);
3792 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003793 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003794 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003795 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003796 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
3797 AddInstruction(global_object);
3798 HStoreGlobalGeneric* instr =
3799 new(zone()) HStoreGlobalGeneric(context,
3800 global_object,
3801 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003802 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003803 function_strict_mode_flag());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003804 instr->set_position(position);
3805 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003806 ASSERT(instr->HasObservableSideEffects());
3807 if (instr->HasObservableSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003808 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003809}
3810
3811
3812void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
3813 Expression* target = expr->target();
3814 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003815 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003816 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003817
3818 // We have a second position recorded in the FullCodeGenerator to have
3819 // type feedback for the binary operation.
3820 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003821
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003822 if (proxy != NULL) {
3823 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003824 if (var->mode() == LET) {
3825 return Bailout("unsupported let compound assignment");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003826 }
3827
danno@chromium.org160a7b02011-04-18 15:51:38 +00003828 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003829
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003830 switch (var->location()) {
3831 case Variable::UNALLOCATED:
3832 HandleGlobalVariableAssignment(var,
3833 Top(),
3834 expr->position(),
3835 expr->AssignmentId());
3836 break;
3837
3838 case Variable::PARAMETER:
3839 case Variable::LOCAL:
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003840 if (var->mode() == CONST) {
3841 return Bailout("unsupported const compound assignment");
3842 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003843 Bind(var, Top());
3844 break;
3845
3846 case Variable::CONTEXT: {
3847 // Bail out if we try to mutate a parameter value in a function
3848 // using the arguments object. We do not (yet) correctly handle the
3849 // arguments property of the function.
3850 if (info()->scope()->arguments() != NULL) {
3851 // Parameters will be allocated to context slots. We have no
3852 // direct way to detect that the variable is a parameter so we do
3853 // a linear search of the parameter variables.
3854 int count = info()->scope()->num_parameters();
3855 for (int i = 0; i < count; ++i) {
3856 if (var == info()->scope()->parameter(i)) {
3857 Bailout(
3858 "assignment to parameter, function uses arguments object");
3859 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003860 }
3861 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003862
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003863 HStoreContextSlot::Mode mode;
3864
3865 switch (var->mode()) {
3866 case LET:
3867 mode = HStoreContextSlot::kCheckDeoptimize;
3868 break;
3869 case CONST:
3870 return ast_context()->ReturnValue(Pop());
3871 case CONST_HARMONY:
3872 // This case is checked statically so no need to
3873 // perform checks here
3874 UNREACHABLE();
3875 default:
3876 mode = HStoreContextSlot::kNoCheck;
3877 }
3878
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003879 HValue* context = BuildContextChainWalk(var);
3880 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003881 new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003882 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003883 if (instr->HasObservableSideEffects()) {
3884 AddSimulate(expr->AssignmentId());
3885 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003886 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003887 }
3888
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003889 case Variable::LOOKUP:
3890 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003891 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003892 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003893
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003894 } else if (prop != NULL) {
3895 prop->RecordTypeFeedback(oracle());
3896
3897 if (prop->key()->IsPropertyName()) {
3898 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003899 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003900 HValue* obj = Top();
3901
3902 HInstruction* load = NULL;
3903 if (prop->IsMonomorphic()) {
3904 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
3905 Handle<Map> map = prop->GetReceiverTypes()->first();
3906 load = BuildLoadNamed(obj, prop, map, name);
3907 } else {
3908 load = BuildLoadNamedGeneric(obj, prop);
3909 }
3910 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003911 if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003912
danno@chromium.org160a7b02011-04-18 15:51:38 +00003913 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003914 HValue* right = Pop();
3915 HValue* left = Pop();
3916
3917 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3918 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003919 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003920
3921 HInstruction* store = BuildStoreNamed(obj, instr, prop);
3922 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003923 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003924 Drop(2);
3925 Push(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003926 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003927 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003928
3929 } else {
3930 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003931 CHECK_ALIVE(VisitForValue(prop->obj()));
3932 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003933 HValue* obj = environment()->ExpressionStackAt(1);
3934 HValue* key = environment()->ExpressionStackAt(0);
3935
whesse@chromium.org7b260152011-06-20 15:33:18 +00003936 bool has_side_effects = false;
3937 HValue* load = HandleKeyedElementAccess(
3938 obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
3939 false, // is_store
3940 &has_side_effects);
3941 Push(load);
3942 if (has_side_effects) AddSimulate(expr->CompoundLoadId());
3943
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003944
danno@chromium.org160a7b02011-04-18 15:51:38 +00003945 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003946 HValue* right = Pop();
3947 HValue* left = Pop();
3948
3949 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3950 PushAndAdd(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003951 if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003952
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003953 expr->RecordTypeFeedback(oracle());
whesse@chromium.org7b260152011-06-20 15:33:18 +00003954 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
3955 RelocInfo::kNoPosition,
3956 true, // is_store
3957 &has_side_effects);
3958
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003959 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003960 Drop(3);
3961 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003962 ASSERT(has_side_effects); // Stores always have side effects.
3963 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003964 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003965 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003966
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003967 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003968 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003969 }
3970}
3971
3972
3973void HGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003974 ASSERT(!HasStackOverflow());
3975 ASSERT(current_block() != NULL);
3976 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003977 VariableProxy* proxy = expr->target()->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003978 Property* prop = expr->target()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003979 ASSERT(proxy == NULL || prop == NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003980
3981 if (expr->is_compound()) {
3982 HandleCompoundAssignment(expr);
3983 return;
3984 }
3985
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00003986 if (prop != NULL) {
3987 HandlePropertyAssignment(expr);
3988 } else if (proxy != NULL) {
3989 Variable* var = proxy->var();
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003990
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00003991 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003992 if (expr->op() != Token::INIT_CONST) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003993 CHECK_ALIVE(VisitForValue(expr->value()));
3994 return ast_context()->ReturnValue(Pop());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003995 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003996
3997 if (var->IsStackAllocated()) {
3998 // We insert a use of the old value to detect unsupported uses of const
3999 // variables (e.g. initialization inside a loop).
4000 HValue* old_value = environment()->Lookup(var);
4001 AddInstruction(new HUseConst(old_value));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004002 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004003 } else if (var->mode() == CONST_HARMONY) {
4004 if (expr->op() != Token::INIT_CONST_HARMONY) {
4005 return Bailout("non-initializer assignment to const");
4006 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004007 }
4008
danno@chromium.org160a7b02011-04-18 15:51:38 +00004009 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004010
4011 // Handle the assignment.
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004012 switch (var->location()) {
4013 case Variable::UNALLOCATED:
4014 CHECK_ALIVE(VisitForValue(expr->value()));
4015 HandleGlobalVariableAssignment(var,
4016 Top(),
4017 expr->position(),
4018 expr->AssignmentId());
4019 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004020
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004021 case Variable::PARAMETER:
4022 case Variable::LOCAL: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004023 // Perform an initialization check for let declared variables
4024 // or parameters.
4025 if (var->mode() == LET && expr->op() == Token::ASSIGN) {
4026 HValue* env_value = environment()->Lookup(var);
4027 if (env_value == graph()->GetConstantHole()) {
4028 return Bailout("assignment to let variable before initialization");
4029 }
4030 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004031 // We do not allow the arguments object to occur in a context where it
4032 // may escape, but assignments to stack-allocated locals are
4033 // permitted.
4034 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
4035 HValue* value = Pop();
4036 Bind(var, value);
4037 return ast_context()->ReturnValue(value);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004038 }
4039
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004040 case Variable::CONTEXT: {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004041 // Bail out if we try to mutate a parameter value in a function using
4042 // the arguments object. We do not (yet) correctly handle the
4043 // arguments property of the function.
4044 if (info()->scope()->arguments() != NULL) {
4045 // Parameters will rewrite to context slots. We have no direct way
4046 // to detect that the variable is a parameter.
4047 int count = info()->scope()->num_parameters();
4048 for (int i = 0; i < count; ++i) {
4049 if (var == info()->scope()->parameter(i)) {
4050 return Bailout("assignment to parameter in arguments object");
4051 }
4052 }
4053 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004054
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004055 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004056 HStoreContextSlot::Mode mode;
4057 if (expr->op() == Token::ASSIGN) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004058 switch (var->mode()) {
4059 case LET:
4060 mode = HStoreContextSlot::kCheckDeoptimize;
4061 break;
4062 case CONST:
4063 return ast_context()->ReturnValue(Pop());
4064 case CONST_HARMONY:
4065 // This case is checked statically so no need to
4066 // perform checks here
4067 UNREACHABLE();
4068 default:
4069 mode = HStoreContextSlot::kNoCheck;
4070 }
4071 } else if (expr->op() == Token::INIT_VAR ||
4072 expr->op() == Token::INIT_LET ||
4073 expr->op() == Token::INIT_CONST_HARMONY) {
4074 mode = HStoreContextSlot::kNoCheck;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004075 } else {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004076 ASSERT(expr->op() == Token::INIT_CONST);
4077
4078 mode = HStoreContextSlot::kCheckIgnoreAssignment;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004079 }
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004080
4081 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004082 HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
4083 context, var->index(), mode, Top());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004084 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004085 if (instr->HasObservableSideEffects()) {
4086 AddSimulate(expr->AssignmentId());
4087 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004088 return ast_context()->ReturnValue(Pop());
4089 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004090
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00004091 case Variable::LOOKUP:
4092 return Bailout("assignment to LOOKUP variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004093 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004094 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004095 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004096 }
4097}
4098
4099
4100void HGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004101 ASSERT(!HasStackOverflow());
4102 ASSERT(current_block() != NULL);
4103 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004104 // We don't optimize functions with invalid left-hand sides in
4105 // assignments, count operations, or for-in. Consequently throw can
4106 // currently only occur in an effect context.
4107 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004108 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004109
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004110 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004111 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004112 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004113 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00004114 AddInstruction(instr);
4115 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004116 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00004117 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004118}
4119
4120
whesse@chromium.org023421e2010-12-21 12:19:12 +00004121HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
4122 Property* expr,
4123 Handle<Map> type,
4124 LookupResult* lookup,
4125 bool smi_and_map_check) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004126 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004127 AddInstruction(new(zone()) HCheckNonSmi(object));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004128 AddInstruction(new(zone()) HCheckMap(object, type, NULL,
4129 ALLOW_ELEMENT_TRANSITION_MAPS));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004130 }
4131
4132 int index = lookup->GetLocalFieldIndexFromMap(*type);
4133 if (index < 0) {
4134 // Negative property indices are in-object properties, indexed
4135 // from the end of the fixed part of the object.
4136 int offset = (index * kPointerSize) + type->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004137 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004138 } else {
4139 // Non-negative property indices are in the properties array.
4140 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004141 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004142 }
4143}
4144
4145
4146HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
4147 Property* expr) {
4148 ASSERT(expr->key()->IsPropertyName());
4149 Handle<Object> name = expr->key()->AsLiteral()->handle();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004150 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004151 return new(zone()) HLoadNamedGeneric(context, obj, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004152}
4153
4154
4155HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
4156 Property* expr,
4157 Handle<Map> map,
4158 Handle<String> name) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004159 LookupResult lookup(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004160 map->LookupInDescriptors(NULL, *name, &lookup);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00004161 if (lookup.IsFound() && lookup.type() == FIELD) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004162 return BuildLoadNamedField(obj,
4163 expr,
4164 map,
4165 &lookup,
4166 true);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00004167 } else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004168 AddInstruction(new(zone()) HCheckNonSmi(obj));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004169 AddInstruction(new(zone()) HCheckMap(obj, map, NULL,
4170 ALLOW_ELEMENT_TRANSITION_MAPS));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004171 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004172 return new(zone()) HConstant(function, Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004173 } else {
4174 return BuildLoadNamedGeneric(obj, expr);
4175 }
4176}
4177
4178
4179HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
4180 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004181 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004182 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004183}
4184
4185
whesse@chromium.org7b260152011-06-20 15:33:18 +00004186HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
4187 HValue* external_elements,
4188 HValue* checked_key,
4189 HValue* val,
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004190 ElementsKind elements_kind,
whesse@chromium.org7b260152011-06-20 15:33:18 +00004191 bool is_store) {
4192 if (is_store) {
4193 ASSERT(val != NULL);
4194 switch (elements_kind) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004195 case EXTERNAL_PIXEL_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004196 HClampToUint8* clamp = new(zone()) HClampToUint8(val);
4197 AddInstruction(clamp);
4198 val = clamp;
4199 break;
4200 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004201 case EXTERNAL_BYTE_ELEMENTS:
4202 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
4203 case EXTERNAL_SHORT_ELEMENTS:
4204 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
4205 case EXTERNAL_INT_ELEMENTS:
4206 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004207 HToInt32* floor_val = new(zone()) HToInt32(val);
4208 AddInstruction(floor_val);
4209 val = floor_val;
4210 break;
4211 }
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004212 case EXTERNAL_FLOAT_ELEMENTS:
4213 case EXTERNAL_DOUBLE_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00004214 break;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004215 case FAST_SMI_ONLY_ELEMENTS:
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004216 case FAST_ELEMENTS:
4217 case FAST_DOUBLE_ELEMENTS:
4218 case DICTIONARY_ELEMENTS:
4219 case NON_STRICT_ARGUMENTS_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00004220 UNREACHABLE();
4221 break;
4222 }
4223 return new(zone()) HStoreKeyedSpecializedArrayElement(
4224 external_elements, checked_key, val, elements_kind);
4225 } else {
4226 return new(zone()) HLoadKeyedSpecializedArrayElement(
4227 external_elements, checked_key, elements_kind);
4228 }
4229}
4230
4231
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004232HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
4233 HValue* checked_key,
4234 HValue* val,
4235 ElementsKind elements_kind,
4236 bool is_store) {
4237 if (is_store) {
4238 ASSERT(val != NULL);
danno@chromium.orgbf0c8202011-12-27 10:09:42 +00004239 switch (elements_kind) {
4240 case FAST_DOUBLE_ELEMENTS:
4241 return new(zone()) HStoreKeyedFastDoubleElement(
4242 elements, checked_key, val);
4243 case FAST_SMI_ONLY_ELEMENTS:
4244 // Smi-only arrays need a smi check.
4245 AddInstruction(new(zone()) HCheckSmi(val));
4246 // Fall through.
4247 case FAST_ELEMENTS:
4248 return new(zone()) HStoreKeyedFastElement(
4249 elements, checked_key, val, elements_kind);
4250 default:
4251 UNREACHABLE();
4252 return NULL;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004253 }
4254 }
4255 // It's an element load (!is_store).
4256 if (elements_kind == FAST_DOUBLE_ELEMENTS) {
4257 return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
4258 } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS.
4259 return new(zone()) HLoadKeyedFastElement(elements, checked_key);
4260 }
4261}
4262
4263
whesse@chromium.org7b260152011-06-20 15:33:18 +00004264HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
4265 HValue* key,
4266 HValue* val,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004267 Handle<Map> map,
whesse@chromium.org7b260152011-06-20 15:33:18 +00004268 bool is_store) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004269 HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
4270 bool fast_smi_only_elements = map->has_fast_smi_only_elements();
4271 bool fast_elements = map->has_fast_elements();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004272 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004273 if (is_store && (fast_elements || fast_smi_only_elements)) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004274 AddInstruction(new(zone()) HCheckMap(
4275 elements, isolate()->factory()->fixed_array_map()));
4276 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004277 HInstruction* length = NULL;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004278 HInstruction* checked_key = NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00004279 if (map->has_external_array_elements()) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004280 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004281 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
4282 HLoadExternalArrayPointer* external_elements =
4283 new(zone()) HLoadExternalArrayPointer(elements);
4284 AddInstruction(external_elements);
4285 return BuildExternalArrayElementAccess(external_elements, checked_key,
4286 val, map->elements_kind(), is_store);
4287 }
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004288 ASSERT(fast_smi_only_elements ||
4289 fast_elements ||
4290 map->has_fast_double_elements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00004291 if (map->instance_type() == JS_ARRAY_TYPE) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004292 length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004293 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004294 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004295 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004296 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004297 return BuildFastElementAccess(elements, checked_key, val,
4298 map->elements_kind(), is_store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004299}
4300
4301
whesse@chromium.org7b260152011-06-20 15:33:18 +00004302HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
4303 HValue* key,
4304 HValue* val,
4305 Expression* prop,
4306 int ast_id,
4307 int position,
4308 bool is_store,
4309 bool* has_side_effects) {
4310 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004311 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004312 SmallMapList* maps = prop->GetReceiverTypes();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004313 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004314
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004315 static const int kNumElementTypes = kElementsKindCount;
whesse@chromium.org7b260152011-06-20 15:33:18 +00004316 bool type_todo[kNumElementTypes];
4317 for (int i = 0; i < kNumElementTypes; ++i) {
4318 type_todo[i] = false;
4319 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004320
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004321 // Elements_kind transition support.
4322 MapHandleList transition_target(maps->length());
4323 // Collect possible transition targets.
4324 MapHandleList possible_transitioned_maps(maps->length());
whesse@chromium.org7b260152011-06-20 15:33:18 +00004325 for (int i = 0; i < maps->length(); ++i) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004326 Handle<Map> map = maps->at(i);
4327 ElementsKind elements_kind = map->elements_kind();
4328 if (elements_kind == FAST_DOUBLE_ELEMENTS ||
4329 elements_kind == FAST_ELEMENTS) {
4330 possible_transitioned_maps.Add(map);
4331 }
4332 }
4333 // Get transition target for each map (NULL == no transition).
4334 for (int i = 0; i < maps->length(); ++i) {
4335 Handle<Map> map = maps->at(i);
4336 Handle<Map> transitioned_map =
4337 map->FindTransitionedMap(&possible_transitioned_maps);
4338 transition_target.Add(transitioned_map);
4339 }
4340
4341 int num_untransitionable_maps = 0;
4342 Handle<Map> untransitionable_map;
4343 for (int i = 0; i < maps->length(); ++i) {
4344 Handle<Map> map = maps->at(i);
4345 ASSERT(map->IsMap());
4346 if (!transition_target.at(i).is_null()) {
4347 object = AddInstruction(new(zone()) HTransitionElementsKind(
4348 object, map, transition_target.at(i)));
4349 } else {
4350 type_todo[map->elements_kind()] = true;
4351 if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
4352 todo_external_array = true;
4353 }
4354 num_untransitionable_maps++;
4355 untransitionable_map = map;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004356 }
4357 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004358
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004359 // If only one map is left after transitioning, handle this case
4360 // monomorphically.
4361 if (num_untransitionable_maps == 1) {
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004362 HInstruction* instr = NULL;
4363 if (untransitionable_map->has_slow_elements_kind()) {
4364 instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
4365 : BuildLoadKeyedGeneric(object, key));
4366 } else {
4367 instr = AddInstruction(BuildMonomorphicElementAccess(
4368 object, key, val, untransitionable_map, is_store));
4369 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004370 *has_side_effects |= instr->HasObservableSideEffects();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004371 instr->set_position(position);
4372 return is_store ? NULL : instr;
4373 }
4374
4375 AddInstruction(HCheckInstanceType::NewIsSpecObject(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004376 HBasicBlock* join = graph()->CreateBasicBlock();
4377
4378 HInstruction* elements_kind_instr =
4379 AddInstruction(new(zone()) HElementsKind(object));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004380 HCompareConstantEqAndBranch* elements_kind_branch = NULL;
4381 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004382 HLoadExternalArrayPointer* external_elements = NULL;
4383 HInstruction* checked_key = NULL;
4384
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004385 // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS,
4386 // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external
4387 // arrays.
4388 STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
4389 STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
4390 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
4391 STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004392
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004393 for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004394 elements_kind <= LAST_ELEMENTS_KIND;
4395 elements_kind = ElementsKind(elements_kind + 1)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004396 // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS,
4397 // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code
4398 // that's executed for all external array cases.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004399 STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
4400 LAST_ELEMENTS_KIND);
4401 if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004402 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004403 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004404 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004405 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
4406 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
4407 AddInstruction(external_elements);
4408 }
4409 if (type_todo[elements_kind]) {
4410 HBasicBlock* if_true = graph()->CreateBasicBlock();
4411 HBasicBlock* if_false = graph()->CreateBasicBlock();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004412 elements_kind_branch = new(zone()) HCompareConstantEqAndBranch(
4413 elements_kind_instr, elements_kind, Token::EQ_STRICT);
4414 elements_kind_branch->SetSuccessorAt(0, if_true);
4415 elements_kind_branch->SetSuccessorAt(1, if_false);
4416 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004417
4418 set_current_block(if_true);
4419 HInstruction* access;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004420 if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
4421 elements_kind == FAST_ELEMENTS ||
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004422 elements_kind == FAST_DOUBLE_ELEMENTS) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004423 if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004424 AddInstruction(new(zone()) HCheckMap(
4425 elements, isolate()->factory()->fixed_array_map(),
4426 elements_kind_branch));
4427 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004428 // TODO(jkummerow): The need for these two blocks could be avoided
4429 // in one of two ways:
4430 // (1) Introduce ElementsKinds for JSArrays that are distinct from
4431 // those for fast objects.
4432 // (2) Put the common instructions into a third "join" block. This
4433 // requires additional AST IDs that we can deopt to from inside
4434 // that join block. They must be added to the Property class (when
4435 // it's a keyed property) and registered in the full codegen.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004436 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
4437 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004438 HHasInstanceTypeAndBranch* typecheck =
4439 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
4440 typecheck->SetSuccessorAt(0, if_jsarray);
4441 typecheck->SetSuccessorAt(1, if_fastobject);
4442 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004443
4444 set_current_block(if_jsarray);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004445 HInstruction* length;
4446 length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004447 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004448 access = AddInstruction(BuildFastElementAccess(
4449 elements, checked_key, val, elements_kind, is_store));
4450 if (!is_store) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004451 Push(access);
4452 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004453
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004454 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004455 if (position != -1) {
4456 access->set_position(position);
4457 }
4458 if_jsarray->Goto(join);
4459
4460 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004461 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004462 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004463 access = AddInstruction(BuildFastElementAccess(
4464 elements, checked_key, val, elements_kind, is_store));
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004465 } else if (elements_kind == DICTIONARY_ELEMENTS) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004466 if (is_store) {
4467 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
4468 } else {
4469 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
4470 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004471 } else { // External array elements.
4472 access = AddInstruction(BuildExternalArrayElementAccess(
4473 external_elements, checked_key, val, elements_kind, is_store));
4474 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004475 *has_side_effects |= access->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004476 access->set_position(position);
4477 if (!is_store) {
4478 Push(access);
4479 }
4480 current_block()->Goto(join);
4481 set_current_block(if_false);
4482 }
4483 }
4484
4485 // Deopt if none of the cases matched.
4486 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
4487 join->SetJoinId(ast_id);
4488 set_current_block(join);
4489 return is_store ? NULL : Pop();
4490}
4491
4492
4493HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
4494 HValue* key,
4495 HValue* val,
4496 Expression* expr,
4497 int ast_id,
4498 int position,
4499 bool is_store,
4500 bool* has_side_effects) {
4501 ASSERT(!expr->IsPropertyName());
4502 HInstruction* instr = NULL;
4503 if (expr->IsMonomorphic()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004504 Handle<Map> map = expr->GetMonomorphicReceiverType();
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004505 if (map->has_slow_elements_kind()) {
4506 instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
4507 : BuildLoadKeyedGeneric(obj, key);
4508 } else {
4509 AddInstruction(new(zone()) HCheckNonSmi(obj));
4510 instr = BuildMonomorphicElementAccess(obj, key, val, map, is_store);
4511 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004512 } else if (expr->GetReceiverTypes() != NULL &&
4513 !expr->GetReceiverTypes()->is_empty()) {
4514 return HandlePolymorphicElementAccess(
4515 obj, key, val, expr, ast_id, position, is_store, has_side_effects);
4516 } else {
4517 if (is_store) {
4518 instr = BuildStoreKeyedGeneric(obj, key, val);
4519 } else {
4520 instr = BuildLoadKeyedGeneric(obj, key);
4521 }
4522 }
4523 instr->set_position(position);
4524 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004525 *has_side_effects = instr->HasObservableSideEffects();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004526 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004527}
4528
4529
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004530HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
4531 HValue* key,
4532 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004533 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004534 return new(zone()) HStoreKeyedGeneric(
4535 context,
4536 object,
4537 key,
4538 value,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004539 function_strict_mode_flag());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004540}
4541
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004542bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
4543 VariableProxy* proxy = expr->obj()->AsVariableProxy();
4544 if (proxy == NULL) return false;
4545 if (!proxy->var()->IsStackAllocated()) return false;
4546 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
4547 return false;
4548 }
4549
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00004550 // Our implementation of arguments (based on this stack frame or an
4551 // adapter below it) does not work for inlined functions.
4552 if (function_state()->outer() != NULL) {
4553 Bailout("arguments access in inlined function");
4554 return true;
4555 }
4556
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004557 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004558 if (expr->key()->IsPropertyName()) {
4559 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
4560 if (!name->IsEqualTo(CStrVector("length"))) return false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004561 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4562 result = new(zone()) HArgumentsLength(elements);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004563 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004564 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004565 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004566 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004567 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004568 Drop(1); // Arguments object.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004569 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4570 HInstruction* length = AddInstruction(
4571 new(zone()) HArgumentsLength(elements));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004572 HInstruction* checked_key =
4573 AddInstruction(new(zone()) HBoundsCheck(key, length));
4574 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004575 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004576 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004577 return true;
4578}
4579
4580
4581void HGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004582 ASSERT(!HasStackOverflow());
4583 ASSERT(current_block() != NULL);
4584 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004585 expr->RecordTypeFeedback(oracle());
4586
4587 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004588
danno@chromium.org160a7b02011-04-18 15:51:38 +00004589 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004590
4591 HInstruction* instr = NULL;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004592 if (expr->AsProperty()->IsArrayLength()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004593 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004594 AddInstruction(new(zone()) HCheckNonSmi(array));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004595 HInstruction* mapcheck =
4596 AddInstruction(HCheckInstanceType::NewIsJSArray(array));
4597 instr = new(zone()) HJSArrayLength(array, mapcheck);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004598
ager@chromium.org378b34e2011-01-28 08:04:38 +00004599 } else if (expr->IsStringLength()) {
4600 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004601 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004602 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004603 instr = new(zone()) HStringLength(string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004604 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004605 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004606 HValue* index = Pop();
4607 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004608 HValue* context = environment()->LookupContext();
4609 HStringCharCodeAt* char_code =
4610 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004611 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004612 instr = new(zone()) HStringCharFromCode(context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00004613
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004614 } else if (expr->IsFunctionPrototype()) {
4615 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004616 AddInstruction(new(zone()) HCheckNonSmi(function));
4617 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004618
4619 } else if (expr->key()->IsPropertyName()) {
4620 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004621 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004622
4623 HValue* obj = Pop();
4624 if (expr->IsMonomorphic()) {
4625 instr = BuildLoadNamed(obj, expr, types->first(), name);
4626 } else if (types != NULL && types->length() > 1) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004627 AddInstruction(new(zone()) HCheckNonSmi(obj));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004628 HValue* context = environment()->LookupContext();
4629 instr = new(zone()) HLoadNamedFieldPolymorphic(context, obj, types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004630 } else {
4631 instr = BuildLoadNamedGeneric(obj, expr);
4632 }
4633
4634 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004635 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004636
4637 HValue* key = Pop();
4638 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004639
4640 bool has_side_effects = false;
4641 HValue* load = HandleKeyedElementAccess(
4642 obj, key, NULL, expr, expr->id(), expr->position(),
4643 false, // is_store
4644 &has_side_effects);
4645 if (has_side_effects) {
4646 if (ast_context()->IsEffect()) {
4647 AddSimulate(expr->id());
4648 } else {
4649 Push(load);
4650 AddSimulate(expr->id());
4651 Drop(1);
4652 }
4653 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004654 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004655 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004656 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004657 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004658}
4659
4660
4661void HGraphBuilder::AddCheckConstantFunction(Call* expr,
4662 HValue* receiver,
4663 Handle<Map> receiver_map,
4664 bool smi_and_map_check) {
4665 // Constant functions have the nice property that the map will change if they
4666 // are overwritten. Therefore it is enough to check the map of the holder and
4667 // its prototypes.
4668 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004669 AddInstruction(new(zone()) HCheckNonSmi(receiver));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004670 AddInstruction(new(zone()) HCheckMap(receiver, receiver_map, NULL,
4671 ALLOW_ELEMENT_TRANSITION_MAPS));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004672 }
4673 if (!expr->holder().is_null()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004674 AddInstruction(new(zone()) HCheckPrototypeMaps(
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004675 Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
4676 expr->holder()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004677 }
4678}
4679
4680
4681void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
4682 HValue* receiver,
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00004683 SmallMapList* types,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004684 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004685 // TODO(ager): We should recognize when the prototype chains for different
4686 // maps are identical. In that case we can avoid repeatedly generating the
4687 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004688 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
4689 int count = 0;
4690 HBasicBlock* join = NULL;
4691 for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004692 Handle<Map> map = types->at(i);
4693 if (expr->ComputeTarget(map, name)) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004694 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004695 // Only needed once.
4696 AddInstruction(new(zone()) HCheckNonSmi(receiver));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004697 join = graph()->CreateBasicBlock();
4698 }
4699 ++count;
4700 HBasicBlock* if_true = graph()->CreateBasicBlock();
4701 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004702 HCompareMap* compare =
4703 new(zone()) HCompareMap(receiver, map, if_true, if_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004704 current_block()->Finish(compare);
4705
4706 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004707 AddCheckConstantFunction(expr, receiver, map, false);
4708 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
4709 PrintF("Trying to inline the polymorphic call to %s\n",
4710 *name->ToCString());
4711 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00004712 if (FLAG_polymorphic_inlining && TryInline(expr)) {
4713 // Trying to inline will signal that we should bailout from the
4714 // entire compilation by setting stack overflow on the visitor.
4715 if (HasStackOverflow()) return;
4716 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004717 HCallConstantFunction* call =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004718 new(zone()) HCallConstantFunction(expr->target(), argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004719 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004720 PreProcessCall(call);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004721 AddInstruction(call);
4722 if (!ast_context()->IsEffect()) Push(call);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004723 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004724
4725 if (current_block() != NULL) current_block()->Goto(join);
4726 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004727 }
4728 }
4729
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004730 // Finish up. Unconditionally deoptimize if we've handled all the maps we
4731 // know about and do not want to handle ones we've never seen. Otherwise
4732 // use a generic IC.
4733 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004734 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004735 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004736 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004737 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004738 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004739 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00004740
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004741 if (join != NULL) {
4742 AddInstruction(call);
4743 if (!ast_context()->IsEffect()) Push(call);
4744 current_block()->Goto(join);
4745 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004746 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00004747 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004748 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004749
4750 // We assume that control flow is always live after an expression. So
4751 // even without predecessors to the join block, we set it as the exit
4752 // block and continue by adding instructions there.
4753 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004754 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004755 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004756 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004757 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004758 } else {
4759 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004760 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004761}
4762
4763
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004764void HGraphBuilder::TraceInline(Handle<JSFunction> target,
4765 Handle<JSFunction> caller,
4766 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004767 if (FLAG_trace_inlining) {
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004768 SmartArrayPointer<char> target_name =
4769 target->shared()->DebugName()->ToCString();
4770 SmartArrayPointer<char> caller_name =
4771 caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004772 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004773 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004774 } else {
4775 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004776 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004777 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004778 }
4779}
4780
4781
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004782bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004783 if (!FLAG_use_inlining) return false;
4784
danno@chromium.org40cb8782011-05-25 07:58:50 +00004785 // The function call we are inlining is a method call if the call
4786 // is a property call.
4787 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
4788 ? CALL_AS_FUNCTION
4789 : CALL_AS_METHOD;
4790
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004791 // Precondition: call is monomorphic and we have found a target with the
4792 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004793 Handle<JSFunction> caller = info()->closure();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004794 Handle<JSFunction> target = expr->target();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004795 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004796
4797 // Do a quick check on source code length to avoid parsing large
4798 // inlining candidates.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004799 if ((FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize)
4800 || target->shared()->SourceSize() > kUnlimitedMaxSourceSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004801 TraceInline(target, caller, "target text too big");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004802 return false;
4803 }
4804
4805 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004806 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004807 TraceInline(target, caller, "target not inlineable");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004808 return false;
4809 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004810
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004811#if !defined(V8_TARGET_ARCH_IA32)
4812 // Target must be able to use caller's context.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004813 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004814 if (target->context() != outer_info->closure()->context() ||
4815 outer_info->scope()->contains_with() ||
4816 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004817 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004818 return false;
4819 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004820#endif
4821
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004822
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004823 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004824 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004825 int current_level = 1;
4826 while (env->outer() != NULL) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004827 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004828 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004829 return false;
4830 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004831 if (!env->outer()->is_arguments_adaptor()) {
4832 current_level++;
4833 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004834 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004835 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004836
4837 // Don't inline recursive functions.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004838 for (FunctionState* state = function_state();
4839 state != NULL;
4840 state = state->outer()) {
4841 if (state->compilation_info()->closure()->shared() == *target_shared) {
4842 TraceInline(target, caller, "target is recursive");
4843 return false;
4844 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004845 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004846
4847 // We don't want to add more than a certain number of nodes from inlining.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004848 if ((FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) ||
4849 inlined_count_ > kUnlimitedMaxInlinedNodes) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004850 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004851 return false;
4852 }
4853
4854 int count_before = AstNode::Count();
4855
4856 // Parse and allocate variables.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004857 CompilationInfo target_info(target);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004858 if (!ParserApi::Parse(&target_info, kNoParsingFlags) ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004859 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004860 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004861 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004862 SetStackOverflow();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004863 target_shared->DisableOptimization(*target);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004864 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004865 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004866 return false;
4867 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004868
4869 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004870 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004871 return false;
4872 }
4873 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004874
4875 // Count the number of AST nodes added by inlining this call.
4876 int nodes_added = AstNode::Count() - count_before;
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004877 if ((FLAG_limit_inlining && nodes_added > kMaxInlinedSize) ||
4878 nodes_added > kUnlimitedMaxInlinedSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004879 TraceInline(target, caller, "target AST is too large");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004880 return false;
4881 }
4882
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004883 // Don't inline functions that uses the arguments object.
4884 if (function->scope()->arguments() != NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004885 TraceInline(target, caller, "target requires special argument handling");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004886 return false;
4887 }
4888
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004889 // All declarations must be inlineable.
4890 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
4891 int decl_count = decls->length();
4892 for (int i = 0; i < decl_count; ++i) {
4893 if (!decls->at(i)->IsInlineable()) {
4894 TraceInline(target, caller, "target has non-trivial declaration");
4895 return false;
4896 }
4897 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004898 // All statements in the body must be inlineable.
4899 for (int i = 0, count = function->body()->length(); i < count; ++i) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004900 if (!function->body()->at(i)->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004901 TraceInline(target, caller, "target contains unsupported syntax");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004902 return false;
4903 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004904 }
4905
4906 // Generate the deoptimization data for the unoptimized version of
4907 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004908 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004909 // Note that we compile here using the same AST that we will use for
4910 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004911 target_info.EnableDeoptimizationSupport();
4912 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004913 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004914 return false;
4915 }
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004916 if (target_shared->scope_info() == ScopeInfo::Empty()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004917 // The scope info might not have been set if a lazily compiled
4918 // function is inlined before being called for the first time.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00004919 Handle<ScopeInfo> target_scope_info =
4920 ScopeInfo::Create(target_info.scope());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004921 target_shared->set_scope_info(*target_scope_info);
4922 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004923 target_shared->EnableDeoptimizationSupport(*target_info.code());
4924 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
4925 &target_info,
4926 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004927 }
4928
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004929 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004930 // After this point, we've made a decision to inline this function (so
4931 // TryInline should always return true).
4932
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004933 // Save the pending call context and type feedback oracle. Set up new ones
4934 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004935 ASSERT(target_shared->has_deoptimization_support());
4936 TypeFeedbackOracle target_oracle(
4937 Handle<Code>(target_shared->code()),
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +00004938 Handle<Context>(target->context()->global_context()),
4939 isolate());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004940 // The function state is new-allocated because we need to delete it
4941 // in two different places.
4942 FunctionState* target_state =
4943 new FunctionState(this, &target_info, &target_oracle, drop_extra);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004944
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004945 HConstant* undefined = graph()->GetConstantUndefined();
4946 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00004947 environment()->CopyForInlining(target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004948 expr->arguments()->length(),
lrn@chromium.org1c092762011-05-09 09:42:16 +00004949 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00004950 undefined,
4951 call_kind);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004952#ifdef V8_TARGET_ARCH_IA32
4953 // IA32 only, overwrite the caller's context in the deoptimization
4954 // environment with the correct one.
4955 //
4956 // TODO(kmillikin): implement the same inlining on other platforms so we
4957 // can remove the unsightly ifdefs in this function.
4958 HConstant* context = new HConstant(Handle<Context>(target->context()),
4959 Representation::Tagged());
4960 AddInstruction(context);
4961 inner_env->BindContext(context);
4962#endif
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004963 HBasicBlock* body_entry = CreateBasicBlock(inner_env);
4964 current_block()->Goto(body_entry);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004965 body_entry->SetJoinId(expr->ReturnId());
4966 set_current_block(body_entry);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004967 AddInstruction(new(zone()) HEnterInlined(target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00004968 expr->arguments()->length(),
danno@chromium.org40cb8782011-05-25 07:58:50 +00004969 function,
4970 call_kind));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004971 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004972 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004973 if (HasStackOverflow()) {
4974 // Bail out if the inline function did, as we cannot residualize a call
4975 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004976 TraceInline(target, caller, "inline graph construction failed");
4977 target_shared->DisableOptimization(*target);
4978 inline_bailout_ = true;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004979 delete target_state;
danno@chromium.org160a7b02011-04-18 15:51:38 +00004980 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004981 }
4982
4983 // Update inlined nodes count.
4984 inlined_count_ += nodes_added;
4985
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004986 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004987
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004988 if (current_block() != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004989 // Add a return of undefined if control can fall off the body. In a
4990 // test context, undefined is false.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004991 if (inlined_test_context() == NULL) {
4992 ASSERT(function_return() != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004993 ASSERT(call_context()->IsEffect() || call_context()->IsValue());
4994 if (call_context()->IsEffect()) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004995 current_block()->Goto(function_return(), drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004996 } else {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004997 current_block()->AddLeaveInlined(undefined,
4998 function_return(),
4999 drop_extra);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005000 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005001 } else {
5002 // The graph builder assumes control can reach both branches of a
5003 // test, so we materialize the undefined value and test it rather than
5004 // simply jumping to the false target.
5005 //
5006 // TODO(3168478): refactor to avoid this.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005007 ASSERT(call_context()->IsTest());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005008 HBasicBlock* empty_true = graph()->CreateBasicBlock();
5009 HBasicBlock* empty_false = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005010 HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00005011 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005012
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005013 empty_true->Goto(inlined_test_context()->if_true(), drop_extra);
5014 empty_false->Goto(inlined_test_context()->if_false(), drop_extra);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005015 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005016 }
5017
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005018 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005019 if (inlined_test_context() != NULL) {
5020 HBasicBlock* if_true = inlined_test_context()->if_true();
5021 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005022
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005023 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005024 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005025 ClearInlinedTestContext();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005026 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005027
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005028 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005029 if (if_true->HasPredecessor()) {
5030 if_true->SetJoinId(expr->id());
5031 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005032 if_true->Goto(true_target, function_state()->drop_extra());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005033 }
5034 if (if_false->HasPredecessor()) {
5035 if_false->SetJoinId(expr->id());
5036 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005037 if_false->Goto(false_target, function_state()->drop_extra());
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005038 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005039 set_current_block(NULL);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005040 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005041
danno@chromium.org160a7b02011-04-18 15:51:38 +00005042 } else if (function_return()->HasPredecessor()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005043 function_return()->SetJoinId(expr->id());
5044 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005045 } else {
5046 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005047 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005048 delete target_state;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005049 return true;
5050}
5051
5052
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005053bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
5054 HValue* receiver,
5055 Handle<Map> receiver_map,
5056 CheckType check_type) {
5057 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005058 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005059 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005060 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005061 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
5062 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005063 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005064 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005065 if (argument_count == 2 && check_type == STRING_CHECK) {
5066 HValue* index = Pop();
5067 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005068 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005069 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005070 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005071 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
5072 expr->holder()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005073 HStringCharCodeAt* char_code =
5074 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005075 if (id == kStringCharCodeAt) {
5076 ast_context()->ReturnInstruction(char_code, expr->id());
5077 return true;
5078 }
5079 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005080 HStringCharFromCode* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005081 new(zone()) HStringCharFromCode(context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005082 ast_context()->ReturnInstruction(result, expr->id());
5083 return true;
5084 }
5085 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005086 case kMathRound:
5087 case kMathFloor:
5088 case kMathAbs:
5089 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005090 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00005091 case kMathSin:
5092 case kMathCos:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005093 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
5094 AddCheckConstantFunction(expr, receiver, receiver_map, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005095 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005096 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005097 Drop(1); // Receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005098 HUnaryMathOperation* op =
5099 new(zone()) HUnaryMathOperation(context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005100 op->set_position(expr->position());
5101 ast_context()->ReturnInstruction(op, expr->id());
5102 return true;
5103 }
5104 break;
5105 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005106 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
5107 AddCheckConstantFunction(expr, receiver, receiver_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005108 HValue* right = Pop();
5109 HValue* left = Pop();
5110 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005111 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005112 HInstruction* result = NULL;
5113 // Use sqrt() if exponent is 0.5 or -0.5.
5114 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
5115 double exponent = HConstant::cast(right)->DoubleValue();
5116 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005117 result =
5118 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005119 } else if (exponent == -0.5) {
5120 HConstant* double_one =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005121 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
5122 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005123 AddInstruction(double_one);
5124 HUnaryMathOperation* square_root =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005125 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005126 AddInstruction(square_root);
5127 // MathPowHalf doesn't have side effects so there's no need for
5128 // an environment simulation here.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005129 ASSERT(!square_root->HasObservableSideEffects());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005130 result = new(zone()) HDiv(context, double_one, square_root);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005131 } else if (exponent == 2.0) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005132 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005133 }
5134 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005135 HConstant::cast(right)->HasInteger32Value() &&
5136 HConstant::cast(right)->Integer32Value() == 2) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005137 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005138 }
5139
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005140 if (result == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005141 result = new(zone()) HPower(left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005142 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005143 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005144 return true;
5145 }
5146 break;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005147 case kMathRandom:
5148 if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
5149 AddCheckConstantFunction(expr, receiver, receiver_map, true);
5150 Drop(1);
5151 HValue* context = environment()->LookupContext();
5152 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
5153 AddInstruction(global_object);
5154 HRandom* result = new(zone()) HRandom(global_object);
5155 ast_context()->ReturnInstruction(result, expr->id());
5156 return true;
5157 }
5158 break;
5159 case kMathMax:
5160 case kMathMin:
5161 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
5162 AddCheckConstantFunction(expr, receiver, receiver_map, true);
5163 HValue* right = Pop();
5164 HValue* left = Pop();
5165 // Do not inline if the return representation is not certain.
5166 if (!left->representation().Equals(right->representation())) {
5167 Push(left);
5168 Push(right);
5169 return false;
5170 }
5171
5172 Pop(); // Pop receiver.
5173 Token::Value op = (id == kMathMin) ? Token::LT : Token::GT;
5174 HCompareIDAndBranch* compare = NULL;
5175
5176 if (left->representation().IsTagged()) {
5177 HChange* left_cvt =
5178 new(zone()) HChange(left, Representation::Double(), false, true);
5179 left_cvt->SetFlag(HValue::kBailoutOnMinusZero);
5180 AddInstruction(left_cvt);
5181 HChange* right_cvt =
5182 new(zone()) HChange(right, Representation::Double(), false, true);
5183 right_cvt->SetFlag(HValue::kBailoutOnMinusZero);
5184 AddInstruction(right_cvt);
5185 compare = new(zone()) HCompareIDAndBranch(left_cvt, right_cvt, op);
5186 compare->SetInputRepresentation(Representation::Double());
5187 } else {
5188 compare = new(zone()) HCompareIDAndBranch(left, right, op);
5189 compare->SetInputRepresentation(left->representation());
5190 }
5191
5192 HBasicBlock* return_left = graph()->CreateBasicBlock();
5193 HBasicBlock* return_right = graph()->CreateBasicBlock();
5194
5195 compare->SetSuccessorAt(0, return_left);
5196 compare->SetSuccessorAt(1, return_right);
5197 current_block()->Finish(compare);
5198
5199 set_current_block(return_left);
5200 Push(left);
5201 set_current_block(return_right);
5202 Push(right);
5203
5204 HBasicBlock* join = CreateJoin(return_left, return_right, expr->id());
5205 set_current_block(join);
5206 ast_context()->ReturnValue(Pop());
5207 return true;
5208 }
5209 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005210 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005211 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005212 break;
5213 }
5214 return false;
5215}
5216
5217
5218bool HGraphBuilder::TryCallApply(Call* expr) {
5219 Expression* callee = expr->expression();
5220 Property* prop = callee->AsProperty();
5221 ASSERT(prop != NULL);
5222
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005223 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
5224 return false;
5225 }
5226 Handle<Map> function_map = expr->GetReceiverTypes()->first();
5227 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
5228 !expr->target()->shared()->HasBuiltinFunctionId() ||
5229 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
5230 return false;
5231 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005232
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00005233 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005234
5235 ZoneList<Expression*>* args = expr->arguments();
5236 if (args->length() != 2) return false;
5237
5238 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00005239 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005240 HValue* arg_two_value = environment()->Lookup(arg_two->var());
5241 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
5242
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00005243 // Our implementation of arguments (based on this stack frame or an
5244 // adapter below it) does not work for inlined functions.
5245 if (function_state()->outer() != NULL) {
5246 Bailout("Function.prototype.apply optimization in inlined function");
5247 return true;
5248 }
5249
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005250 // Found pattern f.apply(receiver, arguments).
5251 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005252 if (HasStackOverflow() || current_block() == NULL) return true;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005253 HValue* function = Top();
5254 AddCheckConstantFunction(expr, function, function_map, true);
5255 Drop(1);
5256
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005257 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00005258 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005259 HValue* receiver = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005260 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
5261 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005262 HInstruction* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005263 new(zone()) HApplyArguments(function, receiver, length, elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005264 result->set_position(expr->position());
5265 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005266 return true;
5267}
5268
5269
5270void HGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005271 ASSERT(!HasStackOverflow());
5272 ASSERT(current_block() != NULL);
5273 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005274 Expression* callee = expr->expression();
5275 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005276 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005277
5278 Property* prop = callee->AsProperty();
5279 if (prop != NULL) {
5280 if (!prop->key()->IsPropertyName()) {
5281 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005282 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005283
danno@chromium.org160a7b02011-04-18 15:51:38 +00005284 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005285 // Push receiver and key like the non-optimized code generator expects it.
5286 HValue* key = Pop();
5287 HValue* receiver = Pop();
5288 Push(key);
5289 Push(receiver);
5290
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005291 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005292
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005293 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005294 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005295 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005296 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005297 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005298 }
5299
5300 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00005301 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005302
5303 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005304
danno@chromium.org160a7b02011-04-18 15:51:38 +00005305 CHECK_ALIVE(VisitForValue(prop->obj()));
5306 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005307
5308 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
5309
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005310 SmallMapList* types = expr->GetReceiverTypes();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005311
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005312 HValue* receiver =
5313 environment()->ExpressionStackAt(expr->arguments()->length());
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005314 if (expr->IsMonomorphic()) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005315 Handle<Map> receiver_map = (types == NULL || types->is_empty())
5316 ? Handle<Map>::null()
5317 : types->first();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005318 if (TryInlineBuiltinFunction(expr,
5319 receiver,
5320 receiver_map,
5321 expr->check_type())) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005322 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005323 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005324
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005325 if (CallStubCompiler::HasCustomCallGenerator(expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005326 expr->check_type() != RECEIVER_MAP_CHECK) {
5327 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005328 // because it is likely to generate better code. Also use the IC
5329 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005330 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005331 call = PreProcessCall(
5332 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005333 } else {
5334 AddCheckConstantFunction(expr, receiver, receiver_map, true);
5335
danno@chromium.org160a7b02011-04-18 15:51:38 +00005336 if (TryInline(expr)) return;
5337 call = PreProcessCall(
5338 new(zone()) HCallConstantFunction(expr->target(),
5339 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005340 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005341 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00005342 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005343 HandlePolymorphicCallNamed(expr, receiver, types, name);
5344 return;
5345
5346 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005347 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005348 call = PreProcessCall(
5349 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005350 }
5351
5352 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005353 expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005354 VariableProxy* proxy = expr->expression()->AsVariableProxy();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005355 bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005356
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005357 if (global_call) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005358 Variable* var = proxy->var();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005359 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005360 // If there is a global property cell for the name at compile time and
5361 // access check is not enabled we assume that the function will not change
5362 // and generate optimized code for calling the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005363 LookupResult lookup(isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005364 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
5365 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005366 !info()->global_object()->IsAccessCheckNeeded()) {
5367 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005368 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005369 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005370 if (known_global_function) {
5371 // Push the global object instead of the global receiver because
5372 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005373 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005374 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005375 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005376 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005377
danno@chromium.org160a7b02011-04-18 15:51:38 +00005378 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005379 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005380 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005381
5382 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005383 HGlobalReceiver* global_receiver =
5384 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005385 // Index of the receiver from the top of the expression stack.
5386 const int receiver_index = argument_count - 1;
5387 AddInstruction(global_receiver);
5388 ASSERT(environment()->ExpressionStackAt(receiver_index)->
5389 IsGlobalObject());
5390 environment()->SetExpressionStackAt(receiver_index, global_receiver);
5391
danno@chromium.org160a7b02011-04-18 15:51:38 +00005392 if (TryInline(expr)) return;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005393 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00005394 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005395 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005396 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005397 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
5398 AddInstruction(receiver);
5399 PushAndAdd(new(zone()) HPushArgument(receiver));
5400 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005401
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005402 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
5403 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005404 }
5405
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005406 } else if (expr->IsMonomorphic()) {
5407 // The function is on the stack in the unoptimized code during
5408 // evaluation of the arguments.
5409 CHECK_ALIVE(VisitForValue(expr->expression()));
5410 HValue* function = Top();
5411 HValue* context = environment()->LookupContext();
5412 HGlobalObject* global = new(zone()) HGlobalObject(context);
5413 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
5414 AddInstruction(global);
5415 PushAndAdd(receiver);
5416 CHECK_ALIVE(VisitExpressions(expr->arguments()));
5417 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005418 if (TryInline(expr, true)) { // Drop function from environment.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005419 return;
5420 } else {
5421 call = PreProcessCall(new(zone()) HInvokeFunction(context,
5422 function,
5423 argument_count));
5424 Drop(1); // The function.
5425 }
5426
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005427 } else {
danno@chromium.orgc612e022011-11-10 11:38:15 +00005428 CHECK_ALIVE(VisitForValue(expr->expression()));
5429 HValue* function = Top();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005430 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005431 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005432 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00005433 AddInstruction(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005434 AddInstruction(receiver);
5435 PushAndAdd(new(zone()) HPushArgument(receiver));
5436 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005437
danno@chromium.orgc612e022011-11-10 11:38:15 +00005438 call = new(zone()) HCallFunction(context, function, argument_count);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005439 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005440 }
5441 }
5442
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005443 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005444 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005445}
5446
5447
5448void HGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005449 ASSERT(!HasStackOverflow());
5450 ASSERT(current_block() != NULL);
5451 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005452 // The constructor function is also used as the receiver argument to the
5453 // JS construct call builtin.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005454 HValue* constructor = NULL;
5455 CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
5456 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005457
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005458 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00005459
5460 // The constructor is both an operand to the instruction and an argument
5461 // to the construct call.
5462 int arg_count = expr->arguments()->length() + 1; // Plus constructor.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005463 HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005464 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00005465 Drop(arg_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005466 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005467}
5468
5469
5470// Support for generating inlined runtime functions.
5471
5472// Lookup table for generators for runtime calls that are generated inline.
5473// Elements of the table are member pointers to functions of HGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005474#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005475 &HGraphBuilder::Generate##Name,
5476
5477const HGraphBuilder::InlineFunctionGenerator
5478 HGraphBuilder::kInlineFunctionGenerators[] = {
5479 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
5480 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
5481};
5482#undef INLINE_FUNCTION_GENERATOR_ADDRESS
5483
5484
5485void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005486 ASSERT(!HasStackOverflow());
5487 ASSERT(current_block() != NULL);
5488 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005489 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005490 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005491 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005492
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005493 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005494 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005495 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005496 ASSERT(expr->name()->length() > 0);
5497 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005498 // Call to an inline function.
5499 int lookup_index = static_cast<int>(function->function_id) -
5500 static_cast<int>(Runtime::kFirstInlineFunction);
5501 ASSERT(lookup_index >= 0);
5502 ASSERT(static_cast<size_t>(lookup_index) <
5503 ARRAY_SIZE(kInlineFunctionGenerators));
5504 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
5505
5506 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005507 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005508 } else {
5509 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005510 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005511
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005512 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005513 Handle<String> name = expr->name();
5514 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005515 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005516 new(zone()) HCallRuntime(context, name, function, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005517 call->set_position(RelocInfo::kNoPosition);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005518 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005519 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005520 }
5521}
5522
5523
5524void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005525 ASSERT(!HasStackOverflow());
5526 ASSERT(current_block() != NULL);
5527 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005528 switch (expr->op()) {
5529 case Token::DELETE: return VisitDelete(expr);
5530 case Token::VOID: return VisitVoid(expr);
5531 case Token::TYPEOF: return VisitTypeof(expr);
5532 case Token::ADD: return VisitAdd(expr);
5533 case Token::SUB: return VisitSub(expr);
5534 case Token::BIT_NOT: return VisitBitNot(expr);
5535 case Token::NOT: return VisitNot(expr);
5536 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005537 }
5538}
5539
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005540void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
5541 Property* prop = expr->expression()->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005542 VariableProxy* proxy = expr->expression()->AsVariableProxy();
5543 if (prop != NULL) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005544 CHECK_ALIVE(VisitForValue(prop->obj()));
5545 CHECK_ALIVE(VisitForValue(prop->key()));
5546 HValue* key = Pop();
5547 HValue* obj = Pop();
5548 HValue* context = environment()->LookupContext();
5549 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
5550 return ast_context()->ReturnInstruction(instr, expr->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005551 } else if (proxy != NULL) {
5552 Variable* var = proxy->var();
5553 if (var->IsUnallocated()) {
5554 Bailout("delete with global variable");
5555 } else if (var->IsStackAllocated() || var->IsContextSlot()) {
5556 // Result of deleting non-global variables is false. 'this' is not
5557 // really a variable, though we implement it as one. The
5558 // subexpression does not have side effects.
5559 HValue* value = var->is_this()
5560 ? graph()->GetConstantTrue()
5561 : graph()->GetConstantFalse();
5562 return ast_context()->ReturnValue(value);
5563 } else {
5564 Bailout("delete with non-global variable");
5565 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005566 } else {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005567 // Result of deleting non-property, non-variable reference is true.
5568 // Evaluate the subexpression for side effects.
5569 CHECK_ALIVE(VisitForEffect(expr->expression()));
5570 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005571 }
5572}
5573
5574
5575void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
5576 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005577 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005578}
5579
5580
5581void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
5582 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
5583 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005584 HValue* context = environment()->LookupContext();
5585 HInstruction* instr = new(zone()) HTypeof(context, value);
5586 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005587}
5588
5589
5590void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
5591 CHECK_ALIVE(VisitForValue(expr->expression()));
5592 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005593 HValue* context = environment()->LookupContext();
5594 HInstruction* instr =
5595 new(zone()) HMul(context, value, graph_->GetConstant1());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005596 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005597}
5598
5599
5600void HGraphBuilder::VisitSub(UnaryOperation* expr) {
5601 CHECK_ALIVE(VisitForValue(expr->expression()));
5602 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005603 HValue* context = environment()->LookupContext();
5604 HInstruction* instr =
5605 new(zone()) HMul(context, value, graph_->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005606 TypeInfo info = oracle()->UnaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005607 if (info.IsUninitialized()) {
5608 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005609 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005610 info = TypeInfo::Unknown();
5611 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005612 Representation rep = ToRepresentation(info);
5613 TraceRepresentation(expr->op(), info, instr, rep);
5614 instr->AssumeRepresentation(rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005615 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005616}
5617
5618
5619void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
5620 CHECK_ALIVE(VisitForValue(expr->expression()));
5621 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005622 TypeInfo info = oracle()->UnaryType(expr);
5623 if (info.IsUninitialized()) {
5624 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005625 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005626 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005627 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005628 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005629}
5630
5631
5632void HGraphBuilder::VisitNot(UnaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005633 if (ast_context()->IsTest()) {
5634 TestContext* context = TestContext::cast(ast_context());
5635 VisitForControl(expr->expression(),
5636 context->if_false(),
5637 context->if_true());
5638 return;
5639 }
5640
5641 if (ast_context()->IsEffect()) {
5642 VisitForEffect(expr->expression());
5643 return;
5644 }
5645
5646 ASSERT(ast_context()->IsValue());
5647 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
5648 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
5649 CHECK_BAILOUT(VisitForControl(expr->expression(),
5650 materialize_false,
5651 materialize_true));
5652
5653 if (materialize_false->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005654 materialize_false->SetJoinId(expr->MaterializeFalseId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005655 set_current_block(materialize_false);
5656 Push(graph()->GetConstantFalse());
5657 } else {
5658 materialize_false = NULL;
5659 }
5660
5661 if (materialize_true->HasPredecessor()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005662 materialize_true->SetJoinId(expr->MaterializeTrueId());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005663 set_current_block(materialize_true);
5664 Push(graph()->GetConstantTrue());
5665 } else {
5666 materialize_true = NULL;
5667 }
5668
5669 HBasicBlock* join =
5670 CreateJoin(materialize_false, materialize_true, expr->id());
5671 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005672 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005673}
5674
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005675
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005676HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00005677 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005678 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005679 TypeInfo info = oracle()->IncrementType(expr);
5680 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00005681 if (rep.IsTagged()) {
5682 rep = Representation::Integer32();
5683 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005684
5685 if (returns_original_input) {
5686 // We need an explicit HValue representing ToNumber(input). The
5687 // actual HChange instruction we need is (sometimes) added in a later
5688 // phase, so it is not available now to be used as an input to HAdd and
5689 // as the return value.
5690 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
5691 AddInstruction(number_input);
5692 Push(number_input);
5693 }
5694
5695 // The addition has no side effects, so we do not need
5696 // to simulate the expression stack after this instruction.
5697 // Any later failures deopt to the load of the input or earlier.
5698 HConstant* delta = (expr->op() == Token::INC)
5699 ? graph_->GetConstant1()
5700 : graph_->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005701 HValue* context = environment()->LookupContext();
5702 HInstruction* instr = new(zone()) HAdd(context, Top(), delta);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005703 TraceRepresentation(expr->op(), info, instr, rep);
5704 instr->AssumeRepresentation(rep);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005705 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005706 return instr;
5707}
5708
5709
5710void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005711 ASSERT(!HasStackOverflow());
5712 ASSERT(current_block() != NULL);
5713 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005714 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005715 VariableProxy* proxy = target->AsVariableProxy();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005716 Property* prop = target->AsProperty();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005717 if (proxy == NULL && prop == NULL) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005718 return Bailout("invalid lhs in count operation");
5719 }
5720
5721 // Match the full code generator stack by simulating an extra stack
5722 // element for postfix operations in a non-effect context. The return
5723 // value is ToNumber(input).
5724 bool returns_original_input =
5725 expr->is_postfix() && !ast_context()->IsEffect();
5726 HValue* input = NULL; // ToNumber(original_input).
5727 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005728
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005729 if (proxy != NULL) {
5730 Variable* var = proxy->var();
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00005731 if (var->mode() == CONST) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005732 return Bailout("unsupported count operation with const");
5733 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005734 // Argument of the count operation is a variable, not a property.
5735 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005736 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005737
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005738 after = BuildIncrement(returns_original_input, expr);
5739 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005740 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005741
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005742 switch (var->location()) {
5743 case Variable::UNALLOCATED:
5744 HandleGlobalVariableAssignment(var,
5745 after,
5746 expr->position(),
5747 expr->AssignmentId());
5748 break;
5749
5750 case Variable::PARAMETER:
5751 case Variable::LOCAL:
5752 Bind(var, after);
5753 break;
5754
5755 case Variable::CONTEXT: {
5756 // Bail out if we try to mutate a parameter value in a function
5757 // using the arguments object. We do not (yet) correctly handle the
5758 // arguments property of the function.
5759 if (info()->scope()->arguments() != NULL) {
5760 // Parameters will rewrite to context slots. We have no direct
5761 // way to detect that the variable is a parameter so we use a
5762 // linear search of the parameter list.
5763 int count = info()->scope()->num_parameters();
5764 for (int i = 0; i < count; ++i) {
5765 if (var == info()->scope()->parameter(i)) {
5766 return Bailout("assignment to parameter in arguments object");
5767 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00005768 }
5769 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005770
5771 HValue* context = BuildContextChainWalk(var);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005772 HStoreContextSlot::Mode mode =
5773 (var->mode() == LET || var->mode() == CONST_HARMONY)
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005774 ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005775 HStoreContextSlot* instr =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005776 new(zone()) HStoreContextSlot(context, var->index(), mode, after);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005777 AddInstruction(instr);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005778 if (instr->HasObservableSideEffects()) {
5779 AddSimulate(expr->AssignmentId());
5780 }
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005781 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005782 }
5783
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00005784 case Variable::LOOKUP:
5785 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005786 }
5787
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005788 } else {
5789 // Argument of the count operation is a property.
5790 ASSERT(prop != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005791 prop->RecordTypeFeedback(oracle());
5792
5793 if (prop->key()->IsPropertyName()) {
5794 // Named property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005795 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005796
danno@chromium.org160a7b02011-04-18 15:51:38 +00005797 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005798 HValue* obj = Top();
5799
5800 HInstruction* load = NULL;
5801 if (prop->IsMonomorphic()) {
5802 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
5803 Handle<Map> map = prop->GetReceiverTypes()->first();
5804 load = BuildLoadNamed(obj, prop, map, name);
5805 } else {
5806 load = BuildLoadNamedGeneric(obj, prop);
5807 }
5808 PushAndAdd(load);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005809 if (load->HasObservableSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005810
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005811 after = BuildIncrement(returns_original_input, expr);
5812 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005813
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005814 HInstruction* store = BuildStoreNamed(obj, after, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005815 AddInstruction(store);
5816
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005817 // Overwrite the receiver in the bailout environment with the result
5818 // of the operation, and the placeholder with the original value if
5819 // necessary.
5820 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005821 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005822 if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005823
5824 } else {
5825 // Keyed property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005826 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005827
danno@chromium.org160a7b02011-04-18 15:51:38 +00005828 CHECK_ALIVE(VisitForValue(prop->obj()));
5829 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005830 HValue* obj = environment()->ExpressionStackAt(1);
5831 HValue* key = environment()->ExpressionStackAt(0);
5832
whesse@chromium.org7b260152011-06-20 15:33:18 +00005833 bool has_side_effects = false;
5834 HValue* load = HandleKeyedElementAccess(
5835 obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
5836 false, // is_store
5837 &has_side_effects);
5838 Push(load);
5839 if (has_side_effects) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005840
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005841 after = BuildIncrement(returns_original_input, expr);
5842 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005843
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005844 expr->RecordTypeFeedback(oracle());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005845 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
5846 RelocInfo::kNoPosition,
5847 true, // is_store
5848 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005849
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005850 // Drop the key from the bailout environment. Overwrite the receiver
5851 // with the result of the operation, and the placeholder with the
5852 // original value if necessary.
5853 Drop(1);
5854 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005855 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005856 ASSERT(has_side_effects); // Stores always have side effects.
5857 AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005858 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005859 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005860
5861 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005862 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005863}
5864
5865
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005866HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context,
5867 HValue* string,
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005868 HValue* index) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005869 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005870 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005871 HStringLength* length = new(zone()) HStringLength(string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005872 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005873 HInstruction* checked_index =
5874 AddInstruction(new(zone()) HBoundsCheck(index, length));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005875 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005876}
5877
5878
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005879HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
5880 HValue* left,
5881 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005882 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005883 TypeInfo info = oracle()->BinaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005884 if (info.IsUninitialized()) {
5885 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005886 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005887 info = TypeInfo::Unknown();
5888 }
5889 HInstruction* instr = NULL;
5890 switch (expr->op()) {
5891 case Token::ADD:
5892 if (info.IsString()) {
5893 AddInstruction(new(zone()) HCheckNonSmi(left));
5894 AddInstruction(HCheckInstanceType::NewIsString(left));
5895 AddInstruction(new(zone()) HCheckNonSmi(right));
5896 AddInstruction(HCheckInstanceType::NewIsString(right));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005897 instr = new(zone()) HStringAdd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005898 } else {
erikcorry0ad885c2011-11-21 13:51:57 +00005899 instr = HAdd::NewHAdd(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005900 }
5901 break;
5902 case Token::SUB:
erikcorry0ad885c2011-11-21 13:51:57 +00005903 instr = HSub::NewHSub(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005904 break;
5905 case Token::MUL:
erikcorry0ad885c2011-11-21 13:51:57 +00005906 instr = HMul::NewHMul(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005907 break;
5908 case Token::MOD:
erikcorry0ad885c2011-11-21 13:51:57 +00005909 instr = HMod::NewHMod(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005910 break;
5911 case Token::DIV:
erikcorry0ad885c2011-11-21 13:51:57 +00005912 instr = HDiv::NewHDiv(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005913 break;
5914 case Token::BIT_XOR:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005915 case Token::BIT_AND:
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005916 case Token::BIT_OR:
erikcorry0ad885c2011-11-21 13:51:57 +00005917 instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005918 break;
5919 case Token::SAR:
erikcorry0ad885c2011-11-21 13:51:57 +00005920 instr = HSar::NewHSar(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005921 break;
5922 case Token::SHR:
erikcorry0ad885c2011-11-21 13:51:57 +00005923 instr = HShr::NewHShr(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005924 break;
5925 case Token::SHL:
erikcorry0ad885c2011-11-21 13:51:57 +00005926 instr = HShl::NewHShl(zone(), context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005927 break;
5928 default:
5929 UNREACHABLE();
5930 }
5931
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005932 // If we hit an uninitialized binary op stub we will get type info
5933 // for a smi operation. If one of the operands is a constant string
5934 // do not generate code assuming it is a smi operation.
5935 if (info.IsSmi() &&
5936 ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) ||
5937 (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
5938 return instr;
5939 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005940 Representation rep = ToRepresentation(info);
5941 // We only generate either int32 or generic tagged bitwise operations.
5942 if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) {
5943 rep = Representation::Integer32();
5944 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005945 TraceRepresentation(expr->op(), info, instr, rep);
5946 instr->AssumeRepresentation(rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005947 return instr;
5948}
5949
5950
5951// Check for the form (%_ClassOf(foo) === 'BarClass').
5952static bool IsClassOfTest(CompareOperation* expr) {
5953 if (expr->op() != Token::EQ_STRICT) return false;
5954 CallRuntime* call = expr->left()->AsCallRuntime();
5955 if (call == NULL) return false;
5956 Literal* literal = expr->right()->AsLiteral();
5957 if (literal == NULL) return false;
5958 if (!literal->handle()->IsString()) return false;
5959 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
5960 ASSERT(call->arguments()->length() == 1);
5961 return true;
5962}
5963
5964
5965void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005966 ASSERT(!HasStackOverflow());
5967 ASSERT(current_block() != NULL);
5968 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005969 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005970 case Token::COMMA:
5971 return VisitComma(expr);
5972 case Token::OR:
5973 case Token::AND:
5974 return VisitLogicalExpression(expr);
5975 default:
5976 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005977 }
5978}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005979
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005980
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005981void HGraphBuilder::VisitComma(BinaryOperation* expr) {
5982 CHECK_ALIVE(VisitForEffect(expr->left()));
5983 // Visit the right subexpression in the same AST context as the entire
5984 // expression.
5985 Visit(expr->right());
5986}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005987
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005988
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005989void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
5990 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005991 if (ast_context()->IsTest()) {
5992 TestContext* context = TestContext::cast(ast_context());
5993 // Translate left subexpression.
5994 HBasicBlock* eval_right = graph()->CreateBasicBlock();
5995 if (is_logical_and) {
5996 CHECK_BAILOUT(VisitForControl(expr->left(),
5997 eval_right,
5998 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005999 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006000 CHECK_BAILOUT(VisitForControl(expr->left(),
6001 context->if_true(),
6002 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006003 }
6004
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006005 // Translate right subexpression by visiting it in the same AST
6006 // context as the entire expression.
6007 if (eval_right->HasPredecessor()) {
6008 eval_right->SetJoinId(expr->RightId());
6009 set_current_block(eval_right);
6010 Visit(expr->right());
6011 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006012
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006013 } else if (ast_context()->IsValue()) {
6014 CHECK_ALIVE(VisitForValue(expr->left()));
6015 ASSERT(current_block() != NULL);
6016
6017 // We need an extra block to maintain edge-split form.
6018 HBasicBlock* empty_block = graph()->CreateBasicBlock();
6019 HBasicBlock* eval_right = graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00006020 unsigned test_id = expr->left()->test_id();
6021 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006022 HBranch* test = is_logical_and
ricow@chromium.org2c99e282011-07-28 09:15:17 +00006023 ? new(zone()) HBranch(Top(), eval_right, empty_block, expected)
6024 : new(zone()) HBranch(Top(), empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006025 current_block()->Finish(test);
6026
6027 set_current_block(eval_right);
6028 Drop(1); // Value of the left subexpression.
6029 CHECK_BAILOUT(VisitForValue(expr->right()));
6030
6031 HBasicBlock* join_block =
6032 CreateJoin(empty_block, current_block(), expr->id());
6033 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006034 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006035
6036 } else {
6037 ASSERT(ast_context()->IsEffect());
6038 // In an effect context, we don't need the value of the left subexpression,
6039 // only its control flow and side effects. We need an extra block to
6040 // maintain edge-split form.
6041 HBasicBlock* empty_block = graph()->CreateBasicBlock();
6042 HBasicBlock* right_block = graph()->CreateBasicBlock();
6043 if (is_logical_and) {
6044 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
6045 } else {
6046 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
6047 }
6048
6049 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
6050 // actually two empty blocks (one here and one inserted by
6051 // TestContext::BuildBranch, and that they both have an HSimulate though the
6052 // second one is not a merge node, and that we really have no good AST ID to
6053 // put on that first HSimulate.
6054
6055 if (empty_block->HasPredecessor()) {
6056 empty_block->SetJoinId(expr->id());
6057 } else {
6058 empty_block = NULL;
6059 }
6060
6061 if (right_block->HasPredecessor()) {
6062 right_block->SetJoinId(expr->RightId());
6063 set_current_block(right_block);
6064 CHECK_BAILOUT(VisitForEffect(expr->right()));
6065 right_block = current_block();
6066 } else {
6067 right_block = NULL;
6068 }
6069
6070 HBasicBlock* join_block =
6071 CreateJoin(empty_block, right_block, expr->id());
6072 set_current_block(join_block);
6073 // We did not materialize any value in the predecessor environments,
6074 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006075 }
6076}
6077
6078
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006079void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006080 CHECK_ALIVE(VisitForValue(expr->left()));
6081 CHECK_ALIVE(VisitForValue(expr->right()));
6082 HValue* right = Pop();
6083 HValue* left = Pop();
6084 HInstruction* instr = BuildBinaryOperation(expr, left, right);
6085 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006086 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006087}
6088
6089
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006090void HGraphBuilder::TraceRepresentation(Token::Value op,
6091 TypeInfo info,
6092 HValue* value,
6093 Representation rep) {
6094 if (!FLAG_trace_representation) return;
6095 // TODO(svenpanne) Under which circumstances are we actually not flexible?
6096 // At first glance, this looks a bit weird...
6097 bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
6098 PrintF("Operation %s has type info %s, %schange representation assumption "
6099 "for %s (ID %d) from %s to %s\n",
6100 Token::Name(op),
6101 info.ToString(),
6102 flexible ? "" : " DO NOT ",
6103 value->Mnemonic(),
6104 graph_->GetMaximumValueID(),
6105 value->representation().Mnemonic(),
6106 rep.Mnemonic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006107}
6108
6109
6110Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
6111 if (info.IsSmi()) return Representation::Integer32();
6112 if (info.IsInteger32()) return Representation::Integer32();
6113 if (info.IsDouble()) return Representation::Double();
6114 if (info.IsNumber()) return Representation::Double();
6115 return Representation::Tagged();
6116}
6117
6118
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006119void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006120 HTypeof* typeof_expr,
ager@chromium.org04921a82011-06-27 13:21:41 +00006121 Handle<String> check) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006122 // Note: The HTypeof itself is removed during canonicalization, if possible.
6123 HValue* value = typeof_expr->value();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006124 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
6125 instr->set_position(expr->position());
6126 return ast_context()->ReturnControl(instr, expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00006127}
6128
6129
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006130static bool MatchLiteralCompareNil(HValue* left,
6131 Token::Value op,
6132 HValue* right,
6133 Handle<Object> nil,
6134 HValue** expr) {
6135 if (left->IsConstant() &&
6136 HConstant::cast(left)->handle().is_identical_to(nil) &&
6137 Token::IsEqualityOp(op)) {
6138 *expr = right;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006139 return true;
6140 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006141 return false;
ager@chromium.org04921a82011-06-27 13:21:41 +00006142}
6143
6144
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006145static bool MatchLiteralCompareTypeof(HValue* left,
6146 Token::Value op,
6147 HValue* right,
6148 HTypeof** typeof_expr,
6149 Handle<String>* check) {
6150 if (left->IsTypeof() &&
6151 Token::IsEqualityOp(op) &&
6152 right->IsConstant() &&
6153 HConstant::cast(right)->HasStringValue()) {
6154 *typeof_expr = HTypeof::cast(left);
6155 *check = Handle<String>::cast(HConstant::cast(right)->handle());
6156 return true;
6157 }
6158 return false;
6159}
6160
6161
6162static bool IsLiteralCompareTypeof(HValue* left,
6163 Token::Value op,
6164 HValue* right,
6165 HTypeof** typeof_expr,
6166 Handle<String>* check) {
6167 return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) ||
6168 MatchLiteralCompareTypeof(right, op, left, typeof_expr, check);
6169}
6170
6171
6172static bool IsLiteralCompareNil(HValue* left,
6173 Token::Value op,
6174 HValue* right,
6175 Handle<Object> nil,
6176 HValue** expr) {
6177 return MatchLiteralCompareNil(left, op, right, nil, expr) ||
6178 MatchLiteralCompareNil(right, op, left, nil, expr);
6179}
6180
6181
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006182static bool IsLiteralCompareBool(HValue* left,
6183 Token::Value op,
6184 HValue* right) {
6185 return op == Token::EQ_STRICT &&
6186 ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) ||
6187 (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean()));
6188}
6189
6190
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006191void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006192 ASSERT(!HasStackOverflow());
6193 ASSERT(current_block() != NULL);
6194 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006195 if (IsClassOfTest(expr)) {
6196 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006197 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006198 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006199 HValue* value = Pop();
6200 Literal* literal = expr->right()->AsLiteral();
6201 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006202 HClassOfTestAndBranch* instr =
6203 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006204 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006205 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006206 }
6207
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006208 TypeInfo type_info = oracle()->CompareType(expr);
6209 // Check if this expression was ever executed according to type feedback.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006210 // Note that for the special typeof/null/undefined cases we get unknown here.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006211 if (type_info.IsUninitialized()) {
6212 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00006213 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006214 type_info = TypeInfo::Unknown();
6215 }
6216
danno@chromium.org160a7b02011-04-18 15:51:38 +00006217 CHECK_ALIVE(VisitForValue(expr->left()));
6218 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006219
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006220 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006221 HValue* right = Pop();
6222 HValue* left = Pop();
6223 Token::Value op = expr->op();
6224
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006225 HTypeof* typeof_expr = NULL;
6226 Handle<String> check;
6227 if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) {
6228 return HandleLiteralCompareTypeof(expr, typeof_expr, check);
6229 }
6230 HValue* sub_expr = NULL;
6231 Factory* f = graph()->isolate()->factory();
6232 if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) {
6233 return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
6234 }
6235 if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) {
6236 return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
6237 }
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006238 if (IsLiteralCompareBool(left, op, right)) {
6239 HCompareObjectEqAndBranch* result =
6240 new(zone()) HCompareObjectEqAndBranch(left, right);
6241 result->set_position(expr->position());
6242 return ast_context()->ReturnControl(result, expr->id());
6243 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006244
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006245 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006246 // Check to see if the rhs of the instanceof is a global function not
6247 // residing in new space. If it is we assume that the function will stay the
6248 // same.
6249 Handle<JSFunction> target = Handle<JSFunction>::null();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006250 VariableProxy* proxy = expr->right()->AsVariableProxy();
6251 bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006252 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006253 info()->has_global_object() &&
6254 !info()->global_object()->IsAccessCheckNeeded()) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006255 Handle<String> name = proxy->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006256 Handle<GlobalObject> global(info()->global_object());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006257 LookupResult lookup(isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006258 global->Lookup(*name, &lookup);
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00006259 if (lookup.IsFound() &&
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006260 lookup.type() == NORMAL &&
6261 lookup.GetValue()->IsJSFunction()) {
6262 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
6263 // If the function is in new space we assume it's more likely to
6264 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006265 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006266 target = candidate;
6267 }
6268 }
6269 }
6270
6271 // If the target is not null we have found a known global function that is
6272 // assumed to stay the same for this instanceof.
6273 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006274 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
6275 result->set_position(expr->position());
6276 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006277 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006278 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006279 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006280 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006281 result->set_position(expr->position());
6282 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00006283 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006284 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006285 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006286 result->set_position(expr->position());
6287 return ast_context()->ReturnInstruction(result, expr->id());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006288 } else if (type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006289 switch (op) {
6290 case Token::EQ:
6291 case Token::EQ_STRICT: {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006292 // Can we get away with map check and not instance type check?
6293 Handle<Map> map = oracle()->GetCompareMap(expr);
6294 if (!map.is_null()) {
6295 AddInstruction(new(zone()) HCheckNonSmi(left));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006296 AddInstruction(new(zone()) HCheckMap(left, map, NULL,
6297 ALLOW_ELEMENT_TRANSITION_MAPS));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006298 AddInstruction(new(zone()) HCheckNonSmi(right));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006299 AddInstruction(new(zone()) HCheckMap(right, map, NULL,
6300 ALLOW_ELEMENT_TRANSITION_MAPS));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006301 HCompareObjectEqAndBranch* result =
6302 new(zone()) HCompareObjectEqAndBranch(left, right);
6303 result->set_position(expr->position());
6304 return ast_context()->ReturnControl(result, expr->id());
6305 } else {
6306 AddInstruction(new(zone()) HCheckNonSmi(left));
6307 AddInstruction(HCheckInstanceType::NewIsSpecObject(left));
6308 AddInstruction(new(zone()) HCheckNonSmi(right));
6309 AddInstruction(HCheckInstanceType::NewIsSpecObject(right));
6310 HCompareObjectEqAndBranch* result =
6311 new(zone()) HCompareObjectEqAndBranch(left, right);
6312 result->set_position(expr->position());
6313 return ast_context()->ReturnControl(result, expr->id());
6314 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006315 }
6316 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00006317 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006318 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006319 } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
6320 (op == Token::EQ || op == Token::EQ_STRICT)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00006321 AddInstruction(new(zone()) HCheckNonSmi(left));
6322 AddInstruction(HCheckInstanceType::NewIsSymbol(left));
6323 AddInstruction(new(zone()) HCheckNonSmi(right));
6324 AddInstruction(HCheckInstanceType::NewIsSymbol(right));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006325 HCompareObjectEqAndBranch* result =
6326 new(zone()) HCompareObjectEqAndBranch(left, right);
6327 result->set_position(expr->position());
6328 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006329 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006330 Representation r = ToRepresentation(type_info);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006331 if (r.IsTagged()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006332 HCompareGeneric* result =
6333 new(zone()) HCompareGeneric(context, left, right, op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006334 result->set_position(expr->position());
6335 return ast_context()->ReturnInstruction(result, expr->id());
6336 } else {
6337 HCompareIDAndBranch* result =
6338 new(zone()) HCompareIDAndBranch(left, right, op);
6339 result->set_position(expr->position());
6340 result->SetInputRepresentation(r);
6341 return ast_context()->ReturnControl(result, expr->id());
6342 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006343 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006344}
6345
6346
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006347void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006348 HValue* value,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006349 NilValue nil) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006350 ASSERT(!HasStackOverflow());
6351 ASSERT(current_block() != NULL);
6352 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006353 EqualityKind kind =
6354 expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
6355 HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
6356 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006357 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006358}
6359
6360
6361void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006362 ASSERT(!HasStackOverflow());
6363 ASSERT(current_block() != NULL);
6364 ASSERT(current_block()->HasPredecessor());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006365 HThisFunction* self = new(zone()) HThisFunction(
6366 function_state()->compilation_info()->closure());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006367 return ast_context()->ReturnInstruction(self, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006368}
6369
6370
6371void HGraphBuilder::VisitDeclaration(Declaration* decl) {
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006372 HandleDeclaration(decl->proxy(), decl->mode(), decl->fun());
6373}
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006374
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006375
6376void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +00006377 VariableMode mode,
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006378 FunctionLiteral* function) {
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006379 Variable* var = proxy->var();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006380 bool binding_needs_init =
6381 (mode == CONST || mode == CONST_HARMONY || mode == LET);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006382 switch (var->location()) {
6383 case Variable::UNALLOCATED:
6384 return Bailout("unsupported global declaration");
6385 case Variable::PARAMETER:
6386 case Variable::LOCAL:
6387 case Variable::CONTEXT:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006388 if (binding_needs_init || function != NULL) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006389 HValue* value = NULL;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006390 if (function != NULL) {
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006391 VisitForValue(function);
6392 value = Pop();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006393 } else {
6394 value = graph()->GetConstantHole();
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006395 }
6396 if (var->IsContextSlot()) {
6397 HValue* context = environment()->LookupContext();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006398 HStoreContextSlot* store = new HStoreContextSlot(
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006399 context, var->index(), HStoreContextSlot::kNoCheck, value);
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006400 AddInstruction(store);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006401 if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006402 } else {
6403 environment()->Bind(var, value);
6404 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006405 }
6406 break;
jkummerow@chromium.org486075a2011-09-07 12:44:28 +00006407 case Variable::LOOKUP:
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006408 return Bailout("unsupported lookup slot in declaration");
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006409 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006410}
6411
6412
6413// Generators for inline runtime functions.
6414// Support for types.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006415void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
6416 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006417 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006418 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006419 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
6420 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006421}
6422
6423
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006424void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
6425 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006426 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006427 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006428 HHasInstanceTypeAndBranch* result =
6429 new(zone()) HHasInstanceTypeAndBranch(value,
6430 FIRST_SPEC_OBJECT_TYPE,
6431 LAST_SPEC_OBJECT_TYPE);
6432 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006433}
6434
6435
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006436void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
6437 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006438 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006439 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006440 HHasInstanceTypeAndBranch* result =
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006441 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006442 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006443}
6444
6445
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006446void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
6447 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006448 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006449 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006450 HHasCachedArrayIndexAndBranch* result =
6451 new(zone()) HHasCachedArrayIndexAndBranch(value);
6452 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006453}
6454
6455
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006456void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
6457 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006458 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006459 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006460 HHasInstanceTypeAndBranch* result =
6461 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
6462 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006463}
6464
6465
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006466void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
6467 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006468 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006469 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006470 HHasInstanceTypeAndBranch* result =
6471 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
6472 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006473}
6474
6475
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006476void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
6477 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006478 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006479 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006480 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
6481 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006482}
6483
6484
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006485void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006486 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006487}
6488
6489
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006490void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006491 ASSERT(call->arguments()->length() == 1);
6492 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6493 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006494 HIsUndetectableAndBranch* result =
6495 new(zone()) HIsUndetectableAndBranch(value);
6496 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006497}
6498
6499
6500void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006501 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006502 return Bailout(
6503 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006504}
6505
6506
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00006507// Support for construct call checks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006508void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
6509 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006510 if (function_state()->outer() != NULL) {
6511 // We are generating graph for inlined function. Currently
6512 // constructor inlining is not supported and we can just return
6513 // false from %_IsConstructCall().
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006514 return ast_context()->ReturnValue(graph()->GetConstantFalse());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006515 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006516 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
6517 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006518 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006519}
6520
6521
6522// Support for arguments.length and arguments[?].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006523void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006524 // Our implementation of arguments (based on this stack frame or an
6525 // adapter below it) does not work for inlined functions. This runtime
6526 // function is blacklisted by AstNode::IsInlineable.
6527 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006528 ASSERT(call->arguments()->length() == 0);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006529 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
6530 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006531 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006532}
6533
6534
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006535void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006536 // Our implementation of arguments (based on this stack frame or an
6537 // adapter below it) does not work for inlined functions. This runtime
6538 // function is blacklisted by AstNode::IsInlineable.
6539 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006540 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006541 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006542 HValue* index = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006543 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
6544 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
6545 HAccessArgumentsAt* result =
6546 new(zone()) HAccessArgumentsAt(elements, length, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006547 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006548}
6549
6550
6551// Support for accessing the class and value fields of an object.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006552void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006553 // The special form detected by IsClassOfTest is detected before we get here
6554 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00006555 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006556}
6557
6558
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006559void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
6560 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006561 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006562 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006563 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006564 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006565}
6566
6567
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006568void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org2c456792011-11-11 12:00:53 +00006569 ASSERT(call->arguments()->length() == 2);
6570 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6571 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
6572 HValue* value = Pop();
6573 HValue* object = Pop();
6574 // Check if object is a not a smi.
6575 HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object);
6576 HBasicBlock* if_smi = graph()->CreateBasicBlock();
6577 HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
6578 HBasicBlock* join = graph()->CreateBasicBlock();
6579 smicheck->SetSuccessorAt(0, if_smi);
6580 smicheck->SetSuccessorAt(1, if_heap_object);
6581 current_block()->Finish(smicheck);
6582 if_smi->Goto(join);
6583
6584 // Check if object is a JSValue.
6585 set_current_block(if_heap_object);
6586 HHasInstanceTypeAndBranch* typecheck =
6587 new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE);
6588 HBasicBlock* if_js_value = graph()->CreateBasicBlock();
6589 HBasicBlock* not_js_value = graph()->CreateBasicBlock();
6590 typecheck->SetSuccessorAt(0, if_js_value);
6591 typecheck->SetSuccessorAt(1, not_js_value);
6592 current_block()->Finish(typecheck);
6593 not_js_value->Goto(join);
6594
6595 // Create in-object property store to kValueOffset.
6596 set_current_block(if_js_value);
6597 Handle<String> name = isolate()->factory()->undefined_symbol();
6598 AddInstruction(new HStoreNamedField(object,
6599 name,
6600 value,
6601 true, // in-object store.
6602 JSValue::kValueOffset));
6603 if_js_value->Goto(join);
6604 join->SetJoinId(call->id());
6605 set_current_block(join);
6606 return ast_context()->ReturnValue(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006607}
6608
6609
6610// Fast support for charCodeAt(n).
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006611void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
6612 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006613 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6614 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00006615 HValue* index = Pop();
6616 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006617 HValue* context = environment()->LookupContext();
6618 HStringCharCodeAt* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006619 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006620}
6621
6622
6623// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006624void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006625 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006626 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006627 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006628 HValue* context = environment()->LookupContext();
6629 HStringCharFromCode* result =
6630 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006631 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006632}
6633
6634
6635// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006636void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006637 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006638 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6639 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006640 HValue* index = Pop();
6641 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006642 HValue* context = environment()->LookupContext();
6643 HStringCharCodeAt* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006644 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006645 HStringCharFromCode* result =
6646 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006647 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006648}
6649
6650
6651// Fast support for object equality testing.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006652void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
6653 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006654 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6655 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006656 HValue* right = Pop();
6657 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006658 HCompareObjectEqAndBranch* result =
6659 new(zone()) HCompareObjectEqAndBranch(left, right);
6660 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006661}
6662
6663
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006664void HGraphBuilder::GenerateLog(CallRuntime* call) {
6665 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006666 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006667}
6668
6669
6670// Fast support for Math.random().
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006671void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006672 HValue* context = environment()->LookupContext();
6673 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
6674 AddInstruction(global_object);
6675 HRandom* result = new(zone()) HRandom(global_object);
6676 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006677}
6678
6679
6680// Fast support for StringAdd.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006681void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
6682 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006683 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006684 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006685 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006686 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006687 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006688}
6689
6690
6691// Fast support for SubString.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006692void HGraphBuilder::GenerateSubString(CallRuntime* call) {
6693 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006694 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006695 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006696 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006697 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006698 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006699}
6700
6701
6702// Fast support for StringCompare.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006703void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
6704 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006705 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006706 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006707 HCallStub* result =
6708 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006709 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006710 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006711}
6712
6713
6714// Support for direct calls from JavaScript to native RegExp code.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006715void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
6716 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006717 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006718 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006719 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006720 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006721 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006722}
6723
6724
6725// Construct a RegExp exec result with two in-object properties.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006726void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
6727 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006728 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006729 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006730 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006731 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006732 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006733 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006734}
6735
6736
6737// Support for fast native caches.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006738void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006739 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006740}
6741
6742
6743// Fast support for number to string.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006744void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
6745 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006746 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006747 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006748 HCallStub* result =
6749 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006750 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006751 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006752}
6753
6754
6755// Fast swapping of elements. Takes three expressions, the object and two
6756// indices. This should only be used if the indices are known to be
6757// non-negative and within bounds of the elements array at the call site.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006758void HGraphBuilder::GenerateSwapElements(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006759 return Bailout("inlined runtime function: SwapElements");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006760}
6761
6762
6763// Fast call for custom callbacks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006764void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006765 // 1 ~ The function to call is not itself an argument to the call.
6766 int arg_count = call->arguments()->length() - 1;
6767 ASSERT(arg_count >= 1); // There's always at least a receiver.
6768
6769 for (int i = 0; i < arg_count; ++i) {
6770 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
6771 }
6772 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
danno@chromium.orgc612e022011-11-10 11:38:15 +00006773
danno@chromium.org160a7b02011-04-18 15:51:38 +00006774 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006775 HValue* context = environment()->LookupContext();
danno@chromium.orgc612e022011-11-10 11:38:15 +00006776
6777 // Branch for function proxies, or other non-functions.
6778 HHasInstanceTypeAndBranch* typecheck =
6779 new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
6780 HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
6781 HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
6782 HBasicBlock* join = graph()->CreateBasicBlock();
6783 typecheck->SetSuccessorAt(0, if_jsfunction);
6784 typecheck->SetSuccessorAt(1, if_nonfunction);
6785 current_block()->Finish(typecheck);
6786
6787 set_current_block(if_jsfunction);
6788 HInstruction* invoke_result = AddInstruction(
6789 new(zone()) HInvokeFunction(context, function, arg_count));
danno@chromium.org160a7b02011-04-18 15:51:38 +00006790 Drop(arg_count);
danno@chromium.orgc612e022011-11-10 11:38:15 +00006791 Push(invoke_result);
6792 if_jsfunction->Goto(join);
6793
6794 set_current_block(if_nonfunction);
6795 HInstruction* call_result = AddInstruction(
6796 new(zone()) HCallFunction(context, function, arg_count));
6797 Drop(arg_count);
6798 Push(call_result);
6799 if_nonfunction->Goto(join);
6800
6801 set_current_block(join);
6802 join->SetJoinId(call->id());
6803 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006804}
6805
6806
6807// Fast call to math functions.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006808void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
6809 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006810 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6811 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006812 HValue* right = Pop();
6813 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006814 HPower* result = new(zone()) HPower(left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006815 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006816}
6817
6818
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006819void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
6820 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006821 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006822 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006823 HCallStub* result =
6824 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006825 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006826 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006827 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006828}
6829
6830
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006831void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
6832 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006833 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006834 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006835 HCallStub* result =
6836 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006837 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006838 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006839 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006840}
6841
6842
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00006843void HGraphBuilder::GenerateMathTan(CallRuntime* call) {
6844 ASSERT_EQ(1, call->arguments()->length());
6845 CHECK_ALIVE(VisitArgumentList(call->arguments()));
6846 HValue* context = environment()->LookupContext();
6847 HCallStub* result =
6848 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
6849 result->set_transcendental_type(TranscendentalCache::TAN);
6850 Drop(1);
6851 return ast_context()->ReturnInstruction(result, call->id());
6852}
6853
6854
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006855void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
6856 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006857 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006858 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006859 HCallStub* result =
6860 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006861 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006862 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006863 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006864}
6865
6866
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006867void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006868 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006869}
6870
6871
6872// Check whether two RegExps are equivalent
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006873void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006874 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006875}
6876
6877
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006878void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
6879 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006880 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00006881 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006882 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006883 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006884}
6885
6886
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006887void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006888 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006889}
6890
6891
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006892#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +00006893#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006894
6895
6896HEnvironment::HEnvironment(HEnvironment* outer,
6897 Scope* scope,
6898 Handle<JSFunction> closure)
6899 : closure_(closure),
6900 values_(0),
6901 assigned_variables_(4),
6902 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006903 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006904 local_count_(0),
6905 outer_(outer),
6906 pop_count_(0),
6907 push_count_(0),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006908 ast_id_(AstNode::kNoNumber),
6909 arguments_adaptor_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006910 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
6911}
6912
6913
6914HEnvironment::HEnvironment(const HEnvironment* other)
6915 : values_(0),
6916 assigned_variables_(0),
6917 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006918 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006919 local_count_(0),
6920 outer_(NULL),
6921 pop_count_(0),
6922 push_count_(0),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006923 ast_id_(other->ast_id()),
6924 arguments_adaptor_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006925 Initialize(other);
6926}
6927
6928
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006929HEnvironment::HEnvironment(HEnvironment* outer,
6930 Handle<JSFunction> closure,
6931 int arguments)
6932 : closure_(closure),
6933 values_(arguments),
6934 assigned_variables_(0),
6935 parameter_count_(arguments),
6936 local_count_(0),
6937 outer_(outer),
6938 pop_count_(0),
6939 push_count_(0),
6940 ast_id_(AstNode::kNoNumber),
6941 arguments_adaptor_(true) {
6942}
6943
6944
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006945void HEnvironment::Initialize(int parameter_count,
6946 int local_count,
6947 int stack_height) {
6948 parameter_count_ = parameter_count;
6949 local_count_ = local_count;
6950
6951 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006952 int total = parameter_count + specials_count_ + local_count + stack_height;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006953 values_.Initialize(total + 4);
6954 for (int i = 0; i < total; ++i) values_.Add(NULL);
6955}
6956
6957
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006958void HEnvironment::Initialize(const HEnvironment* other) {
6959 closure_ = other->closure();
6960 values_.AddAll(other->values_);
6961 assigned_variables_.AddAll(other->assigned_variables_);
6962 parameter_count_ = other->parameter_count_;
6963 local_count_ = other->local_count_;
6964 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
6965 pop_count_ = other->pop_count_;
6966 push_count_ = other->push_count_;
6967 ast_id_ = other->ast_id_;
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00006968 arguments_adaptor_ = other->arguments_adaptor_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006969}
6970
6971
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006972void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
6973 ASSERT(!block->IsLoopHeader());
6974 ASSERT(values_.length() == other->values_.length());
6975
6976 int length = values_.length();
6977 for (int i = 0; i < length; ++i) {
6978 HValue* value = values_[i];
6979 if (value != NULL && value->IsPhi() && value->block() == block) {
6980 // There is already a phi for the i'th value.
6981 HPhi* phi = HPhi::cast(value);
6982 // Assert index is correct and that we haven't missed an incoming edge.
6983 ASSERT(phi->merged_index() == i);
6984 ASSERT(phi->OperandCount() == block->predecessors()->length());
6985 phi->AddInput(other->values_[i]);
6986 } else if (values_[i] != other->values_[i]) {
6987 // There is a fresh value on the incoming edge, a phi is needed.
6988 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006989 HPhi* phi = new(block->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006990 HValue* old_value = values_[i];
6991 for (int j = 0; j < block->predecessors()->length(); j++) {
6992 phi->AddInput(old_value);
6993 }
6994 phi->AddInput(other->values_[i]);
6995 this->values_[i] = phi;
6996 block->AddPhi(phi);
6997 }
6998 }
6999}
7000
7001
lrn@chromium.org5d00b602011-01-05 09:51:43 +00007002void HEnvironment::Bind(int index, HValue* value) {
7003 ASSERT(value != NULL);
7004 if (!assigned_variables_.Contains(index)) {
7005 assigned_variables_.Add(index);
7006 }
7007 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007008}
7009
7010
lrn@chromium.org5d00b602011-01-05 09:51:43 +00007011bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007012 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00007013}
7014
7015
7016bool HEnvironment::ExpressionStackIsEmpty() const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007017 int first_expression = parameter_count() + specials_count() + local_count();
lrn@chromium.org5d00b602011-01-05 09:51:43 +00007018 ASSERT(length() >= first_expression);
7019 return length() == first_expression;
7020}
7021
7022
7023void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
7024 int count = index_from_top + 1;
7025 int index = values_.length() - count;
7026 ASSERT(HasExpressionAt(index));
7027 // The push count must include at least the element in question or else
7028 // the new value will not be included in this environment's history.
7029 if (push_count_ < count) {
7030 // This is the same effect as popping then re-pushing 'count' elements.
7031 pop_count_ += (count - push_count_);
7032 push_count_ = count;
7033 }
7034 values_[index] = value;
7035}
7036
7037
7038void HEnvironment::Drop(int count) {
7039 for (int i = 0; i < count; ++i) {
7040 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007041 }
7042}
7043
7044
7045HEnvironment* HEnvironment::Copy() const {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007046 return new(closure()->GetIsolate()->zone()) HEnvironment(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007047}
7048
7049
7050HEnvironment* HEnvironment::CopyWithoutHistory() const {
7051 HEnvironment* result = Copy();
7052 result->ClearHistory();
7053 return result;
7054}
7055
7056
7057HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
7058 HEnvironment* new_env = Copy();
7059 for (int i = 0; i < values_.length(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007060 HPhi* phi = new(loop_header->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007061 phi->AddInput(values_[i]);
7062 new_env->values_[i] = phi;
7063 loop_header->AddPhi(phi);
7064 }
7065 new_env->ClearHistory();
7066 return new_env;
7067}
7068
7069
danno@chromium.org40cb8782011-05-25 07:58:50 +00007070HEnvironment* HEnvironment::CopyForInlining(
7071 Handle<JSFunction> target,
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007072 int arguments,
danno@chromium.org40cb8782011-05-25 07:58:50 +00007073 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00007074 HConstant* undefined,
7075 CallKind call_kind) const {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007076 ASSERT(!is_arguments_adaptor());
7077
7078 Zone* zone = closure()->GetIsolate()->zone();
7079
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007080 // Outer environment is a copy of this one without the arguments.
7081 int arity = function->scope()->num_parameters();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007082
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007083 HEnvironment* outer = Copy();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007084 outer->Drop(arguments + 1); // Including receiver.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007085 outer->ClearHistory();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007086
7087 if (arity != arguments) {
7088 // Create artificial arguments adaptation environment.
7089 outer = new(zone) HEnvironment(outer, target, arguments + 1);
7090 for (int i = 0; i <= arguments; ++i) { // Include receiver.
7091 outer->Push(ExpressionStackAt(arguments - i));
7092 }
7093 outer->ClearHistory();
7094 }
7095
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00007096 HEnvironment* inner =
7097 new(zone) HEnvironment(outer, function->scope(), target);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007098 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007099 for (int i = 0; i <= arity; ++i) { // Include receiver.
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007100 HValue* push = (i <= arguments) ?
7101 ExpressionStackAt(arguments - i) : undefined;
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00007102 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007103 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007104 // If the function we are inlining is a strict mode function or a
7105 // builtin function, pass undefined as the receiver for function
7106 // calls (instead of the global receiver).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00007107 if ((target->shared()->native() || !function->is_classic_mode()) &&
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007108 call_kind == CALL_AS_FUNCTION) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00007109 inner->SetValueAt(0, undefined);
7110 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007111 inner->SetValueAt(arity + 1, LookupContext());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007112 for (int i = arity + 2; i < inner->length(); ++i) {
7113 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007114 }
7115
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007116 inner->set_ast_id(AstNode::kFunctionEntryId);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007117 return inner;
7118}
7119
7120
7121void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00007122 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007123 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007124 if (i == parameter_count()) stream->Add("specials\n");
7125 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
7126 if (i == parameter_count() + specials_count() + local_count()) {
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007127 stream->Add("expressions\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007128 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007129 HValue* val = values_.at(i);
7130 stream->Add("%d: ", i);
7131 if (val != NULL) {
7132 val->PrintNameTo(stream);
7133 } else {
7134 stream->Add("NULL");
7135 }
7136 stream->Add("\n");
7137 }
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00007138 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007139}
7140
7141
7142void HEnvironment::PrintToStd() {
7143 HeapStringAllocator string_allocator;
7144 StringStream trace(&string_allocator);
7145 PrintTo(&trace);
7146 PrintF("%s", *trace.ToCString());
7147}
7148
7149
7150void HTracer::TraceCompilation(FunctionLiteral* function) {
7151 Tag tag(this, "compilation");
7152 Handle<String> name = function->debug_name();
7153 PrintStringProperty("name", *name->ToCString());
7154 PrintStringProperty("method", *name->ToCString());
7155 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
7156}
7157
7158
7159void HTracer::TraceLithium(const char* name, LChunk* chunk) {
7160 Trace(name, chunk->graph(), chunk);
7161}
7162
7163
7164void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
7165 Trace(name, graph, NULL);
7166}
7167
7168
7169void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
7170 Tag tag(this, "cfg");
7171 PrintStringProperty("name", name);
7172 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
7173 for (int i = 0; i < blocks->length(); i++) {
7174 HBasicBlock* current = blocks->at(i);
7175 Tag block_tag(this, "block");
7176 PrintBlockProperty("name", current->block_id());
7177 PrintIntProperty("from_bci", -1);
7178 PrintIntProperty("to_bci", -1);
7179
7180 if (!current->predecessors()->is_empty()) {
7181 PrintIndent();
7182 trace_.Add("predecessors");
7183 for (int j = 0; j < current->predecessors()->length(); ++j) {
7184 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
7185 }
7186 trace_.Add("\n");
7187 } else {
7188 PrintEmptyProperty("predecessors");
7189 }
7190
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007191 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007192 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007193 } else {
7194 PrintIndent();
7195 trace_.Add("successors");
7196 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
7197 trace_.Add(" \"B%d\"", it.Current()->block_id());
7198 }
7199 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007200 }
7201
7202 PrintEmptyProperty("xhandlers");
7203 PrintEmptyProperty("flags");
7204
7205 if (current->dominator() != NULL) {
7206 PrintBlockProperty("dominator", current->dominator()->block_id());
7207 }
7208
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00007209 PrintIntProperty("loop_depth", current->LoopNestingDepth());
7210
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007211 if (chunk != NULL) {
7212 int first_index = current->first_instruction_index();
7213 int last_index = current->last_instruction_index();
7214 PrintIntProperty(
7215 "first_lir_id",
7216 LifetimePosition::FromInstructionIndex(first_index).Value());
7217 PrintIntProperty(
7218 "last_lir_id",
7219 LifetimePosition::FromInstructionIndex(last_index).Value());
7220 }
7221
7222 {
7223 Tag states_tag(this, "states");
7224 Tag locals_tag(this, "locals");
7225 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007226 PrintIntProperty("size", current->phis()->length());
7227 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007228 for (int j = 0; j < total; ++j) {
7229 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007230 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007231 trace_.Add("%d ", phi->merged_index());
7232 phi->PrintNameTo(&trace_);
7233 trace_.Add(" ");
7234 phi->PrintTo(&trace_);
7235 trace_.Add("\n");
7236 }
7237 }
7238
7239 {
7240 Tag HIR_tag(this, "HIR");
7241 HInstruction* instruction = current->first();
7242 while (instruction != NULL) {
7243 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007244 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007245 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007246 trace_.Add("%d %d ", bci, uses);
7247 instruction->PrintNameTo(&trace_);
7248 trace_.Add(" ");
7249 instruction->PrintTo(&trace_);
7250 trace_.Add(" <|@\n");
7251 instruction = instruction->next();
7252 }
7253 }
7254
7255
7256 if (chunk != NULL) {
7257 Tag LIR_tag(this, "LIR");
7258 int first_index = current->first_instruction_index();
7259 int last_index = current->last_instruction_index();
7260 if (first_index != -1 && last_index != -1) {
7261 const ZoneList<LInstruction*>* instructions = chunk->instructions();
7262 for (int i = first_index; i <= last_index; ++i) {
7263 LInstruction* linstr = instructions->at(i);
7264 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007265 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007266 trace_.Add("%d ",
7267 LifetimePosition::FromInstructionIndex(i).Value());
7268 linstr->PrintTo(&trace_);
7269 trace_.Add(" <|@\n");
7270 }
7271 }
7272 }
7273 }
7274 }
7275}
7276
7277
7278void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
7279 Tag tag(this, "intervals");
7280 PrintStringProperty("name", name);
7281
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007282 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007283 for (int i = 0; i < fixed_d->length(); ++i) {
7284 TraceLiveRange(fixed_d->at(i), "fixed");
7285 }
7286
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007287 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007288 for (int i = 0; i < fixed->length(); ++i) {
7289 TraceLiveRange(fixed->at(i), "fixed");
7290 }
7291
7292 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
7293 for (int i = 0; i < live_ranges->length(); ++i) {
7294 TraceLiveRange(live_ranges->at(i), "object");
7295 }
7296}
7297
7298
7299void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
7300 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007301 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007302 trace_.Add("%d %s", range->id(), type);
7303 if (range->HasRegisterAssigned()) {
7304 LOperand* op = range->CreateAssignedOperand();
7305 int assigned_reg = op->index();
7306 if (op->IsDoubleRegister()) {
7307 trace_.Add(" \"%s\"",
7308 DoubleRegister::AllocationIndexToString(assigned_reg));
7309 } else {
7310 ASSERT(op->IsRegister());
7311 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
7312 }
7313 } else if (range->IsSpilled()) {
7314 LOperand* op = range->TopLevel()->GetSpillOperand();
7315 if (op->IsDoubleStackSlot()) {
7316 trace_.Add(" \"double_stack:%d\"", op->index());
7317 } else {
7318 ASSERT(op->IsStackSlot());
7319 trace_.Add(" \"stack:%d\"", op->index());
7320 }
7321 }
7322 int parent_index = -1;
7323 if (range->IsChild()) {
7324 parent_index = range->parent()->id();
7325 } else {
7326 parent_index = range->id();
7327 }
7328 LOperand* op = range->FirstHint();
7329 int hint_index = -1;
danno@chromium.orgfa458e42012-02-01 10:48:36 +00007330 if (op != NULL && op->IsUnallocated()) {
7331 hint_index = LUnallocated::cast(op)->virtual_register();
7332 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007333 trace_.Add(" %d %d", parent_index, hint_index);
7334 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007335 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007336 trace_.Add(" [%d, %d[",
7337 cur_interval->start().Value(),
7338 cur_interval->end().Value());
7339 cur_interval = cur_interval->next();
7340 }
7341
7342 UsePosition* current_pos = range->first_pos();
7343 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00007344 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007345 trace_.Add(" %d M", current_pos->pos().Value());
7346 }
7347 current_pos = current_pos->next();
7348 }
7349
7350 trace_.Add(" \"\"\n");
7351 }
7352}
7353
7354
7355void HTracer::FlushToFile() {
7356 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
7357 trace_.Reset();
7358}
7359
7360
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007361void HStatistics::Initialize(CompilationInfo* info) {
7362 source_size_ += info->shared_info()->SourceSize();
7363}
7364
7365
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007366void HStatistics::Print() {
7367 PrintF("Timing results:\n");
7368 int64_t sum = 0;
7369 for (int i = 0; i < timing_.length(); ++i) {
7370 sum += timing_[i];
7371 }
7372
7373 for (int i = 0; i < names_.length(); ++i) {
7374 PrintF("%30s", names_[i]);
7375 double ms = static_cast<double>(timing_[i]) / 1000;
7376 double percent = static_cast<double>(timing_[i]) * 100 / sum;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007377 PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
7378
7379 unsigned size = sizes_[i];
7380 double size_percent = static_cast<double>(size) * 100 / total_size_;
7381 PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007382 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007383 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00007384 double normalized_time = source_size_in_kb > 0
7385 ? (static_cast<double>(sum) / 1000) / source_size_in_kb
7386 : 0;
7387 double normalized_bytes = source_size_in_kb > 0
7388 ? total_size_ / source_size_in_kb
7389 : 0;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007390 PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00007391 normalized_time, normalized_bytes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007392 PrintF("---------------------------------------------------------------\n");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007393 PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007394 "Total",
7395 static_cast<double>(total_) / 1000,
7396 static_cast<double>(total_) / full_code_gen_);
7397}
7398
7399
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007400void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007401 if (name == HPhase::kFullCodeGen) {
7402 full_code_gen_ += ticks;
7403 } else if (name == HPhase::kTotal) {
7404 total_ += ticks;
7405 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007406 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007407 for (int i = 0; i < names_.length(); ++i) {
7408 if (names_[i] == name) {
7409 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007410 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007411 return;
7412 }
7413 }
7414 names_.Add(name);
7415 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007416 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007417 }
7418}
7419
7420
7421const char* const HPhase::kFullCodeGen = "Full code generator";
7422const char* const HPhase::kTotal = "Total";
7423
7424
7425void HPhase::Begin(const char* name,
7426 HGraph* graph,
7427 LChunk* chunk,
7428 LAllocator* allocator) {
7429 name_ = name;
7430 graph_ = graph;
7431 chunk_ = chunk;
7432 allocator_ = allocator;
7433 if (allocator != NULL && chunk_ == NULL) {
7434 chunk_ = allocator->chunk();
7435 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007436 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007437 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007438}
7439
7440
7441void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00007442 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007443 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007444 unsigned size = Zone::allocation_size_ - start_allocation_size_;
7445 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007446 }
7447
7448 if (FLAG_trace_hydrogen) {
7449 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
7450 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
7451 if (allocator_ != NULL) {
7452 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
7453 }
7454 }
7455
7456#ifdef DEBUG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007457 if (graph_ != NULL) graph_->Verify(false); // No full verify.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007458 if (allocator_ != NULL) allocator_->Verify();
7459#endif
7460}
7461
7462} } // namespace v8::internal