blob: ca0aebbbb9bf936b3f7969b05ff823762648f311 [file] [log] [blame]
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00001// Copyright 2011 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
ager@chromium.org04921a82011-06-27 13:21:41 +0000167void HBasicBlock::Goto(HBasicBlock* block) {
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);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000170 last_environment_ = last_environment()->outer();
171 }
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
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000178void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) {
179 ASSERT(target->IsInlineReturnTarget());
180 ASSERT(return_value != NULL);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000181 AddInstruction(new(zone()) HLeaveInlined);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000182 last_environment_ = last_environment()->outer();
183 last_environment()->Push(return_value);
184 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000185 HGoto* instr = new(zone()) HGoto(target);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000186 Finish(instr);
187}
188
189
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000190void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
191 ASSERT(!HasEnvironment());
192 ASSERT(first() == NULL);
193 UpdateEnvironment(env);
194}
195
196
ager@chromium.org04921a82011-06-27 13:21:41 +0000197void HBasicBlock::SetJoinId(int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000198 int length = predecessors_.length();
199 ASSERT(length > 0);
200 for (int i = 0; i < length; i++) {
201 HBasicBlock* predecessor = predecessors_[i];
202 ASSERT(predecessor->end()->IsGoto());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000203 HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000204 // We only need to verify the ID once.
205 ASSERT(i != 0 ||
206 predecessor->last_environment()->closure()->shared()
ager@chromium.org04921a82011-06-27 13:21:41 +0000207 ->VerifyBailoutId(ast_id));
208 simulate->set_ast_id(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000209 }
210}
211
212
213bool HBasicBlock::Dominates(HBasicBlock* other) const {
214 HBasicBlock* current = other->dominator();
215 while (current != NULL) {
216 if (current == this) return true;
217 current = current->dominator();
218 }
219 return false;
220}
221
222
223void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
224 ASSERT(IsLoopHeader());
225
226 SetJoinId(stmt->EntryId());
227 if (predecessors()->length() == 1) {
228 // This is a degenerated loop.
229 DetachLoopInformation();
230 return;
231 }
232
233 // Only the first entry into the loop is from outside the loop. All other
234 // entries must be back edges.
235 for (int i = 1; i < predecessors()->length(); ++i) {
236 loop_information()->RegisterBackEdge(predecessors()->at(i));
237 }
238}
239
240
241void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
danno@chromium.org160a7b02011-04-18 15:51:38 +0000242 if (HasPredecessor()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000243 // Only loop header blocks can have a predecessor added after
244 // instructions have been added to the block (they have phis for all
245 // values in the environment, these phis may be eliminated later).
246 ASSERT(IsLoopHeader() || first_ == NULL);
247 HEnvironment* incoming_env = pred->last_environment();
248 if (IsLoopHeader()) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000249 ASSERT(phis()->length() == incoming_env->length());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000250 for (int i = 0; i < phis_.length(); ++i) {
251 phis_[i]->AddInput(incoming_env->values()->at(i));
252 }
253 } else {
254 last_environment()->AddIncomingEdge(this, pred->last_environment());
255 }
256 } else if (!HasEnvironment() && !IsFinished()) {
257 ASSERT(!IsLoopHeader());
258 SetInitialEnvironment(pred->last_environment()->Copy());
259 }
260
261 predecessors_.Add(pred);
262}
263
264
265void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
266 ASSERT(!dominated_blocks_.Contains(block));
267 // Keep the list of dominated blocks sorted such that if there is two
268 // succeeding block in this list, the predecessor is before the successor.
269 int index = 0;
270 while (index < dominated_blocks_.length() &&
271 dominated_blocks_[index]->block_id() < block->block_id()) {
272 ++index;
273 }
274 dominated_blocks_.InsertAt(index, block);
275}
276
277
278void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
279 if (dominator_ == NULL) {
280 dominator_ = other;
281 other->AddDominatedBlock(this);
282 } else if (other->dominator() != NULL) {
283 HBasicBlock* first = dominator_;
284 HBasicBlock* second = other;
285
286 while (first != second) {
287 if (first->block_id() > second->block_id()) {
288 first = first->dominator();
289 } else {
290 second = second->dominator();
291 }
292 ASSERT(first != NULL && second != NULL);
293 }
294
295 if (dominator_ != first) {
296 ASSERT(dominator_->dominated_blocks_.Contains(this));
297 dominator_->dominated_blocks_.RemoveElement(this);
298 dominator_ = first;
299 first->AddDominatedBlock(this);
300 }
301 }
302}
303
304
305int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
306 for (int i = 0; i < predecessors_.length(); ++i) {
307 if (predecessors_[i] == predecessor) return i;
308 }
309 UNREACHABLE();
310 return -1;
311}
312
313
314#ifdef DEBUG
315void HBasicBlock::Verify() {
316 // Check that every block is finished.
317 ASSERT(IsFinished());
318 ASSERT(block_id() >= 0);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000319
320 // Check that the incoming edges are in edge split form.
321 if (predecessors_.length() > 1) {
322 for (int i = 0; i < predecessors_.length(); ++i) {
323 ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
324 }
325 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000326}
327#endif
328
329
330void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
331 this->back_edges_.Add(block);
332 AddBlock(block);
333}
334
335
336HBasicBlock* HLoopInformation::GetLastBackEdge() const {
337 int max_id = -1;
338 HBasicBlock* result = NULL;
339 for (int i = 0; i < back_edges_.length(); ++i) {
340 HBasicBlock* cur = back_edges_[i];
341 if (cur->block_id() > max_id) {
342 max_id = cur->block_id();
343 result = cur;
344 }
345 }
346 return result;
347}
348
349
350void HLoopInformation::AddBlock(HBasicBlock* block) {
351 if (block == loop_header()) return;
352 if (block->parent_loop_header() == loop_header()) return;
353 if (block->parent_loop_header() != NULL) {
354 AddBlock(block->parent_loop_header());
355 } else {
356 block->set_parent_loop_header(loop_header());
357 blocks_.Add(block);
358 for (int i = 0; i < block->predecessors()->length(); ++i) {
359 AddBlock(block->predecessors()->at(i));
360 }
361 }
362}
363
364
365#ifdef DEBUG
366
367// Checks reachability of the blocks in this graph and stores a bit in
368// the BitVector "reachable()" for every block that can be reached
369// from the start block of the graph. If "dont_visit" is non-null, the given
370// block is treated as if it would not be part of the graph. "visited_count()"
371// returns the number of reachable blocks.
372class ReachabilityAnalyzer BASE_EMBEDDED {
373 public:
374 ReachabilityAnalyzer(HBasicBlock* entry_block,
375 int block_count,
376 HBasicBlock* dont_visit)
377 : visited_count_(0),
378 stack_(16),
379 reachable_(block_count),
380 dont_visit_(dont_visit) {
381 PushBlock(entry_block);
382 Analyze();
383 }
384
385 int visited_count() const { return visited_count_; }
386 const BitVector* reachable() const { return &reachable_; }
387
388 private:
389 void PushBlock(HBasicBlock* block) {
390 if (block != NULL && block != dont_visit_ &&
391 !reachable_.Contains(block->block_id())) {
392 reachable_.Add(block->block_id());
393 stack_.Add(block);
394 visited_count_++;
395 }
396 }
397
398 void Analyze() {
399 while (!stack_.is_empty()) {
400 HControlInstruction* end = stack_.RemoveLast()->end();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000401 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
402 PushBlock(it.Current());
403 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000404 }
405 }
406
407 int visited_count_;
408 ZoneList<HBasicBlock*> stack_;
409 BitVector reachable_;
410 HBasicBlock* dont_visit_;
411};
412
413
414void HGraph::Verify() const {
415 for (int i = 0; i < blocks_.length(); i++) {
416 HBasicBlock* block = blocks_.at(i);
417
418 block->Verify();
419
420 // Check that every block contains at least one node and that only the last
421 // node is a control instruction.
422 HInstruction* current = block->first();
423 ASSERT(current != NULL && current->IsBlockEntry());
424 while (current != NULL) {
425 ASSERT((current->next() == NULL) == current->IsControlInstruction());
426 ASSERT(current->block() == block);
427 current->Verify();
428 current = current->next();
429 }
430
431 // Check that successors are correctly set.
432 HBasicBlock* first = block->end()->FirstSuccessor();
433 HBasicBlock* second = block->end()->SecondSuccessor();
434 ASSERT(second == NULL || first != NULL);
435
436 // Check that the predecessor array is correct.
437 if (first != NULL) {
438 ASSERT(first->predecessors()->Contains(block));
439 if (second != NULL) {
440 ASSERT(second->predecessors()->Contains(block));
441 }
442 }
443
444 // Check that phis have correct arguments.
445 for (int j = 0; j < block->phis()->length(); j++) {
446 HPhi* phi = block->phis()->at(j);
447 phi->Verify();
448 }
449
450 // Check that all join blocks have predecessors that end with an
451 // unconditional goto and agree on their environment node id.
452 if (block->predecessors()->length() >= 2) {
453 int id = block->predecessors()->first()->last_environment()->ast_id();
454 for (int k = 0; k < block->predecessors()->length(); k++) {
455 HBasicBlock* predecessor = block->predecessors()->at(k);
456 ASSERT(predecessor->end()->IsGoto());
457 ASSERT(predecessor->last_environment()->ast_id() == id);
458 }
459 }
460 }
461
462 // Check special property of first block to have no predecessors.
463 ASSERT(blocks_.at(0)->predecessors()->is_empty());
464
465 // Check that the graph is fully connected.
466 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL);
467 ASSERT(analyzer.visited_count() == blocks_.length());
468
469 // Check that entry block dominator is NULL.
470 ASSERT(entry_block_->dominator() == NULL);
471
472 // Check dominators.
473 for (int i = 0; i < blocks_.length(); ++i) {
474 HBasicBlock* block = blocks_.at(i);
475 if (block->dominator() == NULL) {
476 // Only start block may have no dominator assigned to.
477 ASSERT(i == 0);
478 } else {
479 // Assert that block is unreachable if dominator must not be visited.
480 ReachabilityAnalyzer dominator_analyzer(entry_block_,
481 blocks_.length(),
482 block->dominator());
483 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id()));
484 }
485 }
486}
487
488#endif
489
490
491HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
492 Object* value) {
493 if (!pointer->is_set()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000494 HConstant* constant = new(zone()) HConstant(Handle<Object>(value),
495 Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000496 constant->InsertAfter(GetConstantUndefined());
497 pointer->set(constant);
498 }
499 return pointer->get();
500}
501
502
503HConstant* HGraph::GetConstant1() {
504 return GetConstant(&constant_1_, Smi::FromInt(1));
505}
506
507
508HConstant* HGraph::GetConstantMinus1() {
509 return GetConstant(&constant_minus1_, Smi::FromInt(-1));
510}
511
512
513HConstant* HGraph::GetConstantTrue() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000514 return GetConstant(&constant_true_, isolate()->heap()->true_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000515}
516
517
518HConstant* HGraph::GetConstantFalse() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000519 return GetConstant(&constant_false_, isolate()->heap()->false_value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000520}
521
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000522
523HConstant* HGraph::GetConstantHole() {
524 return GetConstant(&constant_hole_, isolate()->heap()->the_hole_value());
525}
526
527
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000528HGraphBuilder::HGraphBuilder(CompilationInfo* info,
529 TypeFeedbackOracle* oracle)
530 : function_state_(NULL),
531 initial_function_state_(this, info, oracle),
532 ast_context_(NULL),
533 break_scope_(NULL),
534 graph_(NULL),
535 current_block_(NULL),
536 inlined_count_(0),
537 zone_(info->isolate()->zone()),
538 inline_bailout_(false) {
539 // This is not initialized in the initializer list because the
540 // constructor for the initial state relies on function_state_ == NULL
541 // to know it's the initial state.
542 function_state_= &initial_function_state_;
543}
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000544
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000545HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
546 HBasicBlock* second,
547 int join_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000548 if (first == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000549 return second;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000550 } else if (second == NULL) {
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000551 return first;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000552 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000553 HBasicBlock* join_block = graph_->CreateBasicBlock();
554 first->Goto(join_block);
555 second->Goto(join_block);
556 join_block->SetJoinId(join_id);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000557 return join_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000558 }
559}
560
561
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000562HBasicBlock* HGraphBuilder::JoinContinue(IterationStatement* statement,
563 HBasicBlock* exit_block,
564 HBasicBlock* continue_block) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000565 if (continue_block != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000566 if (exit_block != NULL) exit_block->Goto(continue_block);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000567 continue_block->SetJoinId(statement->ContinueId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000568 return continue_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000569 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000570 return exit_block;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000571}
572
573
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000574HBasicBlock* HGraphBuilder::CreateLoop(IterationStatement* statement,
575 HBasicBlock* loop_entry,
576 HBasicBlock* body_exit,
577 HBasicBlock* loop_successor,
578 HBasicBlock* break_block) {
ager@chromium.org04921a82011-06-27 13:21:41 +0000579 if (body_exit != NULL) body_exit->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000580 loop_entry->PostProcessLoopHeader(statement);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000581 if (break_block != NULL) {
582 if (loop_successor != NULL) loop_successor->Goto(break_block);
583 break_block->SetJoinId(statement->ExitId());
584 return break_block;
585 }
586 return loop_successor;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000587}
588
589
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +0000590void HBasicBlock::FinishExit(HControlInstruction* instruction) {
591 Finish(instruction);
592 ClearEnvironment();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000593}
594
595
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000596HGraph::HGraph(CompilationInfo* info)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000597 : isolate_(info->isolate()),
598 next_block_id_(0),
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000599 entry_block_(NULL),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000600 blocks_(8),
601 values_(16),
602 phi_list_(NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000603 start_environment_ =
604 new(zone()) HEnvironment(NULL, info->scope(), info->closure());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000605 start_environment_->set_ast_id(AstNode::kFunctionEntryId);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000606 entry_block_ = CreateBasicBlock();
607 entry_block_->SetInitialEnvironment(start_environment_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000608}
609
610
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000611Handle<Code> HGraph::Compile(CompilationInfo* info) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000612 int values = GetMaximumValueID();
613 if (values > LAllocator::max_initial_value_ids()) {
614 if (FLAG_trace_bailout) PrintF("Function is too big\n");
615 return Handle<Code>::null();
616 }
617
618 LAllocator allocator(values, this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000619 LChunkBuilder builder(info, this, &allocator);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000620 LChunk* chunk = builder.Build();
621 if (chunk == NULL) return Handle<Code>::null();
622
623 if (!FLAG_alloc_lithium) return Handle<Code>::null();
624
625 allocator.Allocate(chunk);
626
627 if (!FLAG_use_lithium) return Handle<Code>::null();
628
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000629 MacroAssembler assembler(info->isolate(), NULL, 0);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000630 LCodeGen generator(chunk, &assembler, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000631
632 if (FLAG_eliminate_empty_blocks) {
633 chunk->MarkEmptyBlocks();
634 }
635
636 if (generator.GenerateCode()) {
637 if (FLAG_trace_codegen) {
638 PrintF("Crankshaft Compiler - ");
639 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000640 CodeGenerator::MakeCodePrologue(info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000641 Code::Flags flags =
642 Code::ComputeFlags(Code::OPTIMIZED_FUNCTION, NOT_IN_LOOP);
643 Handle<Code> code =
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000644 CodeGenerator::MakeCodeEpilogue(&assembler, flags, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000645 generator.FinishCode(code);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000646 CodeGenerator::PrintCode(code, info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000647 return code;
648 }
649 return Handle<Code>::null();
650}
651
652
653HBasicBlock* HGraph::CreateBasicBlock() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000654 HBasicBlock* result = new(zone()) HBasicBlock(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000655 blocks_.Add(result);
656 return result;
657}
658
659
660void HGraph::Canonicalize() {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000661 if (!FLAG_use_canonicalizing) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000662 HPhase phase("Canonicalize", this);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000663 for (int i = 0; i < blocks()->length(); ++i) {
664 HInstruction* instr = blocks()->at(i)->first();
665 while (instr != NULL) {
666 HValue* value = instr->Canonicalize();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000667 if (value != instr) instr->DeleteAndReplaceWith(value);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000668 instr = instr->next();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000669 }
670 }
671}
672
673
674void HGraph::OrderBlocks() {
675 HPhase phase("Block ordering");
676 BitVector visited(blocks_.length());
677
678 ZoneList<HBasicBlock*> reverse_result(8);
679 HBasicBlock* start = blocks_[0];
680 Postorder(start, &visited, &reverse_result, NULL);
681
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000682 blocks_.Rewind(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000683 int index = 0;
684 for (int i = reverse_result.length() - 1; i >= 0; --i) {
685 HBasicBlock* b = reverse_result[i];
686 blocks_.Add(b);
687 b->set_block_id(index++);
688 }
689}
690
691
692void HGraph::PostorderLoopBlocks(HLoopInformation* loop,
693 BitVector* visited,
694 ZoneList<HBasicBlock*>* order,
695 HBasicBlock* loop_header) {
696 for (int i = 0; i < loop->blocks()->length(); ++i) {
697 HBasicBlock* b = loop->blocks()->at(i);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000698 for (HSuccessorIterator it(b->end()); !it.Done(); it.Advance()) {
699 Postorder(it.Current(), visited, order, loop_header);
700 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000701 if (b->IsLoopHeader() && b != loop->loop_header()) {
702 PostorderLoopBlocks(b->loop_information(), visited, order, loop_header);
703 }
704 }
705}
706
707
708void HGraph::Postorder(HBasicBlock* block,
709 BitVector* visited,
710 ZoneList<HBasicBlock*>* order,
711 HBasicBlock* loop_header) {
712 if (block == NULL || visited->Contains(block->block_id())) return;
713 if (block->parent_loop_header() != loop_header) return;
714 visited->Add(block->block_id());
715 if (block->IsLoopHeader()) {
716 PostorderLoopBlocks(block->loop_information(), visited, order, loop_header);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000717 for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
718 Postorder(it.Current(), visited, order, block);
719 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000720 } else {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000721 for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
722 Postorder(it.Current(), visited, order, loop_header);
723 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000724 }
725 ASSERT(block->end()->FirstSuccessor() == NULL ||
726 order->Contains(block->end()->FirstSuccessor()) ||
727 block->end()->FirstSuccessor()->IsLoopHeader());
728 ASSERT(block->end()->SecondSuccessor() == NULL ||
729 order->Contains(block->end()->SecondSuccessor()) ||
730 block->end()->SecondSuccessor()->IsLoopHeader());
731 order->Add(block);
732}
733
734
735void HGraph::AssignDominators() {
736 HPhase phase("Assign dominators", this);
737 for (int i = 0; i < blocks_.length(); ++i) {
738 if (blocks_[i]->IsLoopHeader()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000739 // Only the first predecessor of a loop header is from outside the loop.
740 // All others are back edges, and thus cannot dominate the loop header.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000741 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
742 } else {
743 for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) {
744 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
745 }
746 }
747 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000748}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000749
ricow@chromium.org2c99e282011-07-28 09:15:17 +0000750// Mark all blocks that are dominated by an unconditional soft deoptimize to
751// prevent code motion across those blocks.
752void HGraph::PropagateDeoptimizingMark() {
753 HPhase phase("Propagate deoptimizing mark", this);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000754 MarkAsDeoptimizingRecursively(entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000755}
756
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000757void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
758 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
759 HBasicBlock* dominated = block->dominated_blocks()->at(i);
760 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
761 MarkAsDeoptimizingRecursively(dominated);
762 }
763}
764
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000765void HGraph::EliminateRedundantPhis() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000766 HPhase phase("Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000767
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000768 // Worklist of phis that can potentially be eliminated. Initialized with
769 // all phi nodes. When elimination of a phi node modifies another phi node
770 // the modified phi node is added to the worklist.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000771 ZoneList<HPhi*> worklist(blocks_.length());
772 for (int i = 0; i < blocks_.length(); ++i) {
773 worklist.AddAll(*blocks_[i]->phis());
774 }
775
776 while (!worklist.is_empty()) {
777 HPhi* phi = worklist.RemoveLast();
778 HBasicBlock* block = phi->block();
779
780 // Skip phi node if it was already replaced.
781 if (block == NULL) continue;
782
783 // Get replacement value if phi is redundant.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000784 HValue* replacement = phi->GetRedundantReplacement();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000785
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000786 if (replacement != NULL) {
787 // Iterate through the uses and replace them all.
788 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
789 HValue* value = it.value();
790 value->SetOperandAt(it.index(), replacement);
791 if (value->IsPhi()) worklist.Add(HPhi::cast(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000792 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000793 block->RemovePhi(phi);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000794 }
795 }
796}
797
798
799void HGraph::EliminateUnreachablePhis() {
800 HPhase phase("Unreachable phi elimination", this);
801
802 // Initialize worklist.
803 ZoneList<HPhi*> phi_list(blocks_.length());
804 ZoneList<HPhi*> worklist(blocks_.length());
805 for (int i = 0; i < blocks_.length(); ++i) {
806 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
807 HPhi* phi = blocks_[i]->phis()->at(j);
808 phi_list.Add(phi);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +0000809 // We can't eliminate phis in the receiver position in the environment
810 // because in case of throwing an error we need this value to
811 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000812 if (phi->HasRealUses() || phi->IsReceiver()) {
813 phi->set_is_live(true);
814 worklist.Add(phi);
815 }
816 }
817 }
818
819 // Iteratively mark live phis.
820 while (!worklist.is_empty()) {
821 HPhi* phi = worklist.RemoveLast();
822 for (int i = 0; i < phi->OperandCount(); i++) {
823 HValue* operand = phi->OperandAt(i);
824 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
825 HPhi::cast(operand)->set_is_live(true);
826 worklist.Add(HPhi::cast(operand));
827 }
828 }
829 }
830
831 // Remove unreachable phis.
832 for (int i = 0; i < phi_list.length(); i++) {
833 HPhi* phi = phi_list[i];
834 if (!phi->is_live()) {
835 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000836 block->RemovePhi(phi);
837 block->RecordDeletedPhi(phi->merged_index());
838 }
839 }
840}
841
842
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +0000843bool HGraph::CheckPhis() {
844 int block_count = blocks_.length();
845 for (int i = 0; i < block_count; ++i) {
846 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
847 HPhi* phi = blocks_[i]->phis()->at(j);
848 // We don't support phi uses of arguments for now.
849 if (phi->CheckFlag(HValue::kIsArguments)) return false;
850 }
851 }
852 return true;
853}
854
855
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000856bool HGraph::CollectPhis() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000857 int block_count = blocks_.length();
858 phi_list_ = new ZoneList<HPhi*>(block_count);
859 for (int i = 0; i < block_count; ++i) {
860 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
861 HPhi* phi = blocks_[i]->phis()->at(j);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000862 phi_list_->Add(phi);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000863 // Check for the hole value (from an uninitialized const).
864 for (int k = 0; k < phi->OperandCount(); k++) {
865 if (phi->OperandAt(k) == GetConstantHole()) return false;
866 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000867 }
868 }
869 return true;
870}
871
872
873void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
874 BitVector in_worklist(GetMaximumValueID());
875 for (int i = 0; i < worklist->length(); ++i) {
876 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
877 in_worklist.Add(worklist->at(i)->id());
878 }
879
880 while (!worklist->is_empty()) {
881 HValue* current = worklist->RemoveLast();
882 in_worklist.Remove(current->id());
883 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000884 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
885 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000886 if (!in_worklist.Contains(use->id())) {
887 in_worklist.Add(use->id());
888 worklist->Add(use);
889 }
890 }
891 }
892 }
893}
894
895
896class HRangeAnalysis BASE_EMBEDDED {
897 public:
898 explicit HRangeAnalysis(HGraph* graph) : graph_(graph), changed_ranges_(16) {}
899
900 void Analyze();
901
902 private:
903 void TraceRange(const char* msg, ...);
904 void Analyze(HBasicBlock* block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000905 void InferControlFlowRange(HCompareIDAndBranch* test, HBasicBlock* dest);
906 void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000907 void InferRange(HValue* value);
908 void RollBackTo(int index);
909 void AddRange(HValue* value, Range* range);
910
911 HGraph* graph_;
912 ZoneList<HValue*> changed_ranges_;
913};
914
915
916void HRangeAnalysis::TraceRange(const char* msg, ...) {
917 if (FLAG_trace_range) {
918 va_list arguments;
919 va_start(arguments, msg);
920 OS::VPrint(msg, arguments);
921 va_end(arguments);
922 }
923}
924
925
926void HRangeAnalysis::Analyze() {
927 HPhase phase("Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000928 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000929}
930
931
932void HRangeAnalysis::Analyze(HBasicBlock* block) {
933 TraceRange("Analyzing block B%d\n", block->block_id());
934
935 int last_changed_range = changed_ranges_.length() - 1;
936
937 // Infer range based on control flow.
938 if (block->predecessors()->length() == 1) {
939 HBasicBlock* pred = block->predecessors()->first();
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000940 if (pred->end()->IsCompareIDAndBranch()) {
941 InferControlFlowRange(HCompareIDAndBranch::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000942 }
943 }
944
945 // Process phi instructions.
946 for (int i = 0; i < block->phis()->length(); ++i) {
947 HPhi* phi = block->phis()->at(i);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000948 InferRange(phi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000949 }
950
951 // Go through all instructions of the current block.
952 HInstruction* instr = block->first();
953 while (instr != block->end()) {
954 InferRange(instr);
955 instr = instr->next();
956 }
957
958 // Continue analysis in all dominated blocks.
959 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
960 Analyze(block->dominated_blocks()->at(i));
961 }
962
963 RollBackTo(last_changed_range);
964}
965
966
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000967void HRangeAnalysis::InferControlFlowRange(HCompareIDAndBranch* test,
968 HBasicBlock* dest) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000969 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000970 if (test->GetInputRepresentation().IsInteger32()) {
971 Token::Value op = test->token();
972 if (test->SecondSuccessor() == dest) {
973 op = Token::NegateCompareOp(op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000974 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000975 Token::Value inverted_op = Token::InvertCompareOp(op);
976 UpdateControlFlowRange(op, test->left(), test->right());
977 UpdateControlFlowRange(inverted_op, test->right(), test->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000978 }
979}
980
981
982// We know that value [op] other. Use this information to update the range on
983// value.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000984void HRangeAnalysis::UpdateControlFlowRange(Token::Value op,
985 HValue* value,
986 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000987 Range temp_range;
988 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000989 Range* new_range = NULL;
990
991 TraceRange("Control flow range infer %d %s %d\n",
992 value->id(),
993 Token::Name(op),
994 other->id());
995
996 if (op == Token::EQ || op == Token::EQ_STRICT) {
997 // The same range has to apply for value.
998 new_range = range->Copy();
999 } else if (op == Token::LT || op == Token::LTE) {
1000 new_range = range->CopyClearLower();
1001 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001002 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001003 }
1004 } else if (op == Token::GT || op == Token::GTE) {
1005 new_range = range->CopyClearUpper();
1006 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001007 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001008 }
1009 }
1010
1011 if (new_range != NULL && !new_range->IsMostGeneric()) {
1012 AddRange(value, new_range);
1013 }
1014}
1015
1016
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001017void HRangeAnalysis::InferRange(HValue* value) {
1018 ASSERT(!value->HasRange());
1019 if (!value->representation().IsNone()) {
1020 value->ComputeInitialRange();
1021 Range* range = value->range();
1022 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1023 value->id(),
1024 value->Mnemonic(),
1025 range->lower(),
1026 range->upper());
1027 }
1028}
1029
1030
1031void HRangeAnalysis::RollBackTo(int index) {
1032 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1033 changed_ranges_[i]->RemoveLastAddedRange();
1034 }
1035 changed_ranges_.Rewind(index + 1);
1036}
1037
1038
1039void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1040 Range* original_range = value->range();
1041 value->AddNewRange(range);
1042 changed_ranges_.Add(value);
1043 Range* new_range = value->range();
1044 TraceRange("Updated range of %d set to [%d,%d]\n",
1045 value->id(),
1046 new_range->lower(),
1047 new_range->upper());
1048 if (original_range != NULL) {
1049 TraceRange("Original range was [%d,%d]\n",
1050 original_range->lower(),
1051 original_range->upper());
1052 }
1053 TraceRange("New information was [%d,%d]\n",
1054 range->lower(),
1055 range->upper());
1056}
1057
1058
1059void TraceGVN(const char* msg, ...) {
1060 if (FLAG_trace_gvn) {
1061 va_list arguments;
1062 va_start(arguments, msg);
1063 OS::VPrint(msg, arguments);
1064 va_end(arguments);
1065 }
1066}
1067
1068
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001069HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001070 : array_size_(other->array_size_),
1071 lists_size_(other->lists_size_),
1072 count_(other->count_),
1073 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001074 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
1075 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001076 free_list_head_(other->free_list_head_) {
1077 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
1078 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
1079}
1080
1081
1082void HValueMap::Kill(int flags) {
1083 int depends_flags = HValue::ConvertChangesToDependsFlags(flags);
1084 if ((present_flags_ & depends_flags) == 0) return;
1085 present_flags_ = 0;
1086 for (int i = 0; i < array_size_; ++i) {
1087 HValue* value = array_[i].value;
1088 if (value != NULL) {
1089 // Clear list of collisions first, so we know if it becomes empty.
1090 int kept = kNil; // List of kept elements.
1091 int next;
1092 for (int current = array_[i].next; current != kNil; current = next) {
1093 next = lists_[current].next;
1094 if ((lists_[current].value->flags() & depends_flags) != 0) {
1095 // Drop it.
1096 count_--;
1097 lists_[current].next = free_list_head_;
1098 free_list_head_ = current;
1099 } else {
1100 // Keep it.
1101 lists_[current].next = kept;
1102 kept = current;
1103 present_flags_ |= lists_[current].value->flags();
1104 }
1105 }
1106 array_[i].next = kept;
1107
1108 // Now possibly drop directly indexed element.
1109 if ((array_[i].value->flags() & depends_flags) != 0) { // Drop it.
1110 count_--;
1111 int head = array_[i].next;
1112 if (head == kNil) {
1113 array_[i].value = NULL;
1114 } else {
1115 array_[i].value = lists_[head].value;
1116 array_[i].next = lists_[head].next;
1117 lists_[head].next = free_list_head_;
1118 free_list_head_ = head;
1119 }
1120 } else {
1121 present_flags_ |= array_[i].value->flags(); // Keep it.
1122 }
1123 }
1124 }
1125}
1126
1127
1128HValue* HValueMap::Lookup(HValue* value) const {
1129 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
1130 uint32_t pos = Bound(hash);
1131 if (array_[pos].value != NULL) {
1132 if (array_[pos].value->Equals(value)) return array_[pos].value;
1133 int next = array_[pos].next;
1134 while (next != kNil) {
1135 if (lists_[next].value->Equals(value)) return lists_[next].value;
1136 next = lists_[next].next;
1137 }
1138 }
1139 return NULL;
1140}
1141
1142
1143void HValueMap::Resize(int new_size) {
1144 ASSERT(new_size > count_);
1145 // Hashing the values into the new array has no more collisions than in the
1146 // old hash map, so we can use the existing lists_ array, if we are careful.
1147
1148 // Make sure we have at least one free element.
1149 if (free_list_head_ == kNil) {
1150 ResizeLists(lists_size_ << 1);
1151 }
1152
1153 HValueMapListElement* new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001154 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001155 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
1156
1157 HValueMapListElement* old_array = array_;
1158 int old_size = array_size_;
1159
1160 int old_count = count_;
1161 count_ = 0;
1162 // Do not modify present_flags_. It is currently correct.
1163 array_size_ = new_size;
1164 array_ = new_array;
1165
1166 if (old_array != NULL) {
1167 // Iterate over all the elements in lists, rehashing them.
1168 for (int i = 0; i < old_size; ++i) {
1169 if (old_array[i].value != NULL) {
1170 int current = old_array[i].next;
1171 while (current != kNil) {
1172 Insert(lists_[current].value);
1173 int next = lists_[current].next;
1174 lists_[current].next = free_list_head_;
1175 free_list_head_ = current;
1176 current = next;
1177 }
1178 // Rehash the directly stored value.
1179 Insert(old_array[i].value);
1180 }
1181 }
1182 }
1183 USE(old_count);
1184 ASSERT(count_ == old_count);
1185}
1186
1187
1188void HValueMap::ResizeLists(int new_size) {
1189 ASSERT(new_size > lists_size_);
1190
1191 HValueMapListElement* new_lists =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001192 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001193 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
1194
1195 HValueMapListElement* old_lists = lists_;
1196 int old_size = lists_size_;
1197
1198 lists_size_ = new_size;
1199 lists_ = new_lists;
1200
1201 if (old_lists != NULL) {
1202 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
1203 }
1204 for (int i = old_size; i < lists_size_; ++i) {
1205 lists_[i].next = free_list_head_;
1206 free_list_head_ = i;
1207 }
1208}
1209
1210
1211void HValueMap::Insert(HValue* value) {
1212 ASSERT(value != NULL);
1213 // Resizing when half of the hashtable is filled up.
1214 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1);
1215 ASSERT(count_ < array_size_);
1216 count_++;
1217 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
1218 if (array_[pos].value == NULL) {
1219 array_[pos].value = value;
1220 array_[pos].next = kNil;
1221 } else {
1222 if (free_list_head_ == kNil) {
1223 ResizeLists(lists_size_ << 1);
1224 }
1225 int new_element_pos = free_list_head_;
1226 ASSERT(new_element_pos != kNil);
1227 free_list_head_ = lists_[free_list_head_].next;
1228 lists_[new_element_pos].value = value;
1229 lists_[new_element_pos].next = array_[pos].next;
1230 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
1231 array_[pos].next = new_element_pos;
1232 }
1233}
1234
1235
1236class HStackCheckEliminator BASE_EMBEDDED {
1237 public:
1238 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
1239
1240 void Process();
1241
1242 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001243 HGraph* graph_;
1244};
1245
1246
1247void HStackCheckEliminator::Process() {
1248 // For each loop block walk the dominator tree from the backwards branch to
1249 // the loop header. If a call instruction is encountered the backwards branch
1250 // is dominated by a call and the stack check in the backwards branch can be
1251 // removed.
1252 for (int i = 0; i < graph_->blocks()->length(); i++) {
1253 HBasicBlock* block = graph_->blocks()->at(i);
1254 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001255 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
1256 HBasicBlock* dominator = back_edge;
whesse@chromium.org7b260152011-06-20 15:33:18 +00001257 while (true) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001258 HInstruction* instr = dominator->first();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001259 while (instr != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001260 if (instr->IsCall()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00001261 block->loop_information()->stack_check()->Eliminate();
whesse@chromium.org7b260152011-06-20 15:33:18 +00001262 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001263 }
1264 instr = instr->next();
1265 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00001266
1267 // Done when the loop header is processed.
1268 if (dominator == block) break;
1269
1270 // Move up the dominator tree.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001271 dominator = dominator->dominator();
1272 }
1273 }
1274 }
1275}
1276
1277
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001278// Simple sparse set with O(1) add, contains, and clear.
1279class SparseSet {
1280 public:
1281 SparseSet(Zone* zone, int capacity)
1282 : capacity_(capacity),
1283 length_(0),
1284 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001285 sparse_(zone->NewArray<int>(capacity)) {
1286#ifndef NVALGRIND
1287 // Initialize the sparse array to make valgrind happy.
1288 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
1289#endif
1290 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001291
1292 bool Contains(int n) const {
1293 ASSERT(0 <= n && n < capacity_);
1294 int d = sparse_[n];
1295 return 0 <= d && d < length_ && dense_[d] == n;
1296 }
1297
1298 bool Add(int n) {
1299 if (Contains(n)) return false;
1300 dense_[length_] = n;
1301 sparse_[n] = length_;
1302 ++length_;
1303 return true;
1304 }
1305
1306 void Clear() { length_ = 0; }
1307
1308 private:
1309 int capacity_;
1310 int length_;
1311 int* dense_;
1312 int* sparse_;
1313
1314 DISALLOW_COPY_AND_ASSIGN(SparseSet);
1315};
1316
1317
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001318class HGlobalValueNumberer BASE_EMBEDDED {
1319 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001320 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001321 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001322 info_(info),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001323 block_side_effects_(graph->blocks()->length()),
1324 loop_side_effects_(graph->blocks()->length()),
1325 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001326 ASSERT(info->isolate()->heap()->allow_allocation(false));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001327 block_side_effects_.AddBlock(0, graph_->blocks()->length());
1328 loop_side_effects_.AddBlock(0, graph_->blocks()->length());
1329 }
1330 ~HGlobalValueNumberer() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001331 ASSERT(!info_->isolate()->heap()->allow_allocation(true));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001332 }
1333
1334 void Analyze();
1335
1336 private:
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001337 int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator,
1338 HBasicBlock* dominated);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001339 void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
1340 void ComputeBlockSideEffects();
1341 void LoopInvariantCodeMotion();
1342 void ProcessLoopBlock(HBasicBlock* block,
1343 HBasicBlock* before_loop,
1344 int loop_kills);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001345 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001346 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
1347
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001348 HGraph* graph() { return graph_; }
1349 CompilationInfo* info() { return info_; }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001350 Zone* zone() { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001351
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001352 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001353 CompilationInfo* info_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001354
1355 // A map of block IDs to their side effects.
1356 ZoneList<int> block_side_effects_;
1357
1358 // A map of loop header block IDs to their loop's side effects.
1359 ZoneList<int> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001360
1361 // Used when collecting side effects on paths from dominator to
1362 // dominated.
1363 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001364};
1365
1366
1367void HGlobalValueNumberer::Analyze() {
1368 ComputeBlockSideEffects();
1369 if (FLAG_loop_invariant_code_motion) {
1370 LoopInvariantCodeMotion();
1371 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001372 HValueMap* map = new(zone()) HValueMap();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001373 AnalyzeBlock(graph_->entry_block(), map);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001374}
1375
1376
1377void HGlobalValueNumberer::ComputeBlockSideEffects() {
1378 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1379 // Compute side effects for the block.
1380 HBasicBlock* block = graph_->blocks()->at(i);
1381 HInstruction* instr = block->first();
1382 int id = block->block_id();
1383 int side_effects = 0;
1384 while (instr != NULL) {
1385 side_effects |= (instr->flags() & HValue::ChangesFlagsMask());
1386 instr = instr->next();
1387 }
1388 block_side_effects_[id] |= side_effects;
1389
1390 // Loop headers are part of their loop.
1391 if (block->IsLoopHeader()) {
1392 loop_side_effects_[id] |= side_effects;
1393 }
1394
1395 // Propagate loop side effects upwards.
1396 if (block->HasParentLoopHeader()) {
1397 int header_id = block->parent_loop_header()->block_id();
1398 loop_side_effects_[header_id] |=
1399 block->IsLoopHeader() ? loop_side_effects_[id] : side_effects;
1400 }
1401 }
1402}
1403
1404
1405void HGlobalValueNumberer::LoopInvariantCodeMotion() {
1406 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1407 HBasicBlock* block = graph_->blocks()->at(i);
1408 if (block->IsLoopHeader()) {
1409 int side_effects = loop_side_effects_[block->block_id()];
1410 TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n",
1411 block->block_id(),
1412 side_effects);
1413
1414 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
1415 for (int j = block->block_id(); j <= last->block_id(); ++j) {
1416 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
1417 }
1418 }
1419 }
1420}
1421
1422
1423void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
1424 HBasicBlock* loop_header,
1425 int loop_kills) {
1426 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
1427 int depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
1428 TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
1429 block->block_id(),
1430 depends_flags);
1431 HInstruction* instr = block->first();
1432 while (instr != NULL) {
1433 HInstruction* next = instr->next();
1434 if (instr->CheckFlag(HValue::kUseGVN) &&
1435 (instr->flags() & depends_flags) == 0) {
1436 TraceGVN("Checking instruction %d (%s)\n",
1437 instr->id(),
1438 instr->Mnemonic());
1439 bool inputs_loop_invariant = true;
1440 for (int i = 0; i < instr->OperandCount(); ++i) {
1441 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
1442 inputs_loop_invariant = false;
1443 }
1444 }
1445
1446 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
1447 TraceGVN("Found loop invariant instruction %d\n", instr->id());
1448 // Move the instruction out of the loop.
1449 instr->Unlink();
1450 instr->InsertBefore(pre_header->end());
1451 }
1452 }
1453 instr = next;
1454 }
1455}
1456
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00001457
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001458bool HGlobalValueNumberer::AllowCodeMotion() {
1459 return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount;
1460}
1461
1462
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001463bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
1464 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001465 // If we've disabled code motion or we're in a block that unconditionally
1466 // deoptimizes, don't move any instructions.
1467 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001468}
1469
1470
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001471int HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
1472 HBasicBlock* dominator, HBasicBlock* dominated) {
1473 int side_effects = 0;
1474 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
1475 HBasicBlock* block = dominated->predecessors()->at(i);
1476 if (dominator->block_id() < block->block_id() &&
1477 block->block_id() < dominated->block_id() &&
1478 visited_on_paths_.Add(block->block_id())) {
1479 side_effects |= block_side_effects_[block->block_id()];
1480 side_effects |= CollectSideEffectsOnPathsToDominatedBlock(
1481 dominator, block);
1482 }
1483 }
1484 return side_effects;
1485}
1486
1487
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001488void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001489 TraceGVN("Analyzing block B%d%s\n",
1490 block->block_id(),
1491 block->IsLoopHeader() ? " (loop header)" : "");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001492
1493 // If this is a loop header kill everything killed by the loop.
1494 if (block->IsLoopHeader()) {
1495 map->Kill(loop_side_effects_[block->block_id()]);
1496 }
1497
1498 // Go through all instructions of the current block.
1499 HInstruction* instr = block->first();
1500 while (instr != NULL) {
1501 HInstruction* next = instr->next();
1502 int flags = (instr->flags() & HValue::ChangesFlagsMask());
1503 if (flags != 0) {
1504 ASSERT(!instr->CheckFlag(HValue::kUseGVN));
1505 // Clear all instructions in the map that are affected by side effects.
1506 map->Kill(flags);
1507 TraceGVN("Instruction %d kills\n", instr->id());
1508 } else if (instr->CheckFlag(HValue::kUseGVN)) {
1509 HValue* other = map->Lookup(instr);
1510 if (other != NULL) {
1511 ASSERT(instr->Equals(other) && other->Equals(instr));
1512 TraceGVN("Replacing value %d (%s) with value %d (%s)\n",
1513 instr->id(),
1514 instr->Mnemonic(),
1515 other->id(),
1516 other->Mnemonic());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001517 instr->DeleteAndReplaceWith(other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001518 } else {
1519 map->Add(instr);
1520 }
1521 }
1522 instr = next;
1523 }
1524
1525 // Recursively continue analysis for all immediately dominated blocks.
1526 int length = block->dominated_blocks()->length();
1527 for (int i = 0; i < length; ++i) {
1528 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1529 // No need to copy the map for the last child in the dominator tree.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001530 HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001531
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001532 // Kill everything killed on any path between this block and the
1533 // dominated block.
1534 // We don't have to traverse these paths if the value map is
1535 // already empty.
1536 // If the range of block ids (block_id, dominated_id) is empty
1537 // there are no such paths.
1538 if (!successor_map->IsEmpty() &&
1539 block->block_id() + 1 < dominated->block_id()) {
1540 visited_on_paths_.Clear();
1541 successor_map->Kill(CollectSideEffectsOnPathsToDominatedBlock(block,
1542 dominated));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001543 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001544 AnalyzeBlock(dominated, successor_map);
1545 }
1546}
1547
1548
1549class HInferRepresentation BASE_EMBEDDED {
1550 public:
1551 explicit HInferRepresentation(HGraph* graph)
1552 : graph_(graph), worklist_(8), in_worklist_(graph->GetMaximumValueID()) {}
1553
1554 void Analyze();
1555
1556 private:
1557 Representation TryChange(HValue* current);
1558 void AddToWorklist(HValue* current);
1559 void InferBasedOnInputs(HValue* current);
1560 void AddDependantsToWorklist(HValue* current);
1561 void InferBasedOnUses(HValue* current);
1562
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001563 Zone* zone() { return graph_->zone(); }
1564
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001565 HGraph* graph_;
1566 ZoneList<HValue*> worklist_;
1567 BitVector in_worklist_;
1568};
1569
1570
1571void HInferRepresentation::AddToWorklist(HValue* current) {
1572 if (current->representation().IsSpecialization()) return;
1573 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
1574 if (in_worklist_.Contains(current->id())) return;
1575 worklist_.Add(current);
1576 in_worklist_.Add(current->id());
1577}
1578
1579
1580// This method tries to specialize the representation type of the value
1581// given as a parameter. The value is asked to infer its representation type
1582// based on its inputs. If the inferred type is more specialized, then this
1583// becomes the new representation type of the node.
1584void HInferRepresentation::InferBasedOnInputs(HValue* current) {
1585 Representation r = current->representation();
1586 if (r.IsSpecialization()) return;
1587 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
1588 Representation inferred = current->InferredRepresentation();
1589 if (inferred.IsSpecialization()) {
1590 current->ChangeRepresentation(inferred);
1591 AddDependantsToWorklist(current);
1592 }
1593}
1594
1595
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001596void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
1597 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1598 AddToWorklist(it.value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001599 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001600 for (int i = 0; i < value->OperandCount(); ++i) {
1601 AddToWorklist(value->OperandAt(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001602 }
1603}
1604
1605
1606// This method calculates whether specializing the representation of the value
1607// given as the parameter has a benefit in terms of less necessary type
1608// conversions. If there is a benefit, then the representation of the value is
1609// specialized.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001610void HInferRepresentation::InferBasedOnUses(HValue* value) {
1611 Representation r = value->representation();
1612 if (r.IsSpecialization() || value->HasNoUses()) return;
1613 ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
1614 Representation new_rep = TryChange(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001615 if (!new_rep.IsNone()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001616 if (!value->representation().Equals(new_rep)) {
1617 value->ChangeRepresentation(new_rep);
1618 AddDependantsToWorklist(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001619 }
1620 }
1621}
1622
1623
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001624Representation HInferRepresentation::TryChange(HValue* value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001625 // Array of use counts for each representation.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001626 int use_count[Representation::kNumRepresentations] = { 0 };
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001627
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001628 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1629 HValue* use = it.value();
1630 Representation rep = use->RequiredInputRepresentation(it.index());
1631 if (rep.IsNone()) continue;
1632 if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
1633 ++use_count[rep.kind()];
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001634 }
1635 int tagged_count = use_count[Representation::kTagged];
1636 int double_count = use_count[Representation::kDouble];
1637 int int32_count = use_count[Representation::kInteger32];
1638 int non_tagged_count = double_count + int32_count;
1639
1640 // If a non-loop phi has tagged uses, don't convert it to untagged.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001641 if (value->IsPhi() && !value->block()->IsLoopHeader()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001642 if (tagged_count > 0) return Representation::None();
1643 }
1644
1645 if (non_tagged_count >= tagged_count) {
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001646 if (int32_count > 0) {
1647 if (!value->IsPhi() || value->IsConvertibleToInteger()) {
1648 return Representation::Integer32();
1649 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001650 }
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001651 if (double_count > 0) return Representation::Double();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001652 }
1653 return Representation::None();
1654}
1655
1656
1657void HInferRepresentation::Analyze() {
1658 HPhase phase("Infer representations", graph_);
1659
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001660 // (1) Initialize bit vectors and count real uses. Each phi gets a
1661 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001662 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001663 int phi_count = phi_list->length();
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001664 ZoneList<BitVector*> connected_phis(phi_count);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001665 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001666 phi_list->at(i)->InitRealUses(i);
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001667 BitVector* connected_set = new(zone()) BitVector(phi_count);
1668 connected_set->Add(i);
1669 connected_phis.Add(connected_set);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001670 }
1671
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001672 // (2) Do a fixed point iteration to find the set of connected phis. A
1673 // phi is connected to another phi if its value is used either directly or
1674 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001675 bool change = true;
1676 while (change) {
1677 change = false;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001678 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001679 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001680 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1681 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001682 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001683 int id = HPhi::cast(use)->phi_id();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00001684 if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
1685 change = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001686 }
1687 }
1688 }
1689 }
1690
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001691 // (3) Sum up the non-phi use counts of all connected phis. Don't include
1692 // the non-phi uses of the phi itself.
1693 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001694 HPhi* phi = phi_list->at(i);
1695 for (BitVector::Iterator it(connected_phis.at(i));
1696 !it.Done();
1697 it.Advance()) {
1698 int index = it.Current();
1699 if (index != i) {
1700 HPhi* it_use = phi_list->at(it.Current());
1701 phi->AddNonPhiUsesFrom(it_use);
1702 }
1703 }
1704 }
1705
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001706 // (4) Compute phis that definitely can't be converted to integer
1707 // without deoptimization and mark them to avoid unnecessary deoptimization.
1708 change = true;
1709 while (change) {
1710 change = false;
1711 for (int i = 0; i < phi_count; ++i) {
1712 HPhi* phi = phi_list->at(i);
1713 for (int j = 0; j < phi->OperandCount(); ++j) {
1714 if (phi->IsConvertibleToInteger() &&
1715 !phi->OperandAt(j)->IsConvertibleToInteger()) {
1716 phi->set_is_convertible_to_integer(false);
1717 change = true;
1718 break;
1719 }
1720 }
1721 }
1722 }
1723
1724
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001725 for (int i = 0; i < graph_->blocks()->length(); ++i) {
1726 HBasicBlock* block = graph_->blocks()->at(i);
1727 const ZoneList<HPhi*>* phis = block->phis();
1728 for (int j = 0; j < phis->length(); ++j) {
1729 AddToWorklist(phis->at(j));
1730 }
1731
1732 HInstruction* current = block->first();
1733 while (current != NULL) {
1734 AddToWorklist(current);
1735 current = current->next();
1736 }
1737 }
1738
1739 while (!worklist_.is_empty()) {
1740 HValue* current = worklist_.RemoveLast();
1741 in_worklist_.Remove(current->id());
1742 InferBasedOnInputs(current);
1743 InferBasedOnUses(current);
1744 }
1745}
1746
1747
1748void HGraph::InitializeInferredTypes() {
1749 HPhase phase("Inferring types", this);
1750 InitializeInferredTypes(0, this->blocks_.length() - 1);
1751}
1752
1753
1754void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
1755 for (int i = from_inclusive; i <= to_inclusive; ++i) {
1756 HBasicBlock* block = blocks_[i];
1757
1758 const ZoneList<HPhi*>* phis = block->phis();
1759 for (int j = 0; j < phis->length(); j++) {
1760 phis->at(j)->UpdateInferredType();
1761 }
1762
1763 HInstruction* current = block->first();
1764 while (current != NULL) {
1765 current->UpdateInferredType();
1766 current = current->next();
1767 }
1768
1769 if (block->IsLoopHeader()) {
1770 HBasicBlock* last_back_edge =
1771 block->loop_information()->GetLastBackEdge();
1772 InitializeInferredTypes(i + 1, last_back_edge->block_id());
1773 // Skip all blocks already processed by the recursive call.
1774 i = last_back_edge->block_id();
1775 // Update phis of the loop header now after the whole loop body is
1776 // guaranteed to be processed.
1777 ZoneList<HValue*> worklist(block->phis()->length());
1778 for (int j = 0; j < block->phis()->length(); ++j) {
1779 worklist.Add(block->phis()->at(j));
1780 }
1781 InferTypes(&worklist);
1782 }
1783 }
1784}
1785
1786
1787void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
1788 HValue* current = value;
1789 while (current != NULL) {
1790 if (visited->Contains(current->id())) return;
1791
1792 // For phis, we must propagate the check to all of its inputs.
1793 if (current->IsPhi()) {
1794 visited->Add(current->id());
1795 HPhi* phi = HPhi::cast(current);
1796 for (int i = 0; i < phi->OperandCount(); ++i) {
1797 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
1798 }
1799 break;
1800 }
1801
1802 // For multiplication and division, we must propagate to the left and
1803 // the right side.
1804 if (current->IsMul()) {
1805 HMul* mul = HMul::cast(current);
1806 mul->EnsureAndPropagateNotMinusZero(visited);
1807 PropagateMinusZeroChecks(mul->left(), visited);
1808 PropagateMinusZeroChecks(mul->right(), visited);
1809 } else if (current->IsDiv()) {
1810 HDiv* div = HDiv::cast(current);
1811 div->EnsureAndPropagateNotMinusZero(visited);
1812 PropagateMinusZeroChecks(div->left(), visited);
1813 PropagateMinusZeroChecks(div->right(), visited);
1814 }
1815
1816 current = current->EnsureAndPropagateNotMinusZero(visited);
1817 }
1818}
1819
1820
1821void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001822 HValue* use_value,
1823 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001824 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001825 // Insert the representation change right before its use. For phi-uses we
1826 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001827 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001828 if (use_value->IsPhi()) {
1829 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001830 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001831 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001832 }
1833
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001834 // For constants we try to make the representation change at compile
1835 // time. When a representation change is not possible without loss of
1836 // information we treat constants like normal instructions and insert the
1837 // change instructions for them.
1838 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001839 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001840 bool deoptimize_on_undefined =
1841 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001842 if (value->IsConstant()) {
1843 HConstant* constant = HConstant::cast(value);
1844 // Try to create a new copy of the constant with the new representation.
1845 new_value = is_truncating
1846 ? constant->CopyToTruncatedInt32()
1847 : constant->CopyToRepresentation(to);
1848 }
1849
1850 if (new_value == NULL) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001851 new_value = new(zone()) HChange(value, value->representation(), to,
1852 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001853 }
1854
1855 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001856 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001857}
1858
1859
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001860void HGraph::InsertRepresentationChangesForValue(HValue* value) {
1861 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001862 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001863 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001864
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001865 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1866 HValue* use_value = it.value();
1867 int use_index = it.index();
1868 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001869 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001870 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001871 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001872 if (value->HasNoUses()) {
1873 ASSERT(value->IsConstant());
1874 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001875 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00001876
1877 // The only purpose of a HForceRepresentation is to represent the value
1878 // after the (possible) HChange instruction. We make it disappear.
1879 if (value->IsForceRepresentation()) {
1880 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
1881 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001882}
1883
1884
1885void HGraph::InsertRepresentationChanges() {
1886 HPhase phase("Insert representation changes", this);
1887
1888
1889 // Compute truncation flag for phis: Initially assume that all
1890 // int32-phis allow truncation and iteratively remove the ones that
1891 // are used in an operation that does not allow a truncating
1892 // conversion.
1893 // TODO(fschneider): Replace this with a worklist-based iteration.
1894 for (int i = 0; i < phi_list()->length(); i++) {
1895 HPhi* phi = phi_list()->at(i);
1896 if (phi->representation().IsInteger32()) {
1897 phi->SetFlag(HValue::kTruncatingToInt32);
1898 }
1899 }
1900 bool change = true;
1901 while (change) {
1902 change = false;
1903 for (int i = 0; i < phi_list()->length(); i++) {
1904 HPhi* phi = phi_list()->at(i);
1905 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001906 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1907 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001908 if (!use->CheckFlag(HValue::kTruncatingToInt32)) {
1909 phi->ClearFlag(HValue::kTruncatingToInt32);
1910 change = true;
1911 break;
1912 }
1913 }
1914 }
1915 }
1916
1917 for (int i = 0; i < blocks_.length(); ++i) {
1918 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001919 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
1920 for (int j = 0; j < phis->length(); j++) {
1921 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001922 }
1923
1924 // Process normal instructions.
1925 HInstruction* current = blocks_[i]->first();
1926 while (current != NULL) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001927 InsertRepresentationChangesForValue(current);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001928 current = current->next();
1929 }
1930 }
1931}
1932
1933
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001934void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
1935 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
1936 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
1937 for (int i = 0; i < phi->OperandCount(); ++i) {
1938 HValue* input = phi->OperandAt(i);
1939 if (input->IsPhi()) {
1940 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
1941 }
1942 }
1943}
1944
1945
1946void HGraph::MarkDeoptimizeOnUndefined() {
1947 HPhase phase("MarkDeoptimizeOnUndefined", this);
1948 // Compute DeoptimizeOnUndefined flag for phis.
1949 // Any phi that can reach a use with DeoptimizeOnUndefined set must
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001950 // have DeoptimizeOnUndefined set. Currently only HCompareIDAndBranch, with
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001951 // double input representation, has this flag set.
1952 // The flag is used by HChange tagged->double, which must deoptimize
1953 // if one of its uses has this flag set.
1954 for (int i = 0; i < phi_list()->length(); i++) {
1955 HPhi* phi = phi_list()->at(i);
1956 if (phi->representation().IsDouble()) {
1957 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1958 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
1959 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
1960 break;
1961 }
1962 }
1963 }
1964 }
1965}
1966
1967
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001968void HGraph::ComputeMinusZeroChecks() {
1969 BitVector visited(GetMaximumValueID());
1970 for (int i = 0; i < blocks_.length(); ++i) {
1971 for (HInstruction* current = blocks_[i]->first();
1972 current != NULL;
1973 current = current->next()) {
1974 if (current->IsChange()) {
1975 HChange* change = HChange::cast(current);
1976 // Propagate flags for negative zero checks upwards from conversions
1977 // int32-to-tagged and int32-to-double.
1978 Representation from = change->value()->representation();
1979 ASSERT(from.Equals(change->from()));
1980 if (from.IsInteger32()) {
1981 ASSERT(change->to().IsTagged() || change->to().IsDouble());
1982 ASSERT(visited.IsEmpty());
1983 PropagateMinusZeroChecks(change->value(), &visited);
1984 visited.Clear();
1985 }
1986 }
1987 }
1988 }
1989}
1990
1991
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001992// Implementation of utility class to encapsulate the translation state for
1993// a (possibly inlined) function.
1994FunctionState::FunctionState(HGraphBuilder* owner,
1995 CompilationInfo* info,
1996 TypeFeedbackOracle* oracle)
1997 : owner_(owner),
1998 compilation_info_(info),
1999 oracle_(oracle),
2000 call_context_(NULL),
2001 function_return_(NULL),
2002 test_context_(NULL),
2003 outer_(owner->function_state()) {
2004 if (outer_ != NULL) {
2005 // State for an inline function.
2006 if (owner->ast_context()->IsTest()) {
2007 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
2008 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
2009 if_true->MarkAsInlineReturnTarget();
2010 if_false->MarkAsInlineReturnTarget();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002011 Expression* cond = TestContext::cast(owner->ast_context())->condition();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002012 // The AstContext constructor pushed on the context stack. This newed
2013 // instance is the reason that AstContext can't be BASE_EMBEDDED.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002014 test_context_ = new TestContext(owner, cond, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002015 } else {
2016 function_return_ = owner->graph()->CreateBasicBlock();
2017 function_return()->MarkAsInlineReturnTarget();
2018 }
2019 // Set this after possibly allocating a new TestContext above.
2020 call_context_ = owner->ast_context();
2021 }
2022
2023 // Push on the state stack.
2024 owner->set_function_state(this);
2025}
2026
2027
2028FunctionState::~FunctionState() {
2029 delete test_context_;
2030 owner_->set_function_state(outer_);
2031}
2032
2033
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002034// Implementation of utility classes to represent an expression's context in
2035// the AST.
2036AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002037 : owner_(owner),
2038 kind_(kind),
2039 outer_(owner->ast_context()),
2040 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002041 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002042#ifdef DEBUG
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002043 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002044#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002045}
2046
2047
2048AstContext::~AstContext() {
2049 owner_->set_ast_context(outer_); // Pop.
2050}
2051
2052
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002053EffectContext::~EffectContext() {
2054 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002055 owner()->current_block() == NULL ||
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002056 owner()->environment()->length() == original_length_);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002057}
2058
2059
2060ValueContext::~ValueContext() {
2061 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002062 owner()->current_block() == NULL ||
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002063 owner()->environment()->length() == original_length_ + 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002064}
2065
2066
2067void EffectContext::ReturnValue(HValue* value) {
2068 // The value is simply ignored.
2069}
2070
2071
2072void ValueContext::ReturnValue(HValue* value) {
2073 // The value is tracked in the bailout environment, and communicated
2074 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002075 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
2076 owner()->Bailout("bad value context for arguments value");
2077 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002078 owner()->Push(value);
2079}
2080
2081
2082void TestContext::ReturnValue(HValue* value) {
2083 BuildBranch(value);
2084}
2085
2086
2087void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002088 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002089 owner()->AddInstruction(instr);
2090 if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
2091}
2092
2093
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002094void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) {
2095 ASSERT(!instr->HasSideEffects());
2096 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2097 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2098 instr->SetSuccessorAt(0, empty_true);
2099 instr->SetSuccessorAt(1, empty_false);
2100 owner()->current_block()->Finish(instr);
2101 HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
2102 owner()->set_current_block(join);
2103}
2104
2105
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002106void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002107 ASSERT(!instr->IsControlInstruction());
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002108 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002109 return owner()->Bailout("bad value context for arguments object value");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002110 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002111 owner()->AddInstruction(instr);
2112 owner()->Push(instr);
2113 if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
2114}
2115
2116
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002117void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) {
2118 ASSERT(!instr->HasSideEffects());
2119 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
2120 return owner()->Bailout("bad value context for arguments object value");
2121 }
2122 HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
2123 HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
2124 instr->SetSuccessorAt(0, materialize_true);
2125 instr->SetSuccessorAt(1, materialize_false);
2126 owner()->current_block()->Finish(instr);
2127 owner()->set_current_block(materialize_true);
2128 owner()->Push(owner()->graph()->GetConstantTrue());
2129 owner()->set_current_block(materialize_false);
2130 owner()->Push(owner()->graph()->GetConstantFalse());
2131 HBasicBlock* join =
2132 owner()->CreateJoin(materialize_true, materialize_false, ast_id);
2133 owner()->set_current_block(join);
2134}
2135
2136
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002137void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002138 ASSERT(!instr->IsControlInstruction());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002139 HGraphBuilder* builder = owner();
2140 builder->AddInstruction(instr);
2141 // We expect a simulate after every expression with side effects, though
2142 // this one isn't actually needed (and wouldn't work if it were targeted).
2143 if (instr->HasSideEffects()) {
2144 builder->Push(instr);
2145 builder->AddSimulate(ast_id);
2146 builder->Pop();
2147 }
2148 BuildBranch(instr);
2149}
2150
2151
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002152void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) {
2153 ASSERT(!instr->HasSideEffects());
2154 HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
2155 HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
2156 instr->SetSuccessorAt(0, empty_true);
2157 instr->SetSuccessorAt(1, empty_false);
2158 owner()->current_block()->Finish(instr);
2159 empty_true->Goto(if_true());
2160 empty_false->Goto(if_false());
2161 owner()->set_current_block(NULL);
2162}
2163
2164
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002165void TestContext::BuildBranch(HValue* value) {
2166 // We expect the graph to be in edge-split form: there is no edge that
2167 // connects a branch node to a join node. We conservatively ensure that
2168 // property by always adding an empty block on the outgoing edges of this
2169 // branch.
2170 HGraphBuilder* builder = owner();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002171 if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002172 builder->Bailout("arguments object value in a test context");
2173 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002174 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
2175 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002176 unsigned test_id = condition()->test_id();
2177 ToBooleanStub::Types expected(builder->oracle()->ToBooleanTypes(test_id));
2178 HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002179 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002180
ager@chromium.org04921a82011-06-27 13:21:41 +00002181 empty_true->Goto(if_true());
2182 empty_false->Goto(if_false());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002183 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002184}
2185
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002186
2187// HGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002188#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002189 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002190 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002191 if (HasStackOverflow()) return; \
2192 } while (false)
2193
2194
danno@chromium.org160a7b02011-04-18 15:51:38 +00002195#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002196 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002197 call; \
2198 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002199 } while (false)
2200
2201
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002202void HGraphBuilder::Bailout(const char* reason) {
2203 if (FLAG_trace_bailout) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002204 SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString());
2205 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002206 }
2207 SetStackOverflow();
2208}
2209
2210
2211void HGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002212 EffectContext for_effect(this);
2213 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002214}
2215
2216
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002217void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
2218 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002219 Visit(expr);
2220}
2221
2222
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002223void HGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002224 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002225 for_value.set_for_typeof(true);
2226 Visit(expr);
2227}
2228
2229
2230
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002231void HGraphBuilder::VisitForControl(Expression* expr,
2232 HBasicBlock* true_block,
2233 HBasicBlock* false_block) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002234 TestContext for_test(this, expr, true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002235 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002236}
2237
2238
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00002239HValue* HGraphBuilder::VisitArgument(Expression* expr) {
2240 VisitForValue(expr);
2241 if (HasStackOverflow() || current_block() == NULL) return NULL;
2242 HValue* value = Pop();
2243 Push(AddInstruction(new(zone()) HPushArgument(value)));
2244 return value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002245}
2246
2247
2248void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
2249 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002250 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002251 }
2252}
2253
2254
2255void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
2256 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002257 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002258 }
2259}
2260
2261
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002262HGraph* HGraphBuilder::CreateGraph() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002263 graph_ = new(zone()) HGraph(info());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002264 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
2265
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002266 {
2267 HPhase phase("Block building");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002268 current_block_ = graph()->entry_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002269
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002270 Scope* scope = info()->scope();
2271 if (scope->HasIllegalRedeclaration()) {
2272 Bailout("function with illegal redeclaration");
2273 return NULL;
2274 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002275 SetupScope(scope);
2276 VisitDeclarations(scope->declarations());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002277 HValue* context = environment()->LookupContext();
2278 AddInstruction(
2279 new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002280
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002281 // Add an edge to the body entry. This is warty: the graph's start
2282 // environment will be used by the Lithium translation as the initial
2283 // environment on graph entry, but it has now been mutated by the
2284 // Hydrogen translation of the instructions in the start block. This
2285 // environment uses values which have not been defined yet. These
2286 // Hydrogen instructions will then be replayed by the Lithium
2287 // translation, so they cannot have an environment effect. The edge to
2288 // the body's entry block (along with some special logic for the start
2289 // block in HInstruction::InsertAfter) seals the start block from
2290 // getting unwanted instructions inserted.
2291 //
2292 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
2293 // Make the Hydrogen instructions in the initial block into Hydrogen
2294 // values (but not instructions), present in the initial environment and
2295 // not replayed by the Lithium translation.
2296 HEnvironment* initial_env = environment()->CopyWithoutHistory();
2297 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
2298 current_block()->Goto(body_entry);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002299 body_entry->SetJoinId(AstNode::kFunctionEntryId);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002300 set_current_block(body_entry);
2301 VisitStatements(info()->function()->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002302 if (HasStackOverflow()) return NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002303
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002304 if (current_block() != NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002305 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002306 current_block()->FinishExit(instr);
2307 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002308 }
2309 }
2310
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002311 graph()->OrderBlocks();
2312 graph()->AssignDominators();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002313 graph()->PropagateDeoptimizingMark();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002314 graph()->EliminateRedundantPhis();
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00002315 if (!graph()->CheckPhis()) {
2316 Bailout("Unsupported phi use of arguments object");
2317 return NULL;
2318 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002319 if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002320 if (!graph()->CollectPhis()) {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +00002321 Bailout("Unsupported phi use of uninitialized constant");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002322 return NULL;
2323 }
2324
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002325 HInferRepresentation rep(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002326 rep.Analyze();
2327
2328 if (FLAG_use_range) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002329 HRangeAnalysis rangeAnalysis(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002330 rangeAnalysis.Analyze();
2331 }
2332
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002333 graph()->InitializeInferredTypes();
2334 graph()->Canonicalize();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002335 graph()->MarkDeoptimizeOnUndefined();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002336 graph()->InsertRepresentationChanges();
2337 graph()->ComputeMinusZeroChecks();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002338
2339 // Eliminate redundant stack checks on backwards branches.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002340 HStackCheckEliminator sce(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002341 sce.Process();
2342
2343 // Perform common subexpression elimination and loop-invariant code motion.
2344 if (FLAG_use_gvn) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002345 HPhase phase("Global value numbering", graph());
2346 HGlobalValueNumberer gvn(graph(), info());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002347 gvn.Analyze();
2348 }
2349
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002350 // Replace the results of check instructions with the original value, if the
2351 // result is used. This is safe now, since we don't do code motion after this
2352 // point. It enables better register allocation since the value produced by
2353 // check instructions is really a copy of the original value.
2354 graph()->ReplaceCheckedValues();
2355
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002356 return graph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002357}
2358
2359
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002360void HGraph::ReplaceCheckedValues() {
2361 HPhase phase("Replace checked values", this);
2362 for (int i = 0; i < blocks()->length(); ++i) {
2363 HInstruction* instr = blocks()->at(i)->first();
2364 while (instr != NULL) {
2365 if (instr->IsBoundsCheck()) {
2366 // Replace all uses of the checked value with the original input.
2367 ASSERT(instr->UseCount() > 0);
2368 instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index());
2369 }
2370 instr = instr->next();
2371 }
2372 }
2373}
2374
2375
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002376HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002377 ASSERT(current_block() != NULL);
2378 current_block()->AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002379 return instr;
2380}
2381
2382
ager@chromium.org04921a82011-06-27 13:21:41 +00002383void HGraphBuilder::AddSimulate(int ast_id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002384 ASSERT(current_block() != NULL);
ager@chromium.org04921a82011-06-27 13:21:41 +00002385 current_block()->AddSimulate(ast_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002386}
2387
2388
2389void HGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002390 ASSERT(current_block() != NULL);
2391 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002392}
2393
2394
2395void HGraphBuilder::PushAndAdd(HInstruction* instr) {
2396 Push(instr);
2397 AddInstruction(instr);
2398}
2399
2400
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002401template <int V>
2402HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002403 int count = call->argument_count();
2404 ZoneList<HValue*> arguments(count);
2405 for (int i = 0; i < count; ++i) {
2406 arguments.Add(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002407 }
2408
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002409 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002410 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002411 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002412 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002413}
2414
2415
2416void HGraphBuilder::SetupScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002417 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002418 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002419 AddInstruction(undefined_constant);
2420 graph_->set_undefined_constant(undefined_constant);
2421
2422 // Set the initial values of parameters including "this". "This" has
2423 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002424 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
2425
2426 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002427 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002428 environment()->Bind(i, parameter);
2429 }
2430
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002431 // First special is HContext.
2432 HInstruction* context = AddInstruction(new(zone()) HContext);
2433 environment()->BindContext(context);
2434
2435 // Initialize specials and locals to undefined.
2436 for (int i = environment()->parameter_count() + 1;
2437 i < environment()->length();
2438 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002439 environment()->Bind(i, undefined_constant);
2440 }
2441
2442 // Handle the arguments and arguments shadow variables specially (they do
2443 // not have declarations).
2444 if (scope->arguments() != NULL) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00002445 if (!scope->arguments()->IsStackAllocated()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002446 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002447 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002448 HArgumentsObject* object = new(zone()) HArgumentsObject;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002449 AddInstruction(object);
2450 graph()->SetArgumentsObject(object);
2451 environment()->Bind(scope->arguments(), object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002452 }
2453}
2454
2455
2456void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
2457 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002458 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002459 }
2460}
2461
2462
2463HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
2464 HBasicBlock* b = graph()->CreateBasicBlock();
2465 b->SetInitialEnvironment(env);
2466 return b;
2467}
2468
2469
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002470HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002471 HBasicBlock* header = graph()->CreateBasicBlock();
2472 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
2473 header->SetInitialEnvironment(entry_env);
2474 header->AttachLoopInformation();
2475 return header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002476}
2477
2478
2479void HGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002480 ASSERT(!HasStackOverflow());
2481 ASSERT(current_block() != NULL);
2482 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002483 if (stmt->block_scope() != NULL) {
2484 return Bailout("ScopedBlock");
2485 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002486 BreakAndContinueInfo break_info(stmt);
2487 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002488 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002489 }
2490 HBasicBlock* break_block = break_info.break_block();
2491 if (break_block != NULL) {
2492 if (current_block() != NULL) current_block()->Goto(break_block);
2493 break_block->SetJoinId(stmt->ExitId());
2494 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002495 }
2496}
2497
2498
2499void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002500 ASSERT(!HasStackOverflow());
2501 ASSERT(current_block() != NULL);
2502 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002503 VisitForEffect(stmt->expression());
2504}
2505
2506
2507void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002508 ASSERT(!HasStackOverflow());
2509 ASSERT(current_block() != NULL);
2510 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002511}
2512
2513
2514void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002515 ASSERT(!HasStackOverflow());
2516 ASSERT(current_block() != NULL);
2517 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002518 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002519 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002520 Visit(stmt->then_statement());
2521 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002522 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002523 Visit(stmt->else_statement());
2524 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002525 HBasicBlock* cond_true = graph()->CreateBasicBlock();
2526 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002527 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002528
danno@chromium.org160a7b02011-04-18 15:51:38 +00002529 if (cond_true->HasPredecessor()) {
2530 cond_true->SetJoinId(stmt->ThenId());
2531 set_current_block(cond_true);
2532 CHECK_BAILOUT(Visit(stmt->then_statement()));
2533 cond_true = current_block();
2534 } else {
2535 cond_true = NULL;
2536 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002537
danno@chromium.org160a7b02011-04-18 15:51:38 +00002538 if (cond_false->HasPredecessor()) {
2539 cond_false->SetJoinId(stmt->ElseId());
2540 set_current_block(cond_false);
2541 CHECK_BAILOUT(Visit(stmt->else_statement()));
2542 cond_false = current_block();
2543 } else {
2544 cond_false = NULL;
2545 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002546
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002547 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002548 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002549 }
2550}
2551
2552
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002553HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
2554 BreakableStatement* stmt,
2555 BreakType type) {
2556 BreakAndContinueScope* current = this;
2557 while (current != NULL && current->info()->target() != stmt) {
2558 current = current->next();
2559 }
2560 ASSERT(current != NULL); // Always found (unless stack is malformed).
2561 HBasicBlock* block = NULL;
2562 switch (type) {
2563 case BREAK:
2564 block = current->info()->break_block();
2565 if (block == NULL) {
2566 block = current->owner()->graph()->CreateBasicBlock();
2567 current->info()->set_break_block(block);
2568 }
2569 break;
2570
2571 case CONTINUE:
2572 block = current->info()->continue_block();
2573 if (block == NULL) {
2574 block = current->owner()->graph()->CreateBasicBlock();
2575 current->info()->set_continue_block(block);
2576 }
2577 break;
2578 }
2579
2580 return block;
2581}
2582
2583
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002584void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002585 ASSERT(!HasStackOverflow());
2586 ASSERT(current_block() != NULL);
2587 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002588 HBasicBlock* continue_block = break_scope()->Get(stmt->target(), CONTINUE);
2589 current_block()->Goto(continue_block);
2590 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002591}
2592
2593
2594void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002595 ASSERT(!HasStackOverflow());
2596 ASSERT(current_block() != NULL);
2597 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002598 HBasicBlock* break_block = break_scope()->Get(stmt->target(), BREAK);
2599 current_block()->Goto(break_block);
2600 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002601}
2602
2603
2604void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002605 ASSERT(!HasStackOverflow());
2606 ASSERT(current_block() != NULL);
2607 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002608 AstContext* context = call_context();
2609 if (context == NULL) {
2610 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002611 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002612 HValue* result = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002613 current_block()->FinishExit(new(zone()) HReturn(result));
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002614 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002615 } else {
2616 // Return from an inlined function, visit the subexpression in the
2617 // expression context of the call.
2618 if (context->IsTest()) {
2619 TestContext* test = TestContext::cast(context);
2620 VisitForControl(stmt->expression(),
2621 test->if_true(),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002622 test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002623 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002624 CHECK_ALIVE(VisitForEffect(stmt->expression()));
ager@chromium.org04921a82011-06-27 13:21:41 +00002625 current_block()->Goto(function_return());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002626 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002627 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00002628 CHECK_ALIVE(VisitForValue(stmt->expression()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002629 HValue* return_value = environment()->Pop();
2630 current_block()->AddLeaveInlined(return_value, function_return());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002631 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002632 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002633 }
2634}
2635
2636
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002637void HGraphBuilder::VisitWithStatement(WithStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002638 ASSERT(!HasStackOverflow());
2639 ASSERT(current_block() != NULL);
2640 ASSERT(current_block()->HasPredecessor());
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00002641 return Bailout("WithStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002642}
2643
2644
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002645void HGraphBuilder::VisitExitContextStatement(ExitContextStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002646 ASSERT(!HasStackOverflow());
2647 ASSERT(current_block() != NULL);
2648 ASSERT(current_block()->HasPredecessor());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002649 return Bailout("ExitContextStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002650}
2651
2652
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002653void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002654 ASSERT(!HasStackOverflow());
2655 ASSERT(current_block() != NULL);
2656 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002657 // We only optimize switch statements with smi-literal smi comparisons,
2658 // with a bounded number of clauses.
2659 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002660 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002661 int clause_count = clauses->length();
2662 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002663 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002664 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002665
danno@chromium.org160a7b02011-04-18 15:51:38 +00002666 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002667 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002668 HValue* tag_value = Pop();
2669 HBasicBlock* first_test_block = current_block();
2670
2671 // 1. Build all the tests, with dangling true branches. Unconditionally
2672 // deoptimize if we encounter a non-smi comparison.
2673 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002674 CaseClause* clause = clauses->at(i);
2675 if (clause->is_default()) continue;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002676 if (!clause->label()->IsSmiLiteral()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002677 return Bailout("SwitchStatement: non-literal switch label");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002678 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002679
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002680 // Unconditionally deoptimize on the first non-smi compare.
2681 clause->RecordTypeFeedback(oracle());
2682 if (!clause->IsSmiCompare()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002683 // Finish with deoptimize and add uses of enviroment values to
2684 // account for invisible uses.
2685 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002686 set_current_block(NULL);
2687 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002688 }
2689
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002690 // Otherwise generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002691 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002692 HValue* label_value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002693 HCompareIDAndBranch* compare =
2694 new(zone()) HCompareIDAndBranch(tag_value,
2695 label_value,
2696 Token::EQ_STRICT);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002697 compare->SetInputRepresentation(Representation::Integer32());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002698 HBasicBlock* body_block = graph()->CreateBasicBlock();
2699 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002700 compare->SetSuccessorAt(0, body_block);
2701 compare->SetSuccessorAt(1, next_test_block);
2702 current_block()->Finish(compare);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002703 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002704 }
2705
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002706 // Save the current block to use for the default or to join with the
2707 // exit. This block is NULL if we deoptimized.
2708 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002709
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002710 // 2. Loop over the clauses and the linked list of tests in lockstep,
2711 // translating the clause bodies.
2712 HBasicBlock* curr_test_block = first_test_block;
2713 HBasicBlock* fall_through_block = NULL;
2714 BreakAndContinueInfo break_info(stmt);
2715 { BreakAndContinueScope push(&break_info, this);
2716 for (int i = 0; i < clause_count; ++i) {
2717 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002718
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002719 // Identify the block where normal (non-fall-through) control flow
2720 // goes to.
2721 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00002722 if (clause->is_default()) {
2723 if (last_block != NULL) {
2724 normal_block = last_block;
2725 last_block = NULL; // Cleared to indicate we've handled it.
2726 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002727 } else if (!curr_test_block->end()->IsDeoptimize()) {
2728 normal_block = curr_test_block->end()->FirstSuccessor();
2729 curr_test_block = curr_test_block->end()->SecondSuccessor();
2730 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002731
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002732 // Identify a block to emit the body into.
2733 if (normal_block == NULL) {
2734 if (fall_through_block == NULL) {
2735 // (a) Unreachable.
2736 if (clause->is_default()) {
2737 continue; // Might still be reachable clause bodies.
2738 } else {
2739 break;
2740 }
2741 } else {
2742 // (b) Reachable only as fall through.
2743 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002744 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002745 } else if (fall_through_block == NULL) {
2746 // (c) Reachable only normally.
2747 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002748 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002749 // (d) Reachable both ways.
2750 HBasicBlock* join = CreateJoin(fall_through_block,
2751 normal_block,
2752 clause->EntryId());
2753 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002754 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002755
danno@chromium.org160a7b02011-04-18 15:51:38 +00002756 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002757 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002758 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002759 }
2760
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002761 // Create an up-to-3-way join. Use the break block if it exists since
2762 // it's already a join block.
2763 HBasicBlock* break_block = break_info.break_block();
2764 if (break_block == NULL) {
2765 set_current_block(CreateJoin(fall_through_block,
2766 last_block,
2767 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002768 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002769 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
2770 if (last_block != NULL) last_block->Goto(break_block);
2771 break_block->SetJoinId(stmt->ExitId());
2772 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002773 }
2774}
2775
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002776
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002777bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002778 return statement->OsrEntryId() == info()->osr_ast_id();
2779}
2780
2781
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002782void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002783 if (!HasOsrEntryAt(statement)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002784
2785 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
2786 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
2787 HValue* true_value = graph()->GetConstantTrue();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002788 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002789 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002790
2791 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
2792 non_osr_entry->Goto(loop_predecessor);
2793
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002794 set_current_block(osr_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002795 int osr_entry_id = statement->OsrEntryId();
2796 // We want the correct environment at the OsrEntry instruction. Build
2797 // it explicitly. The expression stack should be empty.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002798 ASSERT(environment()->ExpressionStackIsEmpty());
2799 for (int i = 0; i < environment()->length(); ++i) {
2800 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
2801 AddInstruction(osr_value);
2802 environment()->Bind(i, osr_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002803 }
2804
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002805 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002806 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002807 HContext* context = new(zone()) HContext;
2808 AddInstruction(context);
2809 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002810 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002811 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002812 set_current_block(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002813}
2814
2815
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002816void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
ager@chromium.org04921a82011-06-27 13:21:41 +00002817 HBasicBlock* loop_entry,
2818 BreakAndContinueInfo* break_info) {
2819 BreakAndContinueScope push(break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002820 AddSimulate(stmt->StackCheckId());
2821 HValue* context = environment()->LookupContext();
ager@chromium.org04921a82011-06-27 13:21:41 +00002822 HStackCheck* stack_check =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002823 new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
ager@chromium.org04921a82011-06-27 13:21:41 +00002824 AddInstruction(stack_check);
2825 ASSERT(loop_entry->IsLoopHeader());
2826 loop_entry->loop_information()->set_stack_check(stack_check);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002827 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org04921a82011-06-27 13:21:41 +00002828}
2829
2830
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002831void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002832 ASSERT(!HasStackOverflow());
2833 ASSERT(current_block() != NULL);
2834 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002835 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002836 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002837 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00002838 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002839 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002840
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002841 BreakAndContinueInfo break_info(stmt);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002842 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002843 HBasicBlock* body_exit =
2844 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002845 HBasicBlock* loop_successor = NULL;
2846 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002847 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002848 // The block for a true condition, the actual predecessor block of the
2849 // back edge.
2850 body_exit = graph()->CreateBasicBlock();
2851 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002852 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
2853 if (body_exit->HasPredecessor()) {
2854 body_exit->SetJoinId(stmt->BackEdgeId());
2855 } else {
2856 body_exit = NULL;
2857 }
2858 if (loop_successor->HasPredecessor()) {
2859 loop_successor->SetJoinId(stmt->ExitId());
2860 } else {
2861 loop_successor = NULL;
2862 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002863 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002864 HBasicBlock* loop_exit = CreateLoop(stmt,
2865 loop_entry,
2866 body_exit,
2867 loop_successor,
2868 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002869 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002870}
2871
2872
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002873void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002874 ASSERT(!HasStackOverflow());
2875 ASSERT(current_block() != NULL);
2876 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002877 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002878 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002879 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00002880 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002881 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002882
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002883 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002884 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002885 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002886 HBasicBlock* body_entry = graph()->CreateBasicBlock();
2887 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002888 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
2889 if (body_entry->HasPredecessor()) {
2890 body_entry->SetJoinId(stmt->BodyId());
2891 set_current_block(body_entry);
2892 }
2893 if (loop_successor->HasPredecessor()) {
2894 loop_successor->SetJoinId(stmt->ExitId());
2895 } else {
2896 loop_successor = NULL;
2897 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002898 }
2899
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002900 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002901 if (current_block() != NULL) {
2902 BreakAndContinueScope push(&break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002903 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002904 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002905 HBasicBlock* body_exit =
2906 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002907 HBasicBlock* loop_exit = CreateLoop(stmt,
2908 loop_entry,
2909 body_exit,
2910 loop_successor,
2911 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002912 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002913}
2914
2915
2916void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002917 ASSERT(!HasStackOverflow());
2918 ASSERT(current_block() != NULL);
2919 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002920 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002921 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002922 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002923 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002924 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002925 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org04921a82011-06-27 13:21:41 +00002926 current_block()->Goto(loop_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002927 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002928
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002929 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002930 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002931 HBasicBlock* body_entry = graph()->CreateBasicBlock();
2932 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002933 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
2934 if (body_entry->HasPredecessor()) {
2935 body_entry->SetJoinId(stmt->BodyId());
2936 set_current_block(body_entry);
2937 }
2938 if (loop_successor->HasPredecessor()) {
2939 loop_successor->SetJoinId(stmt->ExitId());
2940 } else {
2941 loop_successor = NULL;
2942 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002943 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002944
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002945 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002946 if (current_block() != NULL) {
2947 BreakAndContinueScope push(&break_info, this);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002948 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002949 }
2950 HBasicBlock* body_exit =
2951 JoinContinue(stmt, current_block(), break_info.continue_block());
2952
2953 if (stmt->next() != NULL && body_exit != NULL) {
2954 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002955 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002956 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002957 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002958
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002959 HBasicBlock* loop_exit = CreateLoop(stmt,
2960 loop_entry,
2961 body_exit,
2962 loop_successor,
2963 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002964 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002965}
2966
2967
2968void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002969 ASSERT(!HasStackOverflow());
2970 ASSERT(current_block() != NULL);
2971 ASSERT(current_block()->HasPredecessor());
2972 return Bailout("ForInStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002973}
2974
2975
2976void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002977 ASSERT(!HasStackOverflow());
2978 ASSERT(current_block() != NULL);
2979 ASSERT(current_block()->HasPredecessor());
2980 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002981}
2982
2983
2984void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002985 ASSERT(!HasStackOverflow());
2986 ASSERT(current_block() != NULL);
2987 ASSERT(current_block()->HasPredecessor());
2988 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002989}
2990
2991
2992void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002993 ASSERT(!HasStackOverflow());
2994 ASSERT(current_block() != NULL);
2995 ASSERT(current_block()->HasPredecessor());
2996 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002997}
2998
2999
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003000static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
3001 Code* unoptimized_code, FunctionLiteral* expr) {
3002 int start_position = expr->start_position();
3003 RelocIterator it(unoptimized_code);
3004 for (;!it.done(); it.next()) {
3005 RelocInfo* rinfo = it.rinfo();
3006 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
3007 Object* obj = rinfo->target_object();
3008 if (obj->IsSharedFunctionInfo()) {
3009 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
3010 if (shared->start_position() == start_position) {
3011 return Handle<SharedFunctionInfo>(shared);
3012 }
3013 }
3014 }
3015
3016 return Handle<SharedFunctionInfo>();
3017}
3018
3019
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003020void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003021 ASSERT(!HasStackOverflow());
3022 ASSERT(current_block() != NULL);
3023 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003024 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003025 SearchSharedFunctionInfo(info()->shared_info()->code(),
3026 expr);
3027 if (shared_info.is_null()) {
3028 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
3029 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00003030 // We also have a stack overflow if the recursive compilation did.
3031 if (HasStackOverflow()) return;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003032 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003033 HFunctionLiteral* instr =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003034 new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003035 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003036}
3037
3038
3039void HGraphBuilder::VisitSharedFunctionInfoLiteral(
3040 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003041 ASSERT(!HasStackOverflow());
3042 ASSERT(current_block() != NULL);
3043 ASSERT(current_block()->HasPredecessor());
3044 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003045}
3046
3047
3048void HGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003049 ASSERT(!HasStackOverflow());
3050 ASSERT(current_block() != NULL);
3051 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003052 HBasicBlock* cond_true = graph()->CreateBasicBlock();
3053 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003054 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003055
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003056 // Visit the true and false subexpressions in the same AST context as the
3057 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003058 if (cond_true->HasPredecessor()) {
3059 cond_true->SetJoinId(expr->ThenId());
3060 set_current_block(cond_true);
3061 CHECK_BAILOUT(Visit(expr->then_expression()));
3062 cond_true = current_block();
3063 } else {
3064 cond_true = NULL;
3065 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003066
danno@chromium.org160a7b02011-04-18 15:51:38 +00003067 if (cond_false->HasPredecessor()) {
3068 cond_false->SetJoinId(expr->ElseId());
3069 set_current_block(cond_false);
3070 CHECK_BAILOUT(Visit(expr->else_expression()));
3071 cond_false = current_block();
3072 } else {
3073 cond_false = NULL;
3074 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003075
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003076 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003077 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003078 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003079 if (join != NULL && !ast_context()->IsEffect()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003080 return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003081 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003082 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003083}
3084
3085
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003086HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
3087 Variable* var, LookupResult* lookup, bool is_store) {
3088 if (var->is_this() || !info()->has_global_object()) {
3089 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003090 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003091 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003092 global->Lookup(*var->name(), lookup);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003093 if (!lookup->IsProperty() ||
3094 lookup->type() != NORMAL ||
3095 (is_store && lookup->IsReadOnly()) ||
3096 lookup->holder() != *global) {
3097 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003098 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003099
3100 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003101}
3102
3103
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003104HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
3105 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003106 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003107 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003108 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003109 HInstruction* context_instruction = new(zone()) HOuterContext(context);
3110 AddInstruction(context_instruction);
3111 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003112 }
3113 return context;
3114}
3115
3116
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003117void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003118 ASSERT(!HasStackOverflow());
3119 ASSERT(current_block() != NULL);
3120 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003121 Variable* variable = expr->AsVariable();
3122 if (variable == NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003123 return Bailout("reference to rewritten variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003124 } else if (variable->IsStackAllocated()) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003125 HValue* value = environment()->Lookup(variable);
3126 if (variable->mode() == Variable::CONST &&
3127 value == graph()->GetConstantHole()) {
3128 return Bailout("reference to uninitialized const variable");
3129 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003130 return ast_context()->ReturnValue(value);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003131 } else if (variable->IsContextSlot()) {
3132 if (variable->mode() == Variable::CONST) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003133 return Bailout("reference to const context slot");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003134 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003135 HValue* context = BuildContextChainWalk(variable);
3136 int index = variable->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003137 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003138 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003139 } else if (variable->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003140 LookupResult lookup;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003141 GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003142
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003143 if (type == kUseCell &&
3144 info()->global_object()->IsAccessCheckNeeded()) {
3145 type = kUseGeneric;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003146 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003147
3148 if (type == kUseCell) {
3149 Handle<GlobalObject> global(info()->global_object());
3150 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
3151 bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003152 HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003153 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003154 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003155 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003156 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003157 AddInstruction(global_object);
3158 HLoadGlobalGeneric* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003159 new(zone()) HLoadGlobalGeneric(context,
3160 global_object,
3161 variable->name(),
3162 ast_context()->is_for_typeof());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003163 instr->set_position(expr->position());
3164 ASSERT(instr->HasSideEffects());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003165 return ast_context()->ReturnInstruction(instr, expr->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003166 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003167 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003168 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003169 }
3170}
3171
3172
3173void HGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003174 ASSERT(!HasStackOverflow());
3175 ASSERT(current_block() != NULL);
3176 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003177 HConstant* instr =
3178 new(zone()) HConstant(expr->handle(), Representation::Tagged());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003179 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003180}
3181
3182
3183void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003184 ASSERT(!HasStackOverflow());
3185 ASSERT(current_block() != NULL);
3186 ASSERT(current_block()->HasPredecessor());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003187 HValue* context = environment()->LookupContext();
3188
3189 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
3190 expr->pattern(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003191 expr->flags(),
3192 expr->literal_index());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003193 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003194}
3195
3196
3197void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003198 ASSERT(!HasStackOverflow());
3199 ASSERT(current_block() != NULL);
3200 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003201 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003202 HObjectLiteral* literal =
3203 new(zone()) HObjectLiteral(context,
3204 expr->constant_properties(),
3205 expr->fast_elements(),
3206 expr->literal_index(),
3207 expr->depth(),
3208 expr->has_function());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003209 // The object is expected in the bailout environment during computation
3210 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003211 PushAndAdd(literal);
3212
3213 expr->CalculateEmitStore();
3214
3215 for (int i = 0; i < expr->properties()->length(); i++) {
3216 ObjectLiteral::Property* property = expr->properties()->at(i);
3217 if (property->IsCompileTimeValue()) continue;
3218
3219 Literal* key = property->key();
3220 Expression* value = property->value();
3221
3222 switch (property->kind()) {
3223 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3224 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
3225 // Fall through.
3226 case ObjectLiteral::Property::COMPUTED:
3227 if (key->handle()->IsSymbol()) {
3228 if (property->emit_store()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003229 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003230 HValue* value = Pop();
3231 Handle<String> name = Handle<String>::cast(key->handle());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003232 HStoreNamedGeneric* store =
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003233 new(zone()) HStoreNamedGeneric(
3234 context,
3235 literal,
3236 name,
3237 value,
3238 function_strict_mode());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003239 AddInstruction(store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003240 AddSimulate(key->id());
3241 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003242 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003243 }
3244 break;
3245 }
3246 // Fall through.
3247 case ObjectLiteral::Property::PROTOTYPE:
3248 case ObjectLiteral::Property::SETTER:
3249 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00003250 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003251 default: UNREACHABLE();
3252 }
3253 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003254
3255 if (expr->has_function()) {
3256 // Return the result of the transformation to fast properties
3257 // instead of the original since this operation changes the map
3258 // of the object. This makes sure that the original object won't
3259 // be used by other optimized code before it is transformed
3260 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003261 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003262 AddInstruction(result);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003263 return ast_context()->ReturnValue(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003264 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003265 return ast_context()->ReturnValue(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003266 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003267}
3268
3269
3270void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003271 ASSERT(!HasStackOverflow());
3272 ASSERT(current_block() != NULL);
3273 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003274 ZoneList<Expression*>* subexprs = expr->values();
3275 int length = subexprs->length();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003276 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003277
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003278 HArrayLiteral* literal = new(zone()) HArrayLiteral(context,
3279 expr->constant_elements(),
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003280 length,
3281 expr->literal_index(),
3282 expr->depth());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003283 // The array is expected in the bailout environment during computation
3284 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003285 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003286
3287 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003288
3289 for (int i = 0; i < length; i++) {
3290 Expression* subexpr = subexprs->at(i);
3291 // If the subexpression is a literal or a simple materialized literal it
3292 // is already set in the cloned array.
3293 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
3294
danno@chromium.org160a7b02011-04-18 15:51:38 +00003295 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003296 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003297 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003298
3299 // Load the elements array before the first store.
3300 if (elements == NULL) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003301 elements = new(zone()) HLoadElements(literal);
3302 AddInstruction(elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003303 }
3304
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003305 HValue* key = AddInstruction(
3306 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
3307 Representation::Integer32()));
3308 AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003309 AddSimulate(expr->GetIdForElement(i));
3310 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003311 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003312}
3313
3314
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003315// Sets the lookup result and returns true if the store can be inlined.
3316static bool ComputeStoredField(Handle<Map> type,
3317 Handle<String> name,
3318 LookupResult* lookup) {
3319 type->LookupInDescriptors(NULL, *name, lookup);
3320 if (!lookup->IsPropertyOrTransition()) return false;
3321 if (lookup->type() == FIELD) return true;
3322 return (lookup->type() == MAP_TRANSITION) &&
3323 (type->unused_property_fields() > 0);
3324}
3325
3326
3327static int ComputeStoredFieldIndex(Handle<Map> type,
3328 Handle<String> name,
3329 LookupResult* lookup) {
3330 ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION);
3331 if (lookup->type() == FIELD) {
3332 return lookup->GetLocalFieldIndexFromMap(*type);
3333 } else {
3334 Map* transition = lookup->GetTransitionMapFromMap(*type);
3335 return transition->PropertyIndexFor(*name) - type->inobject_properties();
3336 }
3337}
3338
3339
3340HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
3341 Handle<String> name,
3342 HValue* value,
3343 Handle<Map> type,
3344 LookupResult* lookup,
3345 bool smi_and_map_check) {
3346 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003347 AddInstruction(new(zone()) HCheckNonSmi(object));
3348 AddInstruction(new(zone()) HCheckMap(object, type));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003349 }
3350
3351 int index = ComputeStoredFieldIndex(type, name, lookup);
3352 bool is_in_object = index < 0;
3353 int offset = index * kPointerSize;
3354 if (index < 0) {
3355 // Negative property indices are in-object properties, indexed
3356 // from the end of the fixed part of the object.
3357 offset += type->instance_size();
3358 } else {
3359 offset += FixedArray::kHeaderSize;
3360 }
3361 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003362 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003363 if (lookup->type() == MAP_TRANSITION) {
3364 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
3365 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00003366 // TODO(fschneider): Record the new map type of the object in the IR to
3367 // enable elimination of redundant checks after the transition store.
3368 instr->SetFlag(HValue::kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003369 }
3370 return instr;
3371}
3372
3373
3374HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
3375 Handle<String> name,
3376 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003377 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003378 return new(zone()) HStoreNamedGeneric(
3379 context,
3380 object,
3381 name,
3382 value,
3383 function_strict_mode());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003384}
3385
3386
3387HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
3388 HValue* value,
3389 Expression* expr) {
3390 Property* prop = (expr->AsProperty() != NULL)
3391 ? expr->AsProperty()
3392 : expr->AsAssignment()->target()->AsProperty();
3393 Literal* key = prop->key()->AsLiteral();
3394 Handle<String> name = Handle<String>::cast(key->handle());
3395 ASSERT(!name.is_null());
3396
3397 LookupResult lookup;
3398 ZoneMapList* types = expr->GetReceiverTypes();
3399 bool is_monomorphic = expr->IsMonomorphic() &&
3400 ComputeStoredField(types->first(), name, &lookup);
3401
3402 return is_monomorphic
3403 ? BuildStoreNamedField(object, name, value, types->first(), &lookup,
3404 true) // Needs smi and map check.
3405 : BuildStoreNamedGeneric(object, name, value);
3406}
3407
3408
3409void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
3410 HValue* object,
3411 HValue* value,
3412 ZoneMapList* types,
3413 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003414 // TODO(ager): We should recognize when the prototype chains for different
3415 // maps are identical. In that case we can avoid repeatedly generating the
3416 // same prototype map checks.
3417 int count = 0;
3418 HBasicBlock* join = NULL;
3419 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003420 Handle<Map> map = types->at(i);
3421 LookupResult lookup;
3422 if (ComputeStoredField(map, name, &lookup)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003423 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003424 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003425 join = graph()->CreateBasicBlock();
3426 }
3427 ++count;
3428 HBasicBlock* if_true = graph()->CreateBasicBlock();
3429 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003430 HCompareMap* compare =
3431 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003432 current_block()->Finish(compare);
3433
3434 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003435 HInstruction* instr =
3436 BuildStoreNamedField(object, name, value, map, &lookup, false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003437 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003438 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003439 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003440 if (!ast_context()->IsEffect()) Push(value);
3441 current_block()->Goto(join);
3442
3443 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003444 }
3445 }
3446
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003447 // Finish up. Unconditionally deoptimize if we've handled all the maps we
3448 // know about and do not want to handle ones we've never seen. Otherwise
3449 // use a generic IC.
3450 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003451 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003452 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003453 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003454 instr->set_position(expr->position());
3455 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003456
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003457 if (join != NULL) {
3458 if (!ast_context()->IsEffect()) Push(value);
3459 current_block()->Goto(join);
3460 } else {
3461 // The HSimulate for the store should not see the stored value in
3462 // effect contexts (it is not materialized at expr->id() in the
3463 // unoptimized code).
3464 if (instr->HasSideEffects()) {
3465 if (ast_context()->IsEffect()) {
3466 AddSimulate(expr->id());
3467 } else {
3468 Push(value);
3469 AddSimulate(expr->id());
3470 Drop(1);
3471 }
3472 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003473 return ast_context()->ReturnValue(value);
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00003474 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00003475 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003476
3477 ASSERT(join != NULL);
3478 join->SetJoinId(expr->id());
3479 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003480 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003481}
3482
3483
3484void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
3485 Property* prop = expr->target()->AsProperty();
3486 ASSERT(prop != NULL);
3487 expr->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003488 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003489
3490 HValue* value = NULL;
3491 HInstruction* instr = NULL;
3492
3493 if (prop->key()->IsPropertyName()) {
3494 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003495 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003496 value = Pop();
3497 HValue* object = Pop();
3498
3499 Literal* key = prop->key()->AsLiteral();
3500 Handle<String> name = Handle<String>::cast(key->handle());
3501 ASSERT(!name.is_null());
3502
3503 ZoneMapList* types = expr->GetReceiverTypes();
3504 LookupResult lookup;
3505
3506 if (expr->IsMonomorphic()) {
3507 instr = BuildStoreNamed(object, value, expr);
3508
3509 } else if (types != NULL && types->length() > 1) {
3510 HandlePolymorphicStoreNamedField(expr, object, value, types, name);
3511 return;
3512
3513 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003514 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003515 }
3516
3517 } else {
3518 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003519 CHECK_ALIVE(VisitForValue(prop->key()));
3520 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003521 value = Pop();
3522 HValue* key = Pop();
3523 HValue* object = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00003524 bool has_side_effects = false;
3525 HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
3526 expr->position(),
3527 true, // is_store
3528 &has_side_effects);
3529 Push(value);
3530 ASSERT(has_side_effects); // Stores always have side effects.
3531 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003532 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003533 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003534 Push(value);
3535 instr->set_position(expr->position());
3536 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003537 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003538 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003539}
3540
3541
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003542// Because not every expression has a position and there is not common
3543// superclass of Assignment and CountOperation, we cannot just pass the
3544// owning expression instead of position and ast_id separately.
3545void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003546 HValue* value,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003547 int position,
3548 int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003549 LookupResult lookup;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003550 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
3551 if (type == kUseCell) {
3552 bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
3553 Handle<GlobalObject> global(info()->global_object());
3554 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003555 HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003556 instr->set_position(position);
3557 AddInstruction(instr);
3558 if (instr->HasSideEffects()) AddSimulate(ast_id);
3559 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003560 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003561 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
3562 AddInstruction(global_object);
3563 HStoreGlobalGeneric* instr =
3564 new(zone()) HStoreGlobalGeneric(context,
3565 global_object,
3566 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003567 value,
3568 function_strict_mode());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003569 instr->set_position(position);
3570 AddInstruction(instr);
3571 ASSERT(instr->HasSideEffects());
3572 if (instr->HasSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003573 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003574}
3575
3576
3577void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
3578 Expression* target = expr->target();
3579 VariableProxy* proxy = target->AsVariableProxy();
3580 Variable* var = proxy->AsVariable();
3581 Property* prop = target->AsProperty();
3582 ASSERT(var == NULL || prop == NULL);
3583
3584 // We have a second position recorded in the FullCodeGenerator to have
3585 // type feedback for the binary operation.
3586 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003587
3588 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003589 if (var->mode() == Variable::CONST) {
3590 return Bailout("unsupported const compound assignment");
3591 }
3592
danno@chromium.org160a7b02011-04-18 15:51:38 +00003593 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003594
3595 if (var->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003596 HandleGlobalVariableAssignment(var,
3597 Top(),
3598 expr->position(),
3599 expr->AssignmentId());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003600 } else if (var->IsStackAllocated()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003601 Bind(var, Top());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003602 } else if (var->IsContextSlot()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003603 // Bail out if we try to mutate a parameter value in a function using
3604 // the arguments object. We do not (yet) correctly handle the
3605 // arguments property of the function.
3606 if (info()->scope()->arguments() != NULL) {
3607 // Parameters will rewrite to context slots. We have no direct way
3608 // to detect that the variable is a parameter.
3609 int count = info()->scope()->num_parameters();
3610 for (int i = 0; i < count; ++i) {
3611 if (var == info()->scope()->parameter(i)) {
3612 Bailout("assignment to parameter, function uses arguments object");
3613 }
3614 }
3615 }
3616
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003617 HValue* context = BuildContextChainWalk(var);
3618 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003619 HStoreContextSlot* instr =
3620 new(zone()) HStoreContextSlot(context, index, Top());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003621 AddInstruction(instr);
3622 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
3623 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003624 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003625 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003626 return ast_context()->ReturnValue(Pop());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003627
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003628 } else if (prop != NULL) {
3629 prop->RecordTypeFeedback(oracle());
3630
3631 if (prop->key()->IsPropertyName()) {
3632 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003633 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003634 HValue* obj = Top();
3635
3636 HInstruction* load = NULL;
3637 if (prop->IsMonomorphic()) {
3638 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
3639 Handle<Map> map = prop->GetReceiverTypes()->first();
3640 load = BuildLoadNamed(obj, prop, map, name);
3641 } else {
3642 load = BuildLoadNamedGeneric(obj, prop);
3643 }
3644 PushAndAdd(load);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003645 if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003646
danno@chromium.org160a7b02011-04-18 15:51:38 +00003647 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003648 HValue* right = Pop();
3649 HValue* left = Pop();
3650
3651 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3652 PushAndAdd(instr);
3653 if (instr->HasSideEffects()) AddSimulate(operation->id());
3654
3655 HInstruction* store = BuildStoreNamed(obj, instr, prop);
3656 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003657 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003658 Drop(2);
3659 Push(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003660 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003661 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003662
3663 } else {
3664 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003665 CHECK_ALIVE(VisitForValue(prop->obj()));
3666 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003667 HValue* obj = environment()->ExpressionStackAt(1);
3668 HValue* key = environment()->ExpressionStackAt(0);
3669
whesse@chromium.org7b260152011-06-20 15:33:18 +00003670 bool has_side_effects = false;
3671 HValue* load = HandleKeyedElementAccess(
3672 obj, key, NULL, prop, expr->CompoundLoadId(), RelocInfo::kNoPosition,
3673 false, // is_store
3674 &has_side_effects);
3675 Push(load);
3676 if (has_side_effects) AddSimulate(expr->CompoundLoadId());
3677
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003678
danno@chromium.org160a7b02011-04-18 15:51:38 +00003679 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003680 HValue* right = Pop();
3681 HValue* left = Pop();
3682
3683 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3684 PushAndAdd(instr);
3685 if (instr->HasSideEffects()) AddSimulate(operation->id());
3686
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003687 expr->RecordTypeFeedback(oracle());
whesse@chromium.org7b260152011-06-20 15:33:18 +00003688 HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
3689 RelocInfo::kNoPosition,
3690 true, // is_store
3691 &has_side_effects);
3692
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003693 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003694 Drop(3);
3695 Push(instr);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003696 ASSERT(has_side_effects); // Stores always have side effects.
3697 AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003698 return ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003699 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003700
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003701 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003702 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003703 }
3704}
3705
3706
3707void HGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003708 ASSERT(!HasStackOverflow());
3709 ASSERT(current_block() != NULL);
3710 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003711 VariableProxy* proxy = expr->target()->AsVariableProxy();
3712 Variable* var = proxy->AsVariable();
3713 Property* prop = expr->target()->AsProperty();
3714 ASSERT(var == NULL || prop == NULL);
3715
3716 if (expr->is_compound()) {
3717 HandleCompoundAssignment(expr);
3718 return;
3719 }
3720
3721 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003722 if (var->mode() == Variable::CONST) {
3723 if (expr->op() != Token::INIT_CONST) {
3724 return Bailout("non-initializer assignment to const");
3725 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003726 if (!var->IsStackAllocated()) {
3727 return Bailout("assignment to const context slot");
3728 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003729 // We insert a use of the old value to detect unsupported uses of const
3730 // variables (e.g. initialization inside a loop).
3731 HValue* old_value = environment()->Lookup(var);
3732 AddInstruction(new HUseConst(old_value));
3733 }
3734
danno@chromium.org160a7b02011-04-18 15:51:38 +00003735 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003736
3737 // Handle the assignment.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003738 if (var->IsStackAllocated()) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003739 // We do not allow the arguments object to occur in a context where it
3740 // may escape, but assignments to stack-allocated locals are
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003741 // permitted.
3742 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
3743 HValue* value = Pop();
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003744 Bind(var, value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003745 return ast_context()->ReturnValue(value);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003746
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003747 } else if (var->IsContextSlot()) {
3748 ASSERT(var->mode() != Variable::CONST);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003749 // Bail out if we try to mutate a parameter value in a function using
3750 // the arguments object. We do not (yet) correctly handle the
3751 // arguments property of the function.
3752 if (info()->scope()->arguments() != NULL) {
3753 // Parameters will rewrite to context slots. We have no direct way
3754 // to detect that the variable is a parameter.
3755 int count = info()->scope()->num_parameters();
3756 for (int i = 0; i < count; ++i) {
3757 if (var == info()->scope()->parameter(i)) {
3758 Bailout("assignment to parameter, function uses arguments object");
3759 }
3760 }
3761 }
3762
danno@chromium.org160a7b02011-04-18 15:51:38 +00003763 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003764 HValue* context = BuildContextChainWalk(var);
3765 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003766 HStoreContextSlot* instr =
3767 new(zone()) HStoreContextSlot(context, index, Top());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003768 AddInstruction(instr);
3769 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003770 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003771
3772 } else if (var->is_global()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003773 CHECK_ALIVE(VisitForValue(expr->value()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003774 HandleGlobalVariableAssignment(var,
3775 Top(),
3776 expr->position(),
3777 expr->AssignmentId());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00003778 return ast_context()->ReturnValue(Pop());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003779
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003780 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003781 return Bailout("assignment to LOOKUP or const CONTEXT variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003782 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003783
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003784 } else if (prop != NULL) {
3785 HandlePropertyAssignment(expr);
3786 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003787 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003788 }
3789}
3790
3791
3792void HGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003793 ASSERT(!HasStackOverflow());
3794 ASSERT(current_block() != NULL);
3795 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003796 // We don't optimize functions with invalid left-hand sides in
3797 // assignments, count operations, or for-in. Consequently throw can
3798 // currently only occur in an effect context.
3799 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003800 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003801
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003802 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003803 HValue* value = environment()->Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003804 HThrow* instr = new(zone()) HThrow(context, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003805 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003806 AddInstruction(instr);
3807 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003808 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00003809 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003810}
3811
3812
whesse@chromium.org023421e2010-12-21 12:19:12 +00003813HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
3814 Property* expr,
3815 Handle<Map> type,
3816 LookupResult* lookup,
3817 bool smi_and_map_check) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003818 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003819 AddInstruction(new(zone()) HCheckNonSmi(object));
3820 AddInstruction(new(zone()) HCheckMap(object, type));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003821 }
3822
3823 int index = lookup->GetLocalFieldIndexFromMap(*type);
3824 if (index < 0) {
3825 // Negative property indices are in-object properties, indexed
3826 // from the end of the fixed part of the object.
3827 int offset = (index * kPointerSize) + type->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003828 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003829 } else {
3830 // Non-negative property indices are in the properties array.
3831 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003832 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003833 }
3834}
3835
3836
3837HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
3838 Property* expr) {
3839 ASSERT(expr->key()->IsPropertyName());
3840 Handle<Object> name = expr->key()->AsLiteral()->handle();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003841 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003842 return new(zone()) HLoadNamedGeneric(context, obj, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003843}
3844
3845
3846HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
3847 Property* expr,
3848 Handle<Map> map,
3849 Handle<String> name) {
3850 LookupResult lookup;
3851 map->LookupInDescriptors(NULL, *name, &lookup);
3852 if (lookup.IsProperty() && lookup.type() == FIELD) {
3853 return BuildLoadNamedField(obj,
3854 expr,
3855 map,
3856 &lookup,
3857 true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003858 } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003859 AddInstruction(new(zone()) HCheckNonSmi(obj));
3860 AddInstruction(new(zone()) HCheckMap(obj, map));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003861 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003862 return new(zone()) HConstant(function, Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003863 } else {
3864 return BuildLoadNamedGeneric(obj, expr);
3865 }
3866}
3867
3868
3869HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
3870 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003871 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003872 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003873}
3874
3875
whesse@chromium.org7b260152011-06-20 15:33:18 +00003876HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
3877 HValue* external_elements,
3878 HValue* checked_key,
3879 HValue* val,
3880 JSObject::ElementsKind elements_kind,
3881 bool is_store) {
3882 if (is_store) {
3883 ASSERT(val != NULL);
3884 switch (elements_kind) {
3885 case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
3886 HClampToUint8* clamp = new(zone()) HClampToUint8(val);
3887 AddInstruction(clamp);
3888 val = clamp;
3889 break;
3890 }
3891 case JSObject::EXTERNAL_BYTE_ELEMENTS:
3892 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3893 case JSObject::EXTERNAL_SHORT_ELEMENTS:
3894 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3895 case JSObject::EXTERNAL_INT_ELEMENTS:
3896 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
3897 HToInt32* floor_val = new(zone()) HToInt32(val);
3898 AddInstruction(floor_val);
3899 val = floor_val;
3900 break;
3901 }
3902 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
3903 case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
3904 break;
3905 case JSObject::FAST_ELEMENTS:
3906 case JSObject::FAST_DOUBLE_ELEMENTS:
3907 case JSObject::DICTIONARY_ELEMENTS:
3908 case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS:
3909 UNREACHABLE();
3910 break;
3911 }
3912 return new(zone()) HStoreKeyedSpecializedArrayElement(
3913 external_elements, checked_key, val, elements_kind);
3914 } else {
3915 return new(zone()) HLoadKeyedSpecializedArrayElement(
3916 external_elements, checked_key, elements_kind);
3917 }
3918}
3919
3920
3921HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
3922 HValue* key,
3923 HValue* val,
3924 Expression* expr,
3925 bool is_store) {
3926 ASSERT(expr->IsMonomorphic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003927 Handle<Map> map = expr->GetMonomorphicReceiverType();
rossberg@chromium.org717967f2011-07-20 13:44:42 +00003928 if (!map->has_fast_elements() &&
3929 !map->has_fast_double_elements() &&
3930 !map->has_external_array_elements()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003931 return is_store ? BuildStoreKeyedGeneric(object, key, val)
3932 : BuildLoadKeyedGeneric(object, key);
3933 }
3934 AddInstruction(new(zone()) HCheckNonSmi(object));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003935 HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
3936 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
3937 bool fast_double_elements = map->has_fast_double_elements();
3938 if (is_store && map->has_fast_elements()) {
3939 AddInstruction(new(zone()) HCheckMap(
3940 elements, isolate()->factory()->fixed_array_map()));
3941 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003942 HInstruction* length = NULL;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003943 HInstruction* checked_key = NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003944 if (map->has_external_array_elements()) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003945 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003946 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
3947 HLoadExternalArrayPointer* external_elements =
3948 new(zone()) HLoadExternalArrayPointer(elements);
3949 AddInstruction(external_elements);
3950 return BuildExternalArrayElementAccess(external_elements, checked_key,
3951 val, map->elements_kind(), is_store);
3952 }
rossberg@chromium.org717967f2011-07-20 13:44:42 +00003953 ASSERT(map->has_fast_elements() || fast_double_elements);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003954 if (map->instance_type() == JS_ARRAY_TYPE) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003955 length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003956 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00003957 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003958 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003959 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003960 if (is_store) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00003961 if (fast_double_elements) {
3962 return new(zone()) HStoreKeyedFastDoubleElement(elements,
3963 checked_key,
3964 val);
3965 } else {
3966 return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
3967 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003968 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00003969 if (fast_double_elements) {
3970 return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
3971 } else {
3972 return new(zone()) HLoadKeyedFastElement(elements, checked_key);
3973 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003974 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003975}
3976
3977
whesse@chromium.org7b260152011-06-20 15:33:18 +00003978HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
3979 HValue* key,
3980 HValue* val,
3981 Expression* prop,
3982 int ast_id,
3983 int position,
3984 bool is_store,
3985 bool* has_side_effects) {
3986 *has_side_effects = false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003987 AddInstruction(new(zone()) HCheckNonSmi(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003988 AddInstruction(HCheckInstanceType::NewIsSpecObject(object));
3989 ZoneMapList* maps = prop->GetReceiverTypes();
3990 bool todo_external_array = false;
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003991
whesse@chromium.org7b260152011-06-20 15:33:18 +00003992 static const int kNumElementTypes = JSObject::kElementsKindCount;
3993 bool type_todo[kNumElementTypes];
3994 for (int i = 0; i < kNumElementTypes; ++i) {
3995 type_todo[i] = false;
3996 }
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003997
whesse@chromium.org7b260152011-06-20 15:33:18 +00003998 for (int i = 0; i < maps->length(); ++i) {
3999 ASSERT(maps->at(i)->IsMap());
4000 type_todo[maps->at(i)->elements_kind()] = true;
4001 if (maps->at(i)->elements_kind()
4002 >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
4003 todo_external_array = true;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004004 }
4005 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004006
4007 HBasicBlock* join = graph()->CreateBasicBlock();
4008
4009 HInstruction* elements_kind_instr =
4010 AddInstruction(new(zone()) HElementsKind(object));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004011 HCompareConstantEqAndBranch* elements_kind_branch = NULL;
4012 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004013 HLoadExternalArrayPointer* external_elements = NULL;
4014 HInstruction* checked_key = NULL;
4015
4016 // FAST_ELEMENTS is assumed to be the first case.
4017 STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0);
4018
4019 for (JSObject::ElementsKind elements_kind = JSObject::FAST_ELEMENTS;
4020 elements_kind <= JSObject::LAST_ELEMENTS_KIND;
4021 elements_kind = JSObject::ElementsKind(elements_kind + 1)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004022 // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we
4023 // need to add some code that's executed for all external array cases.
4024 STATIC_ASSERT(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
4025 JSObject::LAST_ELEMENTS_KIND);
4026 if (elements_kind == JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
4027 && todo_external_array) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004028 HInstruction* length =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004029 AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004030 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
4031 external_elements = new(zone()) HLoadExternalArrayPointer(elements);
4032 AddInstruction(external_elements);
4033 }
4034 if (type_todo[elements_kind]) {
4035 HBasicBlock* if_true = graph()->CreateBasicBlock();
4036 HBasicBlock* if_false = graph()->CreateBasicBlock();
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004037 elements_kind_branch = new(zone()) HCompareConstantEqAndBranch(
4038 elements_kind_instr, elements_kind, Token::EQ_STRICT);
4039 elements_kind_branch->SetSuccessorAt(0, if_true);
4040 elements_kind_branch->SetSuccessorAt(1, if_false);
4041 current_block()->Finish(elements_kind_branch);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004042
4043 set_current_block(if_true);
4044 HInstruction* access;
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004045 if (elements_kind == JSObject::FAST_ELEMENTS ||
4046 elements_kind == JSObject::FAST_DOUBLE_ELEMENTS) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004047 bool fast_double_elements =
4048 elements_kind == JSObject::FAST_DOUBLE_ELEMENTS;
4049 if (is_store && elements_kind == JSObject::FAST_ELEMENTS) {
4050 AddInstruction(new(zone()) HCheckMap(
4051 elements, isolate()->factory()->fixed_array_map(),
4052 elements_kind_branch));
4053 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004054 HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
4055 HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004056 HHasInstanceTypeAndBranch* typecheck =
4057 new(zone()) HHasInstanceTypeAndBranch(object, JS_ARRAY_TYPE);
4058 typecheck->SetSuccessorAt(0, if_jsarray);
4059 typecheck->SetSuccessorAt(1, if_fastobject);
4060 current_block()->Finish(typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004061
4062 set_current_block(if_jsarray);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004063 HInstruction* length = new(zone()) HJSArrayLength(object, typecheck);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004064 AddInstruction(length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004065 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004066 if (is_store) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004067 if (fast_double_elements) {
4068 access = AddInstruction(
4069 new(zone()) HStoreKeyedFastDoubleElement(elements,
4070 checked_key,
4071 val));
4072 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004073 access = AddInstruction(
4074 new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
4075 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004076 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004077 if (fast_double_elements) {
4078 access = AddInstruction(
4079 new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
4080 } else {
4081 access = AddInstruction(
4082 new(zone()) HLoadKeyedFastElement(elements, checked_key));
4083 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004084 Push(access);
4085 }
4086 *has_side_effects |= access->HasSideEffects();
4087 if (position != -1) {
4088 access->set_position(position);
4089 }
4090 if_jsarray->Goto(join);
4091
4092 set_current_block(if_fastobject);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004093 length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004094 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
4095 if (is_store) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004096 if (fast_double_elements) {
4097 access = AddInstruction(
4098 new(zone()) HStoreKeyedFastDoubleElement(elements,
4099 checked_key,
4100 val));
4101 } else {
4102 access = AddInstruction(
4103 new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
4104 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004105 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00004106 if (fast_double_elements) {
4107 access = AddInstruction(
4108 new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
4109 } else {
4110 access = AddInstruction(
4111 new(zone()) HLoadKeyedFastElement(elements, checked_key));
4112 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004113 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004114 } else if (elements_kind == JSObject::DICTIONARY_ELEMENTS) {
4115 if (is_store) {
4116 access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
4117 } else {
4118 access = AddInstruction(BuildLoadKeyedGeneric(object, key));
4119 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00004120 } else { // External array elements.
4121 access = AddInstruction(BuildExternalArrayElementAccess(
4122 external_elements, checked_key, val, elements_kind, is_store));
4123 }
4124 *has_side_effects |= access->HasSideEffects();
4125 access->set_position(position);
4126 if (!is_store) {
4127 Push(access);
4128 }
4129 current_block()->Goto(join);
4130 set_current_block(if_false);
4131 }
4132 }
4133
4134 // Deopt if none of the cases matched.
4135 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
4136 join->SetJoinId(ast_id);
4137 set_current_block(join);
4138 return is_store ? NULL : Pop();
4139}
4140
4141
4142HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj,
4143 HValue* key,
4144 HValue* val,
4145 Expression* expr,
4146 int ast_id,
4147 int position,
4148 bool is_store,
4149 bool* has_side_effects) {
4150 ASSERT(!expr->IsPropertyName());
4151 HInstruction* instr = NULL;
4152 if (expr->IsMonomorphic()) {
4153 instr = BuildMonomorphicElementAccess(obj, key, val, expr, is_store);
4154 } else if (expr->GetReceiverTypes() != NULL &&
4155 !expr->GetReceiverTypes()->is_empty()) {
4156 return HandlePolymorphicElementAccess(
4157 obj, key, val, expr, ast_id, position, is_store, has_side_effects);
4158 } else {
4159 if (is_store) {
4160 instr = BuildStoreKeyedGeneric(obj, key, val);
4161 } else {
4162 instr = BuildLoadKeyedGeneric(obj, key);
4163 }
4164 }
4165 instr->set_position(position);
4166 AddInstruction(instr);
4167 *has_side_effects = instr->HasSideEffects();
4168 return instr;
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004169}
4170
4171
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004172HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
4173 HValue* key,
4174 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004175 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004176 return new(zone()) HStoreKeyedGeneric(
4177 context,
4178 object,
4179 key,
4180 value,
4181 function_strict_mode());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004182}
4183
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004184bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
4185 VariableProxy* proxy = expr->obj()->AsVariableProxy();
4186 if (proxy == NULL) return false;
4187 if (!proxy->var()->IsStackAllocated()) return false;
4188 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
4189 return false;
4190 }
4191
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00004192 // Our implementation of arguments (based on this stack frame or an
4193 // adapter below it) does not work for inlined functions.
4194 if (function_state()->outer() != NULL) {
4195 Bailout("arguments access in inlined function");
4196 return true;
4197 }
4198
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004199 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004200 if (expr->key()->IsPropertyName()) {
4201 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
4202 if (!name->IsEqualTo(CStrVector("length"))) return false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004203 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4204 result = new(zone()) HArgumentsLength(elements);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004205 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004206 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004207 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004208 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004209 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00004210 Drop(1); // Arguments object.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004211 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4212 HInstruction* length = AddInstruction(
4213 new(zone()) HArgumentsLength(elements));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004214 HInstruction* checked_key =
4215 AddInstruction(new(zone()) HBoundsCheck(key, length));
4216 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004217 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004218 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004219 return true;
4220}
4221
4222
4223void HGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004224 ASSERT(!HasStackOverflow());
4225 ASSERT(current_block() != NULL);
4226 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004227 expr->RecordTypeFeedback(oracle());
4228
4229 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004230
danno@chromium.org160a7b02011-04-18 15:51:38 +00004231 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004232
4233 HInstruction* instr = NULL;
4234 if (expr->IsArrayLength()) {
4235 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004236 AddInstruction(new(zone()) HCheckNonSmi(array));
ricow@chromium.org9fa09672011-07-25 11:05:35 +00004237 HInstruction* mapcheck =
4238 AddInstruction(HCheckInstanceType::NewIsJSArray(array));
4239 instr = new(zone()) HJSArrayLength(array, mapcheck);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004240
ager@chromium.org378b34e2011-01-28 08:04:38 +00004241 } else if (expr->IsStringLength()) {
4242 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004243 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004244 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004245 instr = new(zone()) HStringLength(string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004246 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004247 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004248 HValue* index = Pop();
4249 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004250 HValue* context = environment()->LookupContext();
4251 HStringCharCodeAt* char_code =
4252 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004253 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004254 instr = new(zone()) HStringCharFromCode(context, char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00004255
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004256 } else if (expr->IsFunctionPrototype()) {
4257 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004258 AddInstruction(new(zone()) HCheckNonSmi(function));
4259 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004260
4261 } else if (expr->key()->IsPropertyName()) {
4262 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
4263 ZoneMapList* types = expr->GetReceiverTypes();
4264
4265 HValue* obj = Pop();
4266 if (expr->IsMonomorphic()) {
4267 instr = BuildLoadNamed(obj, expr, types->first(), name);
4268 } else if (types != NULL && types->length() > 1) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004269 AddInstruction(new(zone()) HCheckNonSmi(obj));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004270 HValue* context = environment()->LookupContext();
4271 instr = new(zone()) HLoadNamedFieldPolymorphic(context, obj, types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004272 } else {
4273 instr = BuildLoadNamedGeneric(obj, expr);
4274 }
4275
4276 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004277 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004278
4279 HValue* key = Pop();
4280 HValue* obj = Pop();
whesse@chromium.org7b260152011-06-20 15:33:18 +00004281
4282 bool has_side_effects = false;
4283 HValue* load = HandleKeyedElementAccess(
4284 obj, key, NULL, expr, expr->id(), expr->position(),
4285 false, // is_store
4286 &has_side_effects);
4287 if (has_side_effects) {
4288 if (ast_context()->IsEffect()) {
4289 AddSimulate(expr->id());
4290 } else {
4291 Push(load);
4292 AddSimulate(expr->id());
4293 Drop(1);
4294 }
4295 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004296 return ast_context()->ReturnValue(load);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004297 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004298 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004299 return ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004300}
4301
4302
4303void HGraphBuilder::AddCheckConstantFunction(Call* expr,
4304 HValue* receiver,
4305 Handle<Map> receiver_map,
4306 bool smi_and_map_check) {
4307 // Constant functions have the nice property that the map will change if they
4308 // are overwritten. Therefore it is enough to check the map of the holder and
4309 // its prototypes.
4310 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004311 AddInstruction(new(zone()) HCheckNonSmi(receiver));
4312 AddInstruction(new(zone()) HCheckMap(receiver, receiver_map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004313 }
4314 if (!expr->holder().is_null()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004315 AddInstruction(new(zone()) HCheckPrototypeMaps(
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004316 Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
4317 expr->holder()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004318 }
4319}
4320
4321
4322void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
4323 HValue* receiver,
4324 ZoneMapList* types,
4325 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004326 // TODO(ager): We should recognize when the prototype chains for different
4327 // maps are identical. In that case we can avoid repeatedly generating the
4328 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004329 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
4330 int count = 0;
4331 HBasicBlock* join = NULL;
4332 for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004333 Handle<Map> map = types->at(i);
4334 if (expr->ComputeTarget(map, name)) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004335 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004336 // Only needed once.
4337 AddInstruction(new(zone()) HCheckNonSmi(receiver));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004338 join = graph()->CreateBasicBlock();
4339 }
4340 ++count;
4341 HBasicBlock* if_true = graph()->CreateBasicBlock();
4342 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004343 HCompareMap* compare =
4344 new(zone()) HCompareMap(receiver, map, if_true, if_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004345 current_block()->Finish(compare);
4346
4347 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004348 AddCheckConstantFunction(expr, receiver, map, false);
4349 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
4350 PrintF("Trying to inline the polymorphic call to %s\n",
4351 *name->ToCString());
4352 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00004353 if (FLAG_polymorphic_inlining && TryInline(expr)) {
4354 // Trying to inline will signal that we should bailout from the
4355 // entire compilation by setting stack overflow on the visitor.
4356 if (HasStackOverflow()) return;
4357 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004358 HCallConstantFunction* call =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004359 new(zone()) HCallConstantFunction(expr->target(), argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004360 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004361 PreProcessCall(call);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004362 AddInstruction(call);
4363 if (!ast_context()->IsEffect()) Push(call);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004364 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004365
4366 if (current_block() != NULL) current_block()->Goto(join);
4367 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004368 }
4369 }
4370
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004371 // Finish up. Unconditionally deoptimize if we've handled all the maps we
4372 // know about and do not want to handle ones we've never seen. Otherwise
4373 // use a generic IC.
4374 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004375 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004376 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004377 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004378 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004379 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004380 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00004381
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004382 if (join != NULL) {
4383 AddInstruction(call);
4384 if (!ast_context()->IsEffect()) Push(call);
4385 current_block()->Goto(join);
4386 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004387 return ast_context()->ReturnInstruction(call, expr->id());
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00004388 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004389 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004390
4391 // We assume that control flow is always live after an expression. So
4392 // even without predecessors to the join block, we set it as the exit
4393 // block and continue by adding instructions there.
4394 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004395 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004396 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004397 join->SetJoinId(expr->id());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004398 if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004399 } else {
4400 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004401 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004402}
4403
4404
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004405void HGraphBuilder::TraceInline(Handle<JSFunction> target,
4406 Handle<JSFunction> caller,
4407 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004408 if (FLAG_trace_inlining) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004409 SmartPointer<char> target_name = target->shared()->DebugName()->ToCString();
4410 SmartPointer<char> caller_name = caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004411 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004412 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004413 } else {
4414 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004415 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004416 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004417 }
4418}
4419
4420
4421bool HGraphBuilder::TryInline(Call* expr) {
4422 if (!FLAG_use_inlining) return false;
4423
danno@chromium.org40cb8782011-05-25 07:58:50 +00004424 // The function call we are inlining is a method call if the call
4425 // is a property call.
4426 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
4427 ? CALL_AS_FUNCTION
4428 : CALL_AS_METHOD;
4429
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004430 // Precondition: call is monomorphic and we have found a target with the
4431 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004432 Handle<JSFunction> caller = info()->closure();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004433 Handle<JSFunction> target = expr->target();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004434 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004435
4436 // Do a quick check on source code length to avoid parsing large
4437 // inlining candidates.
4438 if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004439 TraceInline(target, caller, "target text too big");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004440 return false;
4441 }
4442
4443 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004444 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004445 TraceInline(target, caller, "target not inlineable");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004446 return false;
4447 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004448
4449 // No context change required.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004450 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004451 if (target->context() != outer_info->closure()->context() ||
4452 outer_info->scope()->contains_with() ||
4453 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004454 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004455 return false;
4456 }
4457
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004458 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004459 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004460 int current_level = 1;
4461 while (env->outer() != NULL) {
4462 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004463 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004464 return false;
4465 }
4466 current_level++;
4467 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004468 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004469
4470 // Don't inline recursive functions.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004471 if (*target_shared == outer_info->closure()->shared()) {
4472 TraceInline(target, caller, "target is recursive");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004473 return false;
4474 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004475
4476 // We don't want to add more than a certain number of nodes from inlining.
4477 if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004478 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004479 return false;
4480 }
4481
4482 int count_before = AstNode::Count();
4483
4484 // Parse and allocate variables.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004485 CompilationInfo target_info(target);
4486 if (!ParserApi::Parse(&target_info) ||
4487 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004488 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004489 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004490 SetStackOverflow();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004491 target_shared->DisableOptimization(*target);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004492 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004493 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004494 return false;
4495 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004496
4497 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004498 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004499 return false;
4500 }
4501 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004502
4503 // Count the number of AST nodes added by inlining this call.
4504 int nodes_added = AstNode::Count() - count_before;
4505 if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004506 TraceInline(target, caller, "target AST is too large");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004507 return false;
4508 }
4509
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004510 // Don't inline functions that uses the arguments object or that
4511 // have a mismatching number of parameters.
4512 int arity = expr->arguments()->length();
4513 if (function->scope()->arguments() != NULL ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004514 arity != target_shared->formal_parameter_count()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004515 TraceInline(target, caller, "target requires special argument handling");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004516 return false;
4517 }
4518
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004519 // All declarations must be inlineable.
4520 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
4521 int decl_count = decls->length();
4522 for (int i = 0; i < decl_count; ++i) {
4523 if (!decls->at(i)->IsInlineable()) {
4524 TraceInline(target, caller, "target has non-trivial declaration");
4525 return false;
4526 }
4527 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004528 // All statements in the body must be inlineable.
4529 for (int i = 0, count = function->body()->length(); i < count; ++i) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004530 if (!function->body()->at(i)->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004531 TraceInline(target, caller, "target contains unsupported syntax");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004532 return false;
4533 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004534 }
4535
4536 // Generate the deoptimization data for the unoptimized version of
4537 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004538 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004539 // Note that we compile here using the same AST that we will use for
4540 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004541 target_info.EnableDeoptimizationSupport();
4542 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004543 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004544 return false;
4545 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004546 if (target_shared->scope_info() == SerializedScopeInfo::Empty()) {
4547 // The scope info might not have been set if a lazily compiled
4548 // function is inlined before being called for the first time.
4549 Handle<SerializedScopeInfo> target_scope_info =
4550 SerializedScopeInfo::Create(target_info.scope());
4551 target_shared->set_scope_info(*target_scope_info);
4552 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004553 target_shared->EnableDeoptimizationSupport(*target_info.code());
4554 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
4555 &target_info,
4556 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004557 }
4558
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004559 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004560 // After this point, we've made a decision to inline this function (so
4561 // TryInline should always return true).
4562
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004563 // Save the pending call context and type feedback oracle. Set up new ones
4564 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004565 ASSERT(target_shared->has_deoptimization_support());
4566 TypeFeedbackOracle target_oracle(
4567 Handle<Code>(target_shared->code()),
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004568 Handle<Context>(target->context()->global_context()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004569 FunctionState target_state(this, &target_info, &target_oracle);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004570
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004571 HConstant* undefined = graph()->GetConstantUndefined();
4572 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00004573 environment()->CopyForInlining(target,
4574 function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00004575 undefined,
4576 call_kind);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004577 HBasicBlock* body_entry = CreateBasicBlock(inner_env);
4578 current_block()->Goto(body_entry);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004579 body_entry->SetJoinId(expr->ReturnId());
4580 set_current_block(body_entry);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004581 AddInstruction(new(zone()) HEnterInlined(target,
4582 function,
4583 call_kind));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004584 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004585 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004586 if (HasStackOverflow()) {
4587 // Bail out if the inline function did, as we cannot residualize a call
4588 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004589 TraceInline(target, caller, "inline graph construction failed");
4590 target_shared->DisableOptimization(*target);
4591 inline_bailout_ = true;
danno@chromium.org160a7b02011-04-18 15:51:38 +00004592 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004593 }
4594
4595 // Update inlined nodes count.
4596 inlined_count_ += nodes_added;
4597
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004598 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004599
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004600 if (current_block() != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004601 // Add a return of undefined if control can fall off the body. In a
4602 // test context, undefined is false.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004603 if (inlined_test_context() == NULL) {
4604 ASSERT(function_return() != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004605 ASSERT(call_context()->IsEffect() || call_context()->IsValue());
4606 if (call_context()->IsEffect()) {
ager@chromium.org04921a82011-06-27 13:21:41 +00004607 current_block()->Goto(function_return());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004608 } else {
4609 current_block()->AddLeaveInlined(undefined, function_return());
4610 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004611 } else {
4612 // The graph builder assumes control can reach both branches of a
4613 // test, so we materialize the undefined value and test it rather than
4614 // simply jumping to the false target.
4615 //
4616 // TODO(3168478): refactor to avoid this.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004617 HBasicBlock* empty_true = graph()->CreateBasicBlock();
4618 HBasicBlock* empty_false = graph()->CreateBasicBlock();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004619 HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004620 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004621
ager@chromium.org04921a82011-06-27 13:21:41 +00004622 empty_true->Goto(inlined_test_context()->if_true());
4623 empty_false->Goto(inlined_test_context()->if_false());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004624 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004625 }
4626
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004627 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004628 if (inlined_test_context() != NULL) {
4629 HBasicBlock* if_true = inlined_test_context()->if_true();
4630 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004631
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004632 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004633 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004634 ClearInlinedTestContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004635
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004636 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004637 if (if_true->HasPredecessor()) {
4638 if_true->SetJoinId(expr->id());
4639 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
ager@chromium.org04921a82011-06-27 13:21:41 +00004640 if_true->Goto(true_target);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004641 }
4642 if (if_false->HasPredecessor()) {
4643 if_false->SetJoinId(expr->id());
4644 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
ager@chromium.org04921a82011-06-27 13:21:41 +00004645 if_false->Goto(false_target);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004646 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004647 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004648
danno@chromium.org160a7b02011-04-18 15:51:38 +00004649 } else if (function_return()->HasPredecessor()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004650 function_return()->SetJoinId(expr->id());
4651 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004652 } else {
4653 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004654 }
4655
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004656 return true;
4657}
4658
4659
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004660bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
4661 HValue* receiver,
4662 Handle<Map> receiver_map,
4663 CheckType check_type) {
4664 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004665 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004666 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004667 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004668 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
4669 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004670 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004671 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004672 if (argument_count == 2 && check_type == STRING_CHECK) {
4673 HValue* index = Pop();
4674 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004675 HValue* context = environment()->LookupContext();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004676 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004677 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004678 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
4679 expr->holder()));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004680 HStringCharCodeAt* char_code =
4681 BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004682 if (id == kStringCharCodeAt) {
4683 ast_context()->ReturnInstruction(char_code, expr->id());
4684 return true;
4685 }
4686 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004687 HStringCharFromCode* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004688 new(zone()) HStringCharFromCode(context, char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004689 ast_context()->ReturnInstruction(result, expr->id());
4690 return true;
4691 }
4692 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004693 case kMathRound:
4694 case kMathFloor:
4695 case kMathAbs:
4696 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004697 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00004698 case kMathSin:
4699 case kMathCos:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004700 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
4701 AddCheckConstantFunction(expr, receiver, receiver_map, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004702 HValue* argument = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004703 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004704 Drop(1); // Receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004705 HUnaryMathOperation* op =
4706 new(zone()) HUnaryMathOperation(context, argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004707 op->set_position(expr->position());
4708 ast_context()->ReturnInstruction(op, expr->id());
4709 return true;
4710 }
4711 break;
4712 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004713 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
4714 AddCheckConstantFunction(expr, receiver, receiver_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004715 HValue* right = Pop();
4716 HValue* left = Pop();
4717 Pop(); // Pop receiver.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004718 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004719 HInstruction* result = NULL;
4720 // Use sqrt() if exponent is 0.5 or -0.5.
4721 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
4722 double exponent = HConstant::cast(right)->DoubleValue();
4723 if (exponent == 0.5) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004724 result =
4725 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004726 } else if (exponent == -0.5) {
4727 HConstant* double_one =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004728 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
4729 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004730 AddInstruction(double_one);
4731 HUnaryMathOperation* square_root =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004732 new(zone()) HUnaryMathOperation(context, left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004733 AddInstruction(square_root);
4734 // MathPowHalf doesn't have side effects so there's no need for
4735 // an environment simulation here.
4736 ASSERT(!square_root->HasSideEffects());
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004737 result = new(zone()) HDiv(context, double_one, square_root);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004738 } else if (exponent == 2.0) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004739 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004740 }
4741 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004742 HConstant::cast(right)->HasInteger32Value() &&
4743 HConstant::cast(right)->Integer32Value() == 2) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00004744 result = new(zone()) HMul(context, left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004745 }
4746
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004747 if (result == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004748 result = new(zone()) HPower(left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004749 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004750 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004751 return true;
4752 }
4753 break;
4754 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004755 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004756 break;
4757 }
4758 return false;
4759}
4760
4761
4762bool HGraphBuilder::TryCallApply(Call* expr) {
4763 Expression* callee = expr->expression();
4764 Property* prop = callee->AsProperty();
4765 ASSERT(prop != NULL);
4766
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004767 if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
4768 return false;
4769 }
4770 Handle<Map> function_map = expr->GetReceiverTypes()->first();
4771 if (function_map->instance_type() != JS_FUNCTION_TYPE ||
4772 !expr->target()->shared()->HasBuiltinFunctionId() ||
4773 expr->target()->shared()->builtin_function_id() != kFunctionApply) {
4774 return false;
4775 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004776
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004777 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004778
4779 ZoneList<Expression*>* args = expr->arguments();
4780 if (args->length() != 2) return false;
4781
4782 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00004783 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004784 HValue* arg_two_value = environment()->Lookup(arg_two->var());
4785 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
4786
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00004787 // Our implementation of arguments (based on this stack frame or an
4788 // adapter below it) does not work for inlined functions.
4789 if (function_state()->outer() != NULL) {
4790 Bailout("Function.prototype.apply optimization in inlined function");
4791 return true;
4792 }
4793
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004794 // Found pattern f.apply(receiver, arguments).
4795 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004796 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004797 HValue* function = Pop();
4798 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00004799 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004800 HValue* receiver = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004801 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4802 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004803 AddCheckConstantFunction(expr, function, function_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004804 HInstruction* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004805 new(zone()) HApplyArguments(function, receiver, length, elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004806 result->set_position(expr->position());
4807 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004808 return true;
4809}
4810
4811
4812void HGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004813 ASSERT(!HasStackOverflow());
4814 ASSERT(current_block() != NULL);
4815 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004816 Expression* callee = expr->expression();
4817 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004818 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004819
4820 Property* prop = callee->AsProperty();
4821 if (prop != NULL) {
4822 if (!prop->key()->IsPropertyName()) {
4823 // Keyed function call.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004824 CHECK_ALIVE(VisitArgument(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004825
danno@chromium.org160a7b02011-04-18 15:51:38 +00004826 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004827 // Push receiver and key like the non-optimized code generator expects it.
4828 HValue* key = Pop();
4829 HValue* receiver = Pop();
4830 Push(key);
4831 Push(receiver);
4832
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004833 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004834
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004835 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004836 call = new(zone()) HCallKeyed(context, key, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004837 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004838 Drop(argument_count + 1); // 1 is the key.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004839 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004840 }
4841
4842 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00004843 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004844
4845 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004846
danno@chromium.org160a7b02011-04-18 15:51:38 +00004847 CHECK_ALIVE(VisitForValue(prop->obj()));
4848 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004849
4850 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
4851
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004852 ZoneMapList* types = expr->GetReceiverTypes();
4853
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004854 HValue* receiver =
4855 environment()->ExpressionStackAt(expr->arguments()->length());
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004856 if (expr->IsMonomorphic()) {
4857 Handle<Map> receiver_map =
4858 (types == NULL) ? Handle<Map>::null() : types->first();
4859 if (TryInlineBuiltinFunction(expr,
4860 receiver,
4861 receiver_map,
4862 expr->check_type())) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004863 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004864 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004865
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004866 if (CallStubCompiler::HasCustomCallGenerator(*expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004867 expr->check_type() != RECEIVER_MAP_CHECK) {
4868 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004869 // because it is likely to generate better code. Also use the IC
4870 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004871 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004872 call = PreProcessCall(
4873 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004874 } else {
4875 AddCheckConstantFunction(expr, receiver, receiver_map, true);
4876
danno@chromium.org160a7b02011-04-18 15:51:38 +00004877 if (TryInline(expr)) return;
4878 call = PreProcessCall(
4879 new(zone()) HCallConstantFunction(expr->target(),
4880 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004881 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004882 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004883 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004884 HandlePolymorphicCallNamed(expr, receiver, types, name);
4885 return;
4886
4887 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004888 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004889 call = PreProcessCall(
4890 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004891 }
4892
4893 } else {
4894 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
4895 bool global_call = (var != NULL) && var->is_global() && !var->is_this();
4896
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004897 if (global_call) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004898 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004899 // If there is a global property cell for the name at compile time and
4900 // access check is not enabled we assume that the function will not change
4901 // and generate optimized code for calling the function.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004902 LookupResult lookup;
4903 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
4904 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004905 !info()->global_object()->IsAccessCheckNeeded()) {
4906 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004907 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004908 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004909 if (known_global_function) {
4910 // Push the global object instead of the global receiver because
4911 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004912 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004913 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004914 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004915 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004916
danno@chromium.org160a7b02011-04-18 15:51:38 +00004917 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004918 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004919 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004920
4921 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004922 HGlobalReceiver* global_receiver =
4923 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004924 // Index of the receiver from the top of the expression stack.
4925 const int receiver_index = argument_count - 1;
4926 AddInstruction(global_receiver);
4927 ASSERT(environment()->ExpressionStackAt(receiver_index)->
4928 IsGlobalObject());
4929 environment()->SetExpressionStackAt(receiver_index, global_receiver);
4930
danno@chromium.org160a7b02011-04-18 15:51:38 +00004931 if (TryInline(expr)) return;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004932 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00004933 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004934 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004935 HValue* context = environment()->LookupContext();
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004936 HGlobalObject* receiver = new(zone()) HGlobalObject(context);
4937 AddInstruction(receiver);
4938 PushAndAdd(new(zone()) HPushArgument(receiver));
4939 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004940
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004941 call = new(zone()) HCallGlobal(context, var->name(), argument_count);
4942 Drop(argument_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004943 }
4944
4945 } else {
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004946 CHECK_ALIVE(VisitArgument(expr->expression()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004947 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004948 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004949 HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004950 AddInstruction(global_object);
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004951 AddInstruction(receiver);
4952 PushAndAdd(new(zone()) HPushArgument(receiver));
4953 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004954
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004955 // The function to call is treated as an argument to the call function
4956 // stub.
4957 call = new(zone()) HCallFunction(context, argument_count + 1);
4958 Drop(argument_count + 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004959 }
4960 }
4961
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004962 call->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004963 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004964}
4965
4966
4967void HGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004968 ASSERT(!HasStackOverflow());
4969 ASSERT(current_block() != NULL);
4970 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004971 // The constructor function is also used as the receiver argument to the
4972 // JS construct call builtin.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004973 HValue* constructor = NULL;
4974 CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
4975 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004976
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004977 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004978
4979 // The constructor is both an operand to the instruction and an argument
4980 // to the construct call.
4981 int arg_count = expr->arguments()->length() + 1; // Plus constructor.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004982 HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004983 call->set_position(expr->position());
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00004984 Drop(arg_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00004985 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004986}
4987
4988
4989// Support for generating inlined runtime functions.
4990
4991// Lookup table for generators for runtime calls that are generated inline.
4992// Elements of the table are member pointers to functions of HGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004993#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004994 &HGraphBuilder::Generate##Name,
4995
4996const HGraphBuilder::InlineFunctionGenerator
4997 HGraphBuilder::kInlineFunctionGenerators[] = {
4998 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
4999 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
5000};
5001#undef INLINE_FUNCTION_GENERATOR_ADDRESS
5002
5003
5004void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005005 ASSERT(!HasStackOverflow());
5006 ASSERT(current_block() != NULL);
5007 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005008 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005009 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005010 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005011
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005012 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005013 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005014 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005015 ASSERT(expr->name()->length() > 0);
5016 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005017 // Call to an inline function.
5018 int lookup_index = static_cast<int>(function->function_id) -
5019 static_cast<int>(Runtime::kFirstInlineFunction);
5020 ASSERT(lookup_index >= 0);
5021 ASSERT(static_cast<size_t>(lookup_index) <
5022 ARRAY_SIZE(kInlineFunctionGenerators));
5023 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
5024
5025 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005026 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005027 } else {
5028 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005029 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005030
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005031 HValue* context = environment()->LookupContext();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005032 Handle<String> name = expr->name();
5033 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005034 HCallRuntime* call =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005035 new(zone()) HCallRuntime(context, name, function, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005036 call->set_position(RelocInfo::kNoPosition);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005037 Drop(argument_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005038 return ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005039 }
5040}
5041
5042
5043void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005044 ASSERT(!HasStackOverflow());
5045 ASSERT(current_block() != NULL);
5046 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005047 switch (expr->op()) {
5048 case Token::DELETE: return VisitDelete(expr);
5049 case Token::VOID: return VisitVoid(expr);
5050 case Token::TYPEOF: return VisitTypeof(expr);
5051 case Token::ADD: return VisitAdd(expr);
5052 case Token::SUB: return VisitSub(expr);
5053 case Token::BIT_NOT: return VisitBitNot(expr);
5054 case Token::NOT: return VisitNot(expr);
5055 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005056 }
5057}
5058
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005059void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
5060 Property* prop = expr->expression()->AsProperty();
5061 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
5062 if (prop == NULL && var == NULL) {
5063 // Result of deleting non-property, non-variable reference is true.
5064 // Evaluate the subexpression for side effects.
5065 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005066 return ast_context()->ReturnValue(graph()->GetConstantTrue());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005067 } else if (var != NULL &&
5068 !var->is_global() &&
5069 var->AsSlot() != NULL &&
5070 var->AsSlot()->type() != Slot::LOOKUP) {
5071 // Result of deleting non-global, non-dynamic variables is false.
5072 // The subexpression does not have side effects.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005073 return ast_context()->ReturnValue(graph()->GetConstantFalse());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005074 } else if (prop != NULL) {
5075 if (prop->is_synthetic()) {
5076 // Result of deleting parameters is false, even when they rewrite
5077 // to accesses on the arguments object.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005078 return ast_context()->ReturnValue(graph()->GetConstantFalse());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005079 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005080 CHECK_ALIVE(VisitForValue(prop->obj()));
5081 CHECK_ALIVE(VisitForValue(prop->key()));
5082 HValue* key = Pop();
5083 HValue* obj = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005084 HValue* context = environment()->LookupContext();
5085 HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005086 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005087 }
5088 } else if (var->is_global()) {
5089 Bailout("delete with global variable");
5090 } else {
5091 Bailout("delete with non-global variable");
5092 }
5093}
5094
5095
5096void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
5097 CHECK_ALIVE(VisitForEffect(expr->expression()));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005098 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005099}
5100
5101
5102void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
5103 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
5104 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005105 HValue* context = environment()->LookupContext();
5106 HInstruction* instr = new(zone()) HTypeof(context, value);
5107 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005108}
5109
5110
5111void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
5112 CHECK_ALIVE(VisitForValue(expr->expression()));
5113 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005114 HValue* context = environment()->LookupContext();
5115 HInstruction* instr =
5116 new(zone()) HMul(context, value, graph_->GetConstant1());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005117 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005118}
5119
5120
5121void HGraphBuilder::VisitSub(UnaryOperation* expr) {
5122 CHECK_ALIVE(VisitForValue(expr->expression()));
5123 HValue* value = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005124 HValue* context = environment()->LookupContext();
5125 HInstruction* instr =
5126 new(zone()) HMul(context, value, graph_->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005127 TypeInfo info = oracle()->UnaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005128 if (info.IsUninitialized()) {
5129 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005130 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005131 info = TypeInfo::Unknown();
5132 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005133 Representation rep = ToRepresentation(info);
5134 TraceRepresentation(expr->op(), info, instr, rep);
5135 instr->AssumeRepresentation(rep);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005136 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005137}
5138
5139
5140void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
5141 CHECK_ALIVE(VisitForValue(expr->expression()));
5142 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005143 TypeInfo info = oracle()->UnaryType(expr);
5144 if (info.IsUninitialized()) {
5145 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005146 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005147 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005148 HInstruction* instr = new(zone()) HBitNot(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005149 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005150}
5151
5152
5153void HGraphBuilder::VisitNot(UnaryOperation* expr) {
5154 // TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
5155 if (ast_context()->IsTest()) {
5156 TestContext* context = TestContext::cast(ast_context());
5157 VisitForControl(expr->expression(),
5158 context->if_false(),
5159 context->if_true());
5160 return;
5161 }
5162
5163 if (ast_context()->IsEffect()) {
5164 VisitForEffect(expr->expression());
5165 return;
5166 }
5167
5168 ASSERT(ast_context()->IsValue());
5169 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
5170 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
5171 CHECK_BAILOUT(VisitForControl(expr->expression(),
5172 materialize_false,
5173 materialize_true));
5174
5175 if (materialize_false->HasPredecessor()) {
5176 materialize_false->SetJoinId(expr->expression()->id());
5177 set_current_block(materialize_false);
5178 Push(graph()->GetConstantFalse());
5179 } else {
5180 materialize_false = NULL;
5181 }
5182
5183 if (materialize_true->HasPredecessor()) {
5184 materialize_true->SetJoinId(expr->expression()->id());
5185 set_current_block(materialize_true);
5186 Push(graph()->GetConstantTrue());
5187 } else {
5188 materialize_true = NULL;
5189 }
5190
5191 HBasicBlock* join =
5192 CreateJoin(materialize_false, materialize_true, expr->id());
5193 set_current_block(join);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005194 if (join != NULL) return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005195}
5196
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005197
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005198HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00005199 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005200 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005201 TypeInfo info = oracle()->IncrementType(expr);
5202 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00005203 if (rep.IsTagged()) {
5204 rep = Representation::Integer32();
5205 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005206
5207 if (returns_original_input) {
5208 // We need an explicit HValue representing ToNumber(input). The
5209 // actual HChange instruction we need is (sometimes) added in a later
5210 // phase, so it is not available now to be used as an input to HAdd and
5211 // as the return value.
5212 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
5213 AddInstruction(number_input);
5214 Push(number_input);
5215 }
5216
5217 // The addition has no side effects, so we do not need
5218 // to simulate the expression stack after this instruction.
5219 // Any later failures deopt to the load of the input or earlier.
5220 HConstant* delta = (expr->op() == Token::INC)
5221 ? graph_->GetConstant1()
5222 : graph_->GetConstantMinus1();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005223 HValue* context = environment()->LookupContext();
5224 HInstruction* instr = new(zone()) HAdd(context, Top(), delta);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005225 TraceRepresentation(expr->op(), info, instr, rep);
5226 instr->AssumeRepresentation(rep);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005227 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005228 return instr;
5229}
5230
5231
5232void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005233 ASSERT(!HasStackOverflow());
5234 ASSERT(current_block() != NULL);
5235 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005236 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005237 VariableProxy* proxy = target->AsVariableProxy();
5238 Variable* var = proxy->AsVariable();
5239 Property* prop = target->AsProperty();
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005240 if (var == NULL && prop == NULL) {
5241 return Bailout("invalid lhs in count operation");
5242 }
5243
5244 // Match the full code generator stack by simulating an extra stack
5245 // element for postfix operations in a non-effect context. The return
5246 // value is ToNumber(input).
5247 bool returns_original_input =
5248 expr->is_postfix() && !ast_context()->IsEffect();
5249 HValue* input = NULL; // ToNumber(original_input).
5250 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005251
5252 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005253 if (var->mode() == Variable::CONST) {
5254 return Bailout("unsupported count operation with const");
5255 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005256 // Argument of the count operation is a variable, not a property.
5257 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005258 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005259
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005260 after = BuildIncrement(returns_original_input, expr);
5261 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005262 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005263
5264 if (var->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005265 HandleGlobalVariableAssignment(var,
5266 after,
5267 expr->position(),
5268 expr->AssignmentId());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00005269 } else if (var->IsStackAllocated()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005270 Bind(var, after);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00005271 } else if (var->IsContextSlot()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00005272 // Bail out if we try to mutate a parameter value in a function using
5273 // the arguments object. We do not (yet) correctly handle the
5274 // arguments property of the function.
5275 if (info()->scope()->arguments() != NULL) {
5276 // Parameters will rewrite to context slots. We have no direct way
5277 // to detect that the variable is a parameter.
5278 int count = info()->scope()->num_parameters();
5279 for (int i = 0; i < count; ++i) {
5280 if (var == info()->scope()->parameter(i)) {
5281 Bailout("assignment to parameter, function uses arguments object");
5282 }
5283 }
5284 }
5285
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00005286 HValue* context = BuildContextChainWalk(var);
5287 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005288 HStoreContextSlot* instr =
5289 new(zone()) HStoreContextSlot(context, index, after);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00005290 AddInstruction(instr);
5291 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
5292 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005293 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005294 }
5295
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005296 } else {
5297 // Argument of the count operation is a property.
5298 ASSERT(prop != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005299 prop->RecordTypeFeedback(oracle());
5300
5301 if (prop->key()->IsPropertyName()) {
5302 // Named property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005303 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005304
danno@chromium.org160a7b02011-04-18 15:51:38 +00005305 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005306 HValue* obj = Top();
5307
5308 HInstruction* load = NULL;
5309 if (prop->IsMonomorphic()) {
5310 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
5311 Handle<Map> map = prop->GetReceiverTypes()->first();
5312 load = BuildLoadNamed(obj, prop, map, name);
5313 } else {
5314 load = BuildLoadNamedGeneric(obj, prop);
5315 }
5316 PushAndAdd(load);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005317 if (load->HasSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005318
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005319 after = BuildIncrement(returns_original_input, expr);
5320 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005321
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005322 HInstruction* store = BuildStoreNamed(obj, after, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005323 AddInstruction(store);
5324
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005325 // Overwrite the receiver in the bailout environment with the result
5326 // of the operation, and the placeholder with the original value if
5327 // necessary.
5328 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005329 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005330 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005331
5332 } else {
5333 // Keyed property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005334 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005335
danno@chromium.org160a7b02011-04-18 15:51:38 +00005336 CHECK_ALIVE(VisitForValue(prop->obj()));
5337 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005338 HValue* obj = environment()->ExpressionStackAt(1);
5339 HValue* key = environment()->ExpressionStackAt(0);
5340
whesse@chromium.org7b260152011-06-20 15:33:18 +00005341 bool has_side_effects = false;
5342 HValue* load = HandleKeyedElementAccess(
5343 obj, key, NULL, prop, expr->CountId(), RelocInfo::kNoPosition,
5344 false, // is_store
5345 &has_side_effects);
5346 Push(load);
5347 if (has_side_effects) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005348
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005349 after = BuildIncrement(returns_original_input, expr);
5350 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005351
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005352 expr->RecordTypeFeedback(oracle());
whesse@chromium.org7b260152011-06-20 15:33:18 +00005353 HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
5354 RelocInfo::kNoPosition,
5355 true, // is_store
5356 &has_side_effects);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005357
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005358 // Drop the key from the bailout environment. Overwrite the receiver
5359 // with the result of the operation, and the placeholder with the
5360 // original value if necessary.
5361 Drop(1);
5362 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005363 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005364 ASSERT(has_side_effects); // Stores always have side effects.
5365 AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005366 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005367 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005368
5369 Drop(returns_original_input ? 2 : 1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005370 return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005371}
5372
5373
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005374HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context,
5375 HValue* string,
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005376 HValue* index) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005377 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005378 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005379 HStringLength* length = new(zone()) HStringLength(string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005380 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005381 HInstruction* checked_index =
5382 AddInstruction(new(zone()) HBoundsCheck(index, length));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005383 return new(zone()) HStringCharCodeAt(context, string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005384}
5385
5386
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005387HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
5388 HValue* left,
5389 HValue* right) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005390 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005391 TypeInfo info = oracle()->BinaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005392 if (info.IsUninitialized()) {
5393 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005394 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005395 info = TypeInfo::Unknown();
5396 }
5397 HInstruction* instr = NULL;
5398 switch (expr->op()) {
5399 case Token::ADD:
5400 if (info.IsString()) {
5401 AddInstruction(new(zone()) HCheckNonSmi(left));
5402 AddInstruction(HCheckInstanceType::NewIsString(left));
5403 AddInstruction(new(zone()) HCheckNonSmi(right));
5404 AddInstruction(HCheckInstanceType::NewIsString(right));
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005405 instr = new(zone()) HStringAdd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005406 } else {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005407 instr = new(zone()) HAdd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005408 }
5409 break;
5410 case Token::SUB:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005411 instr = new(zone()) HSub(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005412 break;
5413 case Token::MUL:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005414 instr = new(zone()) HMul(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005415 break;
5416 case Token::MOD:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005417 instr = new(zone()) HMod(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005418 break;
5419 case Token::DIV:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005420 instr = new(zone()) HDiv(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005421 break;
5422 case Token::BIT_XOR:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005423 instr = new(zone()) HBitXor(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005424 break;
5425 case Token::BIT_AND:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005426 instr = new(zone()) HBitAnd(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005427 break;
5428 case Token::BIT_OR:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005429 instr = new(zone()) HBitOr(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005430 break;
5431 case Token::SAR:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005432 instr = new(zone()) HSar(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005433 break;
5434 case Token::SHR:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005435 instr = new(zone()) HShr(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005436 break;
5437 case Token::SHL:
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005438 instr = new(zone()) HShl(context, left, right);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005439 break;
5440 default:
5441 UNREACHABLE();
5442 }
5443
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005444 // If we hit an uninitialized binary op stub we will get type info
5445 // for a smi operation. If one of the operands is a constant string
5446 // do not generate code assuming it is a smi operation.
5447 if (info.IsSmi() &&
5448 ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) ||
5449 (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
5450 return instr;
5451 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005452 Representation rep = ToRepresentation(info);
5453 // We only generate either int32 or generic tagged bitwise operations.
5454 if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) {
5455 rep = Representation::Integer32();
5456 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005457 TraceRepresentation(expr->op(), info, instr, rep);
5458 instr->AssumeRepresentation(rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005459 return instr;
5460}
5461
5462
5463// Check for the form (%_ClassOf(foo) === 'BarClass').
5464static bool IsClassOfTest(CompareOperation* expr) {
5465 if (expr->op() != Token::EQ_STRICT) return false;
5466 CallRuntime* call = expr->left()->AsCallRuntime();
5467 if (call == NULL) return false;
5468 Literal* literal = expr->right()->AsLiteral();
5469 if (literal == NULL) return false;
5470 if (!literal->handle()->IsString()) return false;
5471 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
5472 ASSERT(call->arguments()->length() == 1);
5473 return true;
5474}
5475
5476
5477void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005478 ASSERT(!HasStackOverflow());
5479 ASSERT(current_block() != NULL);
5480 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005481 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005482 case Token::COMMA:
5483 return VisitComma(expr);
5484 case Token::OR:
5485 case Token::AND:
5486 return VisitLogicalExpression(expr);
5487 default:
5488 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005489 }
5490}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005491
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005492
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005493void HGraphBuilder::VisitComma(BinaryOperation* expr) {
5494 CHECK_ALIVE(VisitForEffect(expr->left()));
5495 // Visit the right subexpression in the same AST context as the entire
5496 // expression.
5497 Visit(expr->right());
5498}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005499
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005500
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005501void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
5502 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005503 if (ast_context()->IsTest()) {
5504 TestContext* context = TestContext::cast(ast_context());
5505 // Translate left subexpression.
5506 HBasicBlock* eval_right = graph()->CreateBasicBlock();
5507 if (is_logical_and) {
5508 CHECK_BAILOUT(VisitForControl(expr->left(),
5509 eval_right,
5510 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005511 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005512 CHECK_BAILOUT(VisitForControl(expr->left(),
5513 context->if_true(),
5514 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005515 }
5516
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005517 // Translate right subexpression by visiting it in the same AST
5518 // context as the entire expression.
5519 if (eval_right->HasPredecessor()) {
5520 eval_right->SetJoinId(expr->RightId());
5521 set_current_block(eval_right);
5522 Visit(expr->right());
5523 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005524
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005525 } else if (ast_context()->IsValue()) {
5526 CHECK_ALIVE(VisitForValue(expr->left()));
5527 ASSERT(current_block() != NULL);
5528
5529 // We need an extra block to maintain edge-split form.
5530 HBasicBlock* empty_block = graph()->CreateBasicBlock();
5531 HBasicBlock* eval_right = graph()->CreateBasicBlock();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00005532 unsigned test_id = expr->left()->test_id();
5533 ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005534 HBranch* test = is_logical_and
ricow@chromium.org2c99e282011-07-28 09:15:17 +00005535 ? new(zone()) HBranch(Top(), eval_right, empty_block, expected)
5536 : new(zone()) HBranch(Top(), empty_block, eval_right, expected);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005537 current_block()->Finish(test);
5538
5539 set_current_block(eval_right);
5540 Drop(1); // Value of the left subexpression.
5541 CHECK_BAILOUT(VisitForValue(expr->right()));
5542
5543 HBasicBlock* join_block =
5544 CreateJoin(empty_block, current_block(), expr->id());
5545 set_current_block(join_block);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005546 return ast_context()->ReturnValue(Pop());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005547
5548 } else {
5549 ASSERT(ast_context()->IsEffect());
5550 // In an effect context, we don't need the value of the left subexpression,
5551 // only its control flow and side effects. We need an extra block to
5552 // maintain edge-split form.
5553 HBasicBlock* empty_block = graph()->CreateBasicBlock();
5554 HBasicBlock* right_block = graph()->CreateBasicBlock();
5555 if (is_logical_and) {
5556 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
5557 } else {
5558 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
5559 }
5560
5561 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
5562 // actually two empty blocks (one here and one inserted by
5563 // TestContext::BuildBranch, and that they both have an HSimulate though the
5564 // second one is not a merge node, and that we really have no good AST ID to
5565 // put on that first HSimulate.
5566
5567 if (empty_block->HasPredecessor()) {
5568 empty_block->SetJoinId(expr->id());
5569 } else {
5570 empty_block = NULL;
5571 }
5572
5573 if (right_block->HasPredecessor()) {
5574 right_block->SetJoinId(expr->RightId());
5575 set_current_block(right_block);
5576 CHECK_BAILOUT(VisitForEffect(expr->right()));
5577 right_block = current_block();
5578 } else {
5579 right_block = NULL;
5580 }
5581
5582 HBasicBlock* join_block =
5583 CreateJoin(empty_block, right_block, expr->id());
5584 set_current_block(join_block);
5585 // We did not materialize any value in the predecessor environments,
5586 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005587 }
5588}
5589
5590
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005591void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005592 CHECK_ALIVE(VisitForValue(expr->left()));
5593 CHECK_ALIVE(VisitForValue(expr->right()));
5594 HValue* right = Pop();
5595 HValue* left = Pop();
5596 HInstruction* instr = BuildBinaryOperation(expr, left, right);
5597 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005598 return ast_context()->ReturnInstruction(instr, expr->id());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005599}
5600
5601
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005602void HGraphBuilder::TraceRepresentation(Token::Value op,
5603 TypeInfo info,
5604 HValue* value,
5605 Representation rep) {
5606 if (!FLAG_trace_representation) return;
5607 // TODO(svenpanne) Under which circumstances are we actually not flexible?
5608 // At first glance, this looks a bit weird...
5609 bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
5610 PrintF("Operation %s has type info %s, %schange representation assumption "
5611 "for %s (ID %d) from %s to %s\n",
5612 Token::Name(op),
5613 info.ToString(),
5614 flexible ? "" : " DO NOT ",
5615 value->Mnemonic(),
5616 graph_->GetMaximumValueID(),
5617 value->representation().Mnemonic(),
5618 rep.Mnemonic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005619}
5620
5621
5622Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
5623 if (info.IsSmi()) return Representation::Integer32();
5624 if (info.IsInteger32()) return Representation::Integer32();
5625 if (info.IsDouble()) return Representation::Double();
5626 if (info.IsNumber()) return Representation::Double();
5627 return Representation::Tagged();
5628}
5629
5630
ager@chromium.org04921a82011-06-27 13:21:41 +00005631void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr,
5632 Expression* expr,
5633 Handle<String> check) {
5634 CHECK_ALIVE(VisitForTypeOf(expr));
5635 HValue* expr_value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005636 HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(expr_value, check);
ager@chromium.org04921a82011-06-27 13:21:41 +00005637 instr->set_position(compare_expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005638 return ast_context()->ReturnControl(instr, compare_expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00005639}
5640
5641
5642void HGraphBuilder::HandleLiteralCompareUndefined(
5643 CompareOperation* compare_expr, Expression* expr) {
5644 CHECK_ALIVE(VisitForValue(expr));
5645 HValue* lhs = Pop();
5646 HValue* rhs = graph()->GetConstantUndefined();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005647 HCompareObjectEqAndBranch* instr =
5648 new(zone()) HCompareObjectEqAndBranch(lhs, rhs);
ager@chromium.org04921a82011-06-27 13:21:41 +00005649 instr->set_position(compare_expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005650 return ast_context()->ReturnControl(instr, compare_expr->id());
ager@chromium.org04921a82011-06-27 13:21:41 +00005651}
5652
5653
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005654void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005655 ASSERT(!HasStackOverflow());
5656 ASSERT(current_block() != NULL);
5657 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005658 if (IsClassOfTest(expr)) {
5659 CallRuntime* call = expr->left()->AsCallRuntime();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005660 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005661 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005662 HValue* value = Pop();
5663 Literal* literal = expr->right()->AsLiteral();
5664 Handle<String> rhs = Handle<String>::cast(literal->handle());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005665 HClassOfTestAndBranch* instr =
5666 new(zone()) HClassOfTestAndBranch(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005667 instr->set_position(expr->position());
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005668 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005669 }
5670
ager@chromium.org04921a82011-06-27 13:21:41 +00005671 // Check for special cases that compare against literals.
5672 Expression *sub_expr;
5673 Handle<String> check;
5674 if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
5675 HandleLiteralCompareTypeof(expr, sub_expr, check);
5676 return;
5677 }
5678
5679 if (expr->IsLiteralCompareUndefined(&sub_expr)) {
5680 HandleLiteralCompareUndefined(expr, sub_expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005681 return;
5682 }
5683
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005684 TypeInfo type_info = oracle()->CompareType(expr);
5685 // Check if this expression was ever executed according to type feedback.
5686 if (type_info.IsUninitialized()) {
5687 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005688 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005689 type_info = TypeInfo::Unknown();
5690 }
5691
danno@chromium.org160a7b02011-04-18 15:51:38 +00005692 CHECK_ALIVE(VisitForValue(expr->left()));
5693 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005694
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005695 HValue* context = environment()->LookupContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005696 HValue* right = Pop();
5697 HValue* left = Pop();
5698 Token::Value op = expr->op();
5699
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005700 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005701 // Check to see if the rhs of the instanceof is a global function not
5702 // residing in new space. If it is we assume that the function will stay the
5703 // same.
5704 Handle<JSFunction> target = Handle<JSFunction>::null();
5705 Variable* var = expr->right()->AsVariableProxy()->AsVariable();
5706 bool global_function = (var != NULL) && var->is_global() && !var->is_this();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005707 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005708 info()->has_global_object() &&
5709 !info()->global_object()->IsAccessCheckNeeded()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005710 Handle<String> name = var->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005711 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005712 LookupResult lookup;
5713 global->Lookup(*name, &lookup);
5714 if (lookup.IsProperty() &&
5715 lookup.type() == NORMAL &&
5716 lookup.GetValue()->IsJSFunction()) {
5717 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
5718 // If the function is in new space we assume it's more likely to
5719 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005720 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005721 target = candidate;
5722 }
5723 }
5724 }
5725
5726 // If the target is not null we have found a known global function that is
5727 // assumed to stay the same for this instanceof.
5728 if (target.is_null()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005729 HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
5730 result->set_position(expr->position());
5731 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005732 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005733 AddInstruction(new(zone()) HCheckFunction(right, target));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005734 HInstanceOfKnownGlobal* result =
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005735 new(zone()) HInstanceOfKnownGlobal(context, left, target);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005736 result->set_position(expr->position());
5737 return ast_context()->ReturnInstruction(result, expr->id());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005738 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005739 } else if (op == Token::IN) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005740 HIn* result = new(zone()) HIn(context, left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005741 result->set_position(expr->position());
5742 return ast_context()->ReturnInstruction(result, expr->id());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005743 } else if (type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005744 switch (op) {
5745 case Token::EQ:
5746 case Token::EQ_STRICT: {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005747 AddInstruction(new(zone()) HCheckNonSmi(left));
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005748 AddInstruction(HCheckInstanceType::NewIsSpecObject(left));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005749 AddInstruction(new(zone()) HCheckNonSmi(right));
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005750 AddInstruction(HCheckInstanceType::NewIsSpecObject(right));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005751 HCompareObjectEqAndBranch* result =
5752 new(zone()) HCompareObjectEqAndBranch(left, right);
5753 result->set_position(expr->position());
5754 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005755 }
5756 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00005757 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005758 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005759 } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
5760 (op == Token::EQ || op == Token::EQ_STRICT)) {
lrn@chromium.orgac2828d2011-06-23 06:29:21 +00005761 AddInstruction(new(zone()) HCheckNonSmi(left));
5762 AddInstruction(HCheckInstanceType::NewIsSymbol(left));
5763 AddInstruction(new(zone()) HCheckNonSmi(right));
5764 AddInstruction(HCheckInstanceType::NewIsSymbol(right));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005765 HCompareObjectEqAndBranch* result =
5766 new(zone()) HCompareObjectEqAndBranch(left, right);
5767 result->set_position(expr->position());
5768 return ast_context()->ReturnControl(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005769 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005770 Representation r = ToRepresentation(type_info);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005771 if (r.IsTagged()) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005772 HCompareGeneric* result =
5773 new(zone()) HCompareGeneric(context, left, right, op);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005774 result->set_position(expr->position());
5775 return ast_context()->ReturnInstruction(result, expr->id());
5776 } else {
5777 HCompareIDAndBranch* result =
5778 new(zone()) HCompareIDAndBranch(left, right, op);
5779 result->set_position(expr->position());
5780 result->SetInputRepresentation(r);
5781 return ast_context()->ReturnControl(result, expr->id());
5782 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005783 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005784}
5785
5786
5787void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005788 ASSERT(!HasStackOverflow());
5789 ASSERT(current_block() != NULL);
5790 ASSERT(current_block()->HasPredecessor());
5791 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005792 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005793 HIsNullAndBranch* instr =
5794 new(zone()) HIsNullAndBranch(value, expr->is_strict());
5795 return ast_context()->ReturnControl(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005796}
5797
5798
5799void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005800 ASSERT(!HasStackOverflow());
5801 ASSERT(current_block() != NULL);
5802 ASSERT(current_block()->HasPredecessor());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005803 HThisFunction* self = new(zone()) HThisFunction;
5804 return ast_context()->ReturnInstruction(self, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005805}
5806
5807
5808void HGraphBuilder::VisitDeclaration(Declaration* decl) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005809 // We support only declarations that do not require code generation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005810 Variable* var = decl->proxy()->var();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005811 if (!var->IsStackAllocated() || decl->fun() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005812 return Bailout("unsupported declaration");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005813 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005814
5815 if (decl->mode() == Variable::CONST) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005816 ASSERT(var->IsStackAllocated());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005817 environment()->Bind(var, graph()->GetConstantHole());
5818 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005819}
5820
5821
5822// Generators for inline runtime functions.
5823// Support for types.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005824void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
5825 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005826 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005827 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005828 HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
5829 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005830}
5831
5832
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005833void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
5834 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005835 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005836 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005837 HHasInstanceTypeAndBranch* result =
5838 new(zone()) HHasInstanceTypeAndBranch(value,
5839 FIRST_SPEC_OBJECT_TYPE,
5840 LAST_SPEC_OBJECT_TYPE);
5841 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005842}
5843
5844
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005845void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
5846 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005847 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005848 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005849 HHasInstanceTypeAndBranch* result =
5850 new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
5851 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005852}
5853
5854
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005855void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
5856 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005857 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005858 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005859 HHasCachedArrayIndexAndBranch* result =
5860 new(zone()) HHasCachedArrayIndexAndBranch(value);
5861 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005862}
5863
5864
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005865void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
5866 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005867 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005868 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005869 HHasInstanceTypeAndBranch* result =
5870 new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
5871 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005872}
5873
5874
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005875void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
5876 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005877 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005878 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005879 HHasInstanceTypeAndBranch* result =
5880 new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
5881 return ast_context()->ReturnControl(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005882}
5883
5884
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005885void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
5886 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005887 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005888 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005889 HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
5890 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005891}
5892
5893
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005894void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005895 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005896}
5897
5898
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005899void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005900 ASSERT(call->arguments()->length() == 1);
5901 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5902 HValue* value = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005903 HIsUndetectableAndBranch* result =
5904 new(zone()) HIsUndetectableAndBranch(value);
5905 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005906}
5907
5908
5909void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005910 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005911 return Bailout(
5912 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005913}
5914
5915
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00005916// Support for construct call checks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005917void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
5918 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005919 if (function_state()->outer() != NULL) {
5920 // We are generating graph for inlined function. Currently
5921 // constructor inlining is not supported and we can just return
5922 // false from %_IsConstructCall().
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005923 return ast_context()->ReturnValue(graph()->GetConstantFalse());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005924 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005925 return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
5926 call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005927 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005928}
5929
5930
5931// Support for arguments.length and arguments[?].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005932void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00005933 // Our implementation of arguments (based on this stack frame or an
5934 // adapter below it) does not work for inlined functions. This runtime
5935 // function is blacklisted by AstNode::IsInlineable.
5936 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005937 ASSERT(call->arguments()->length() == 0);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005938 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
5939 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005940 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005941}
5942
5943
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005944void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00005945 // Our implementation of arguments (based on this stack frame or an
5946 // adapter below it) does not work for inlined functions. This runtime
5947 // function is blacklisted by AstNode::IsInlineable.
5948 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005949 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005950 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005951 HValue* index = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005952 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
5953 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
5954 HAccessArgumentsAt* result =
5955 new(zone()) HAccessArgumentsAt(elements, length, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005956 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005957}
5958
5959
5960// Support for accessing the class and value fields of an object.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005961void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005962 // The special form detected by IsClassOfTest is detected before we get here
5963 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005964 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005965}
5966
5967
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005968void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
5969 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005970 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005971 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005972 HValueOf* result = new(zone()) HValueOf(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005973 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005974}
5975
5976
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005977void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005978 return Bailout("inlined runtime function: SetValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005979}
5980
5981
5982// Fast support for charCodeAt(n).
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005983void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
5984 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005985 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5986 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005987 HValue* index = Pop();
5988 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00005989 HValue* context = environment()->LookupContext();
5990 HStringCharCodeAt* result = BuildStringCharCodeAt(context, string, index);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00005991 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005992}
5993
5994
5995// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005996void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005997 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005998 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005999 HValue* char_code = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006000 HValue* context = environment()->LookupContext();
6001 HStringCharFromCode* result =
6002 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006003 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006004}
6005
6006
6007// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006008void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006009 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006010 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6011 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006012 HValue* index = Pop();
6013 HValue* string = Pop();
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006014 HValue* context = environment()->LookupContext();
6015 HStringCharCodeAt* char_code = BuildStringCharCodeAt(context, string, index);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006016 AddInstruction(char_code);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00006017 HStringCharFromCode* result =
6018 new(zone()) HStringCharFromCode(context, char_code);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006019 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006020}
6021
6022
6023// Fast support for object equality testing.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006024void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
6025 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006026 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6027 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006028 HValue* right = Pop();
6029 HValue* left = Pop();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006030 HCompareObjectEqAndBranch* result =
6031 new(zone()) HCompareObjectEqAndBranch(left, right);
6032 return ast_context()->ReturnControl(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006033}
6034
6035
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006036void HGraphBuilder::GenerateLog(CallRuntime* call) {
6037 // %_Log is ignored in optimized code.
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006038 return ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006039}
6040
6041
6042// Fast support for Math.random().
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006043void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006044 return Bailout("inlined runtime function: RandomHeapNumber");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006045}
6046
6047
6048// Fast support for StringAdd.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006049void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
6050 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006051 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006052 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006053 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006054 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006055 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006056}
6057
6058
6059// Fast support for SubString.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006060void HGraphBuilder::GenerateSubString(CallRuntime* call) {
6061 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006062 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006063 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006064 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006065 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006066 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006067}
6068
6069
6070// Fast support for StringCompare.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006071void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
6072 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006073 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006074 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006075 HCallStub* result =
6076 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006077 Drop(2);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006078 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006079}
6080
6081
6082// Support for direct calls from JavaScript to native RegExp code.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006083void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
6084 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006085 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006086 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006087 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006088 Drop(4);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006089 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006090}
6091
6092
6093// Construct a RegExp exec result with two in-object properties.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006094void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
6095 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006096 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006097 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006098 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006099 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006100 Drop(3);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006101 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006102}
6103
6104
6105// Support for fast native caches.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006106void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006107 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006108}
6109
6110
6111// Fast support for number to string.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006112void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
6113 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006114 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006115 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006116 HCallStub* result =
6117 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006118 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006119 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006120}
6121
6122
6123// Fast swapping of elements. Takes three expressions, the object and two
6124// indices. This should only be used if the indices are known to be
6125// non-negative and within bounds of the elements array at the call site.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006126void HGraphBuilder::GenerateSwapElements(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006127 return Bailout("inlined runtime function: SwapElements");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006128}
6129
6130
6131// Fast call for custom callbacks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006132void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006133 // 1 ~ The function to call is not itself an argument to the call.
6134 int arg_count = call->arguments()->length() - 1;
6135 ASSERT(arg_count >= 1); // There's always at least a receiver.
6136
6137 for (int i = 0; i < arg_count; ++i) {
6138 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
6139 }
6140 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
6141 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006142 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00006143 HInvokeFunction* result =
6144 new(zone()) HInvokeFunction(context, function, arg_count);
6145 Drop(arg_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006146 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006147}
6148
6149
6150// Fast call to math functions.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006151void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
6152 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006153 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
6154 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006155 HValue* right = Pop();
6156 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006157 HPower* result = new(zone()) HPower(left, right);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006158 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006159}
6160
6161
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006162void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
6163 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006164 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006165 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006166 HCallStub* result =
6167 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006168 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006169 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006170 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006171}
6172
6173
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006174void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
6175 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006176 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006177 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006178 HCallStub* result =
6179 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006180 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006181 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006182 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006183}
6184
6185
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006186void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
6187 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00006188 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006189 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006190 HCallStub* result =
6191 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00006192 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006193 Drop(1);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006194 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006195}
6196
6197
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006198void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006199 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006200}
6201
6202
6203// Check whether two RegExps are equivalent
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006204void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006205 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006206}
6207
6208
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006209void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
6210 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00006211 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00006212 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006213 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006214 return ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006215}
6216
6217
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006218void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00006219 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006220}
6221
6222
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006223void HGraphBuilder::GenerateIsNativeOrStrictMode(CallRuntime* call) {
6224 return Bailout("inlined runtime function: IsNativeOrStrictMode");
6225}
6226
6227
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006228#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +00006229#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006230
6231
6232HEnvironment::HEnvironment(HEnvironment* outer,
6233 Scope* scope,
6234 Handle<JSFunction> closure)
6235 : closure_(closure),
6236 values_(0),
6237 assigned_variables_(4),
6238 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006239 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006240 local_count_(0),
6241 outer_(outer),
6242 pop_count_(0),
6243 push_count_(0),
6244 ast_id_(AstNode::kNoNumber) {
6245 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
6246}
6247
6248
6249HEnvironment::HEnvironment(const HEnvironment* other)
6250 : values_(0),
6251 assigned_variables_(0),
6252 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006253 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006254 local_count_(0),
6255 outer_(NULL),
6256 pop_count_(0),
6257 push_count_(0),
6258 ast_id_(other->ast_id()) {
6259 Initialize(other);
6260}
6261
6262
6263void HEnvironment::Initialize(int parameter_count,
6264 int local_count,
6265 int stack_height) {
6266 parameter_count_ = parameter_count;
6267 local_count_ = local_count;
6268
6269 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006270 int total = parameter_count + specials_count_ + local_count + stack_height;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006271 values_.Initialize(total + 4);
6272 for (int i = 0; i < total; ++i) values_.Add(NULL);
6273}
6274
6275
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006276void HEnvironment::Initialize(const HEnvironment* other) {
6277 closure_ = other->closure();
6278 values_.AddAll(other->values_);
6279 assigned_variables_.AddAll(other->assigned_variables_);
6280 parameter_count_ = other->parameter_count_;
6281 local_count_ = other->local_count_;
6282 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
6283 pop_count_ = other->pop_count_;
6284 push_count_ = other->push_count_;
6285 ast_id_ = other->ast_id_;
6286}
6287
6288
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006289void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
6290 ASSERT(!block->IsLoopHeader());
6291 ASSERT(values_.length() == other->values_.length());
6292
6293 int length = values_.length();
6294 for (int i = 0; i < length; ++i) {
6295 HValue* value = values_[i];
6296 if (value != NULL && value->IsPhi() && value->block() == block) {
6297 // There is already a phi for the i'th value.
6298 HPhi* phi = HPhi::cast(value);
6299 // Assert index is correct and that we haven't missed an incoming edge.
6300 ASSERT(phi->merged_index() == i);
6301 ASSERT(phi->OperandCount() == block->predecessors()->length());
6302 phi->AddInput(other->values_[i]);
6303 } else if (values_[i] != other->values_[i]) {
6304 // There is a fresh value on the incoming edge, a phi is needed.
6305 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006306 HPhi* phi = new(block->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006307 HValue* old_value = values_[i];
6308 for (int j = 0; j < block->predecessors()->length(); j++) {
6309 phi->AddInput(old_value);
6310 }
6311 phi->AddInput(other->values_[i]);
6312 this->values_[i] = phi;
6313 block->AddPhi(phi);
6314 }
6315 }
6316}
6317
6318
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006319void HEnvironment::Bind(int index, HValue* value) {
6320 ASSERT(value != NULL);
6321 if (!assigned_variables_.Contains(index)) {
6322 assigned_variables_.Add(index);
6323 }
6324 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006325}
6326
6327
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006328bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006329 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006330}
6331
6332
6333bool HEnvironment::ExpressionStackIsEmpty() const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006334 int first_expression = parameter_count() + specials_count() + local_count();
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006335 ASSERT(length() >= first_expression);
6336 return length() == first_expression;
6337}
6338
6339
6340void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
6341 int count = index_from_top + 1;
6342 int index = values_.length() - count;
6343 ASSERT(HasExpressionAt(index));
6344 // The push count must include at least the element in question or else
6345 // the new value will not be included in this environment's history.
6346 if (push_count_ < count) {
6347 // This is the same effect as popping then re-pushing 'count' elements.
6348 pop_count_ += (count - push_count_);
6349 push_count_ = count;
6350 }
6351 values_[index] = value;
6352}
6353
6354
6355void HEnvironment::Drop(int count) {
6356 for (int i = 0; i < count; ++i) {
6357 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006358 }
6359}
6360
6361
6362HEnvironment* HEnvironment::Copy() const {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006363 return new(closure()->GetIsolate()->zone()) HEnvironment(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006364}
6365
6366
6367HEnvironment* HEnvironment::CopyWithoutHistory() const {
6368 HEnvironment* result = Copy();
6369 result->ClearHistory();
6370 return result;
6371}
6372
6373
6374HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
6375 HEnvironment* new_env = Copy();
6376 for (int i = 0; i < values_.length(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006377 HPhi* phi = new(loop_header->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006378 phi->AddInput(values_[i]);
6379 new_env->values_[i] = phi;
6380 loop_header->AddPhi(phi);
6381 }
6382 new_env->ClearHistory();
6383 return new_env;
6384}
6385
6386
danno@chromium.org40cb8782011-05-25 07:58:50 +00006387HEnvironment* HEnvironment::CopyForInlining(
6388 Handle<JSFunction> target,
6389 FunctionLiteral* function,
danno@chromium.org40cb8782011-05-25 07:58:50 +00006390 HConstant* undefined,
6391 CallKind call_kind) const {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006392 // Outer environment is a copy of this one without the arguments.
6393 int arity = function->scope()->num_parameters();
6394 HEnvironment* outer = Copy();
6395 outer->Drop(arity + 1); // Including receiver.
6396 outer->ClearHistory();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006397 Zone* zone = closure()->GetIsolate()->zone();
6398 HEnvironment* inner =
6399 new(zone) HEnvironment(outer, function->scope(), target);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006400 // Get the argument values from the original environment.
vegorov@chromium.org3cf47312011-06-29 13:20:01 +00006401 for (int i = 0; i <= arity; ++i) { // Include receiver.
6402 HValue* push = ExpressionStackAt(arity - i);
6403 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006404 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006405 // If the function we are inlining is a strict mode function or a
6406 // builtin function, pass undefined as the receiver for function
6407 // calls (instead of the global receiver).
6408 if ((target->shared()->native() || function->strict_mode()) &&
6409 call_kind == CALL_AS_FUNCTION) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00006410 inner->SetValueAt(0, undefined);
6411 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006412 inner->SetValueAt(arity + 1, outer->LookupContext());
6413 for (int i = arity + 2; i < inner->length(); ++i) {
6414 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006415 }
6416
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006417 inner->set_ast_id(AstNode::kFunctionEntryId);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006418 return inner;
6419}
6420
6421
6422void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006423 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006424 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006425 if (i == parameter_count()) stream->Add("specials\n");
6426 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
6427 if (i == parameter_count() + specials_count() + local_count()) {
6428 stream->Add("expressions");
6429 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006430 HValue* val = values_.at(i);
6431 stream->Add("%d: ", i);
6432 if (val != NULL) {
6433 val->PrintNameTo(stream);
6434 } else {
6435 stream->Add("NULL");
6436 }
6437 stream->Add("\n");
6438 }
6439}
6440
6441
6442void HEnvironment::PrintToStd() {
6443 HeapStringAllocator string_allocator;
6444 StringStream trace(&string_allocator);
6445 PrintTo(&trace);
6446 PrintF("%s", *trace.ToCString());
6447}
6448
6449
6450void HTracer::TraceCompilation(FunctionLiteral* function) {
6451 Tag tag(this, "compilation");
6452 Handle<String> name = function->debug_name();
6453 PrintStringProperty("name", *name->ToCString());
6454 PrintStringProperty("method", *name->ToCString());
6455 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
6456}
6457
6458
6459void HTracer::TraceLithium(const char* name, LChunk* chunk) {
6460 Trace(name, chunk->graph(), chunk);
6461}
6462
6463
6464void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
6465 Trace(name, graph, NULL);
6466}
6467
6468
6469void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
6470 Tag tag(this, "cfg");
6471 PrintStringProperty("name", name);
6472 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
6473 for (int i = 0; i < blocks->length(); i++) {
6474 HBasicBlock* current = blocks->at(i);
6475 Tag block_tag(this, "block");
6476 PrintBlockProperty("name", current->block_id());
6477 PrintIntProperty("from_bci", -1);
6478 PrintIntProperty("to_bci", -1);
6479
6480 if (!current->predecessors()->is_empty()) {
6481 PrintIndent();
6482 trace_.Add("predecessors");
6483 for (int j = 0; j < current->predecessors()->length(); ++j) {
6484 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
6485 }
6486 trace_.Add("\n");
6487 } else {
6488 PrintEmptyProperty("predecessors");
6489 }
6490
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00006491 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006492 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00006493 } else {
6494 PrintIndent();
6495 trace_.Add("successors");
6496 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
6497 trace_.Add(" \"B%d\"", it.Current()->block_id());
6498 }
6499 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006500 }
6501
6502 PrintEmptyProperty("xhandlers");
6503 PrintEmptyProperty("flags");
6504
6505 if (current->dominator() != NULL) {
6506 PrintBlockProperty("dominator", current->dominator()->block_id());
6507 }
6508
6509 if (chunk != NULL) {
6510 int first_index = current->first_instruction_index();
6511 int last_index = current->last_instruction_index();
6512 PrintIntProperty(
6513 "first_lir_id",
6514 LifetimePosition::FromInstructionIndex(first_index).Value());
6515 PrintIntProperty(
6516 "last_lir_id",
6517 LifetimePosition::FromInstructionIndex(last_index).Value());
6518 }
6519
6520 {
6521 Tag states_tag(this, "states");
6522 Tag locals_tag(this, "locals");
6523 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006524 PrintIntProperty("size", current->phis()->length());
6525 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006526 for (int j = 0; j < total; ++j) {
6527 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006528 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006529 trace_.Add("%d ", phi->merged_index());
6530 phi->PrintNameTo(&trace_);
6531 trace_.Add(" ");
6532 phi->PrintTo(&trace_);
6533 trace_.Add("\n");
6534 }
6535 }
6536
6537 {
6538 Tag HIR_tag(this, "HIR");
6539 HInstruction* instruction = current->first();
6540 while (instruction != NULL) {
6541 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00006542 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006543 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006544 trace_.Add("%d %d ", bci, uses);
6545 instruction->PrintNameTo(&trace_);
6546 trace_.Add(" ");
6547 instruction->PrintTo(&trace_);
6548 trace_.Add(" <|@\n");
6549 instruction = instruction->next();
6550 }
6551 }
6552
6553
6554 if (chunk != NULL) {
6555 Tag LIR_tag(this, "LIR");
6556 int first_index = current->first_instruction_index();
6557 int last_index = current->last_instruction_index();
6558 if (first_index != -1 && last_index != -1) {
6559 const ZoneList<LInstruction*>* instructions = chunk->instructions();
6560 for (int i = first_index; i <= last_index; ++i) {
6561 LInstruction* linstr = instructions->at(i);
6562 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006563 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006564 trace_.Add("%d ",
6565 LifetimePosition::FromInstructionIndex(i).Value());
6566 linstr->PrintTo(&trace_);
6567 trace_.Add(" <|@\n");
6568 }
6569 }
6570 }
6571 }
6572 }
6573}
6574
6575
6576void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
6577 Tag tag(this, "intervals");
6578 PrintStringProperty("name", name);
6579
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006580 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006581 for (int i = 0; i < fixed_d->length(); ++i) {
6582 TraceLiveRange(fixed_d->at(i), "fixed");
6583 }
6584
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006585 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006586 for (int i = 0; i < fixed->length(); ++i) {
6587 TraceLiveRange(fixed->at(i), "fixed");
6588 }
6589
6590 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
6591 for (int i = 0; i < live_ranges->length(); ++i) {
6592 TraceLiveRange(live_ranges->at(i), "object");
6593 }
6594}
6595
6596
6597void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
6598 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006599 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006600 trace_.Add("%d %s", range->id(), type);
6601 if (range->HasRegisterAssigned()) {
6602 LOperand* op = range->CreateAssignedOperand();
6603 int assigned_reg = op->index();
6604 if (op->IsDoubleRegister()) {
6605 trace_.Add(" \"%s\"",
6606 DoubleRegister::AllocationIndexToString(assigned_reg));
6607 } else {
6608 ASSERT(op->IsRegister());
6609 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
6610 }
6611 } else if (range->IsSpilled()) {
6612 LOperand* op = range->TopLevel()->GetSpillOperand();
6613 if (op->IsDoubleStackSlot()) {
6614 trace_.Add(" \"double_stack:%d\"", op->index());
6615 } else {
6616 ASSERT(op->IsStackSlot());
6617 trace_.Add(" \"stack:%d\"", op->index());
6618 }
6619 }
6620 int parent_index = -1;
6621 if (range->IsChild()) {
6622 parent_index = range->parent()->id();
6623 } else {
6624 parent_index = range->id();
6625 }
6626 LOperand* op = range->FirstHint();
6627 int hint_index = -1;
6628 if (op != NULL && op->IsUnallocated()) hint_index = op->VirtualRegister();
6629 trace_.Add(" %d %d", parent_index, hint_index);
6630 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006631 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006632 trace_.Add(" [%d, %d[",
6633 cur_interval->start().Value(),
6634 cur_interval->end().Value());
6635 cur_interval = cur_interval->next();
6636 }
6637
6638 UsePosition* current_pos = range->first_pos();
6639 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006640 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006641 trace_.Add(" %d M", current_pos->pos().Value());
6642 }
6643 current_pos = current_pos->next();
6644 }
6645
6646 trace_.Add(" \"\"\n");
6647 }
6648}
6649
6650
6651void HTracer::FlushToFile() {
6652 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
6653 trace_.Reset();
6654}
6655
6656
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006657void HStatistics::Initialize(CompilationInfo* info) {
6658 source_size_ += info->shared_info()->SourceSize();
6659}
6660
6661
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006662void HStatistics::Print() {
6663 PrintF("Timing results:\n");
6664 int64_t sum = 0;
6665 for (int i = 0; i < timing_.length(); ++i) {
6666 sum += timing_[i];
6667 }
6668
6669 for (int i = 0; i < names_.length(); ++i) {
6670 PrintF("%30s", names_[i]);
6671 double ms = static_cast<double>(timing_[i]) / 1000;
6672 double percent = static_cast<double>(timing_[i]) * 100 / sum;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006673 PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
6674
6675 unsigned size = sizes_[i];
6676 double size_percent = static_cast<double>(size) * 100 / total_size_;
6677 PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006678 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006679 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006680 double normalized_time = source_size_in_kb > 0
6681 ? (static_cast<double>(sum) / 1000) / source_size_in_kb
6682 : 0;
6683 double normalized_bytes = source_size_in_kb > 0
6684 ? total_size_ / source_size_in_kb
6685 : 0;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006686 PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006687 normalized_time, normalized_bytes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006688 PrintF("---------------------------------------------------------------\n");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006689 PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006690 "Total",
6691 static_cast<double>(total_) / 1000,
6692 static_cast<double>(total_) / full_code_gen_);
6693}
6694
6695
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006696void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006697 if (name == HPhase::kFullCodeGen) {
6698 full_code_gen_ += ticks;
6699 } else if (name == HPhase::kTotal) {
6700 total_ += ticks;
6701 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006702 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006703 for (int i = 0; i < names_.length(); ++i) {
6704 if (names_[i] == name) {
6705 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006706 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006707 return;
6708 }
6709 }
6710 names_.Add(name);
6711 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006712 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006713 }
6714}
6715
6716
6717const char* const HPhase::kFullCodeGen = "Full code generator";
6718const char* const HPhase::kTotal = "Total";
6719
6720
6721void HPhase::Begin(const char* name,
6722 HGraph* graph,
6723 LChunk* chunk,
6724 LAllocator* allocator) {
6725 name_ = name;
6726 graph_ = graph;
6727 chunk_ = chunk;
6728 allocator_ = allocator;
6729 if (allocator != NULL && chunk_ == NULL) {
6730 chunk_ = allocator->chunk();
6731 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006732 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006733 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006734}
6735
6736
6737void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006738 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006739 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006740 unsigned size = Zone::allocation_size_ - start_allocation_size_;
6741 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006742 }
6743
6744 if (FLAG_trace_hydrogen) {
6745 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
6746 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
6747 if (allocator_ != NULL) {
6748 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
6749 }
6750 }
6751
6752#ifdef DEBUG
6753 if (graph_ != NULL) graph_->Verify();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006754 if (allocator_ != NULL) allocator_->Verify();
6755#endif
6756}
6757
6758} } // namespace v8::internal