blob: 7ad556476ecb873cd3ee6d143111e0360d534618 [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"
36#include "scopes.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000037#include "stub-cache.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000038
39#if V8_TARGET_ARCH_IA32
40#include "ia32/lithium-codegen-ia32.h"
41#elif V8_TARGET_ARCH_X64
42#include "x64/lithium-codegen-x64.h"
43#elif V8_TARGET_ARCH_ARM
44#include "arm/lithium-codegen-arm.h"
lrn@chromium.org7516f052011-03-30 08:52:27 +000045#elif V8_TARGET_ARCH_MIPS
46#include "mips/lithium-codegen-mips.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000047#else
48#error Unsupported target architecture.
49#endif
50
51namespace v8 {
52namespace internal {
53
54HBasicBlock::HBasicBlock(HGraph* graph)
55 : block_id_(graph->GetNextBlockID()),
56 graph_(graph),
57 phis_(4),
58 first_(NULL),
59 last_(NULL),
60 end_(NULL),
61 loop_information_(NULL),
62 predecessors_(2),
63 dominator_(NULL),
64 dominated_blocks_(4),
65 last_environment_(NULL),
66 argument_count_(-1),
67 first_instruction_index_(-1),
68 last_instruction_index_(-1),
69 deleted_phis_(4),
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000070 parent_loop_header_(NULL),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +000071 is_inline_return_target_(false),
72 is_deoptimizing_(false) { }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000073
74
75void HBasicBlock::AttachLoopInformation() {
76 ASSERT(!IsLoopHeader());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +000077 loop_information_ = new(zone()) HLoopInformation(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000078}
79
80
81void HBasicBlock::DetachLoopInformation() {
82 ASSERT(IsLoopHeader());
83 loop_information_ = NULL;
84}
85
86
87void HBasicBlock::AddPhi(HPhi* phi) {
88 ASSERT(!IsStartBlock());
89 phis_.Add(phi);
90 phi->SetBlock(this);
91}
92
93
94void HBasicBlock::RemovePhi(HPhi* phi) {
95 ASSERT(phi->block() == this);
96 ASSERT(phis_.Contains(phi));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000097 ASSERT(phi->HasNoUses() || !phi->is_live());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000098 phi->ClearOperands();
99 phis_.RemoveElement(phi);
100 phi->SetBlock(NULL);
101}
102
103
104void HBasicBlock::AddInstruction(HInstruction* instr) {
105 ASSERT(!IsStartBlock() || !IsFinished());
106 ASSERT(!instr->IsLinked());
107 ASSERT(!IsFinished());
108 if (first_ == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000109 HBlockEntry* entry = new(zone()) HBlockEntry();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000110 entry->InitializeAsFirst(this);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000111 first_ = last_ = entry;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000112 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000113 instr->InsertAfter(last_);
114 last_ = instr;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000115}
116
117
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000118HDeoptimize* HBasicBlock::CreateDeoptimize(
119 HDeoptimize::UseEnvironment has_uses) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000120 ASSERT(HasEnvironment());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000121 if (has_uses == HDeoptimize::kNoUses) return new(zone()) HDeoptimize(0);
122
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000123 HEnvironment* environment = last_environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000124 HDeoptimize* instr = new(zone()) HDeoptimize(environment->length());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000125 for (int i = 0; i < environment->length(); i++) {
126 HValue* val = environment->values()->at(i);
127 instr->AddEnvironmentValue(val);
128 }
129
130 return instr;
131}
132
133
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000134HSimulate* HBasicBlock::CreateSimulate(int id) {
135 ASSERT(HasEnvironment());
136 HEnvironment* environment = last_environment();
137 ASSERT(id == AstNode::kNoNumber ||
138 environment->closure()->shared()->VerifyBailoutId(id));
139
140 int push_count = environment->push_count();
141 int pop_count = environment->pop_count();
142
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000143 HSimulate* instr = new(zone()) HSimulate(id, pop_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000144 for (int i = push_count - 1; i >= 0; --i) {
145 instr->AddPushedValue(environment->ExpressionStackAt(i));
146 }
147 for (int i = 0; i < environment->assigned_variables()->length(); ++i) {
148 int index = environment->assigned_variables()->at(i);
149 instr->AddAssignedValue(index, environment->Lookup(index));
150 }
151 environment->ClearHistory();
152 return instr;
153}
154
155
156void HBasicBlock::Finish(HControlInstruction* end) {
157 ASSERT(!IsFinished());
158 AddInstruction(end);
159 end_ = end;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000160 for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
161 it.Current()->RegisterPredecessor(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000162 }
163}
164
165
166void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000167 if (block->IsInlineReturnTarget()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000168 AddInstruction(new(zone()) HLeaveInlined);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000169 last_environment_ = last_environment()->outer();
170 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000171 AddSimulate(AstNode::kNoNumber);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +0000172 HGoto* instr = new(zone()) HGoto(block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000173 instr->set_include_stack_check(include_stack_check);
174 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
197void HBasicBlock::SetJoinId(int id) {
198 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()
207 ->VerifyBailoutId(id));
208 simulate->set_ast_id(id);
209 }
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.org9ee27ae2011-03-02 13:43:26 +0000579 if (body_exit != NULL) body_exit->Goto(loop_entry, true);
580 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()) {
739 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
740 } else {
741 for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) {
742 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
743 }
744 }
745 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000746
747 // Propagate flag marking blocks containing unconditional deoptimize.
748 MarkAsDeoptimizingRecursively(entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000749}
750
751
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000752// Mark all blocks that are dominated by an unconditional deoptimize.
753void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
754 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
755 HBasicBlock* dominated = block->dominated_blocks()->at(i);
756 if (block->IsDeoptimizing()) dominated->MarkAsDeoptimizing();
757 MarkAsDeoptimizingRecursively(dominated);
758 }
759}
760
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000761void HGraph::EliminateRedundantPhis() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000762 HPhase phase("Redundant phi elimination", this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000763
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000764 // Worklist of phis that can potentially be eliminated. Initialized with
765 // all phi nodes. When elimination of a phi node modifies another phi node
766 // the modified phi node is added to the worklist.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000767 ZoneList<HPhi*> worklist(blocks_.length());
768 for (int i = 0; i < blocks_.length(); ++i) {
769 worklist.AddAll(*blocks_[i]->phis());
770 }
771
772 while (!worklist.is_empty()) {
773 HPhi* phi = worklist.RemoveLast();
774 HBasicBlock* block = phi->block();
775
776 // Skip phi node if it was already replaced.
777 if (block == NULL) continue;
778
779 // Get replacement value if phi is redundant.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000780 HValue* replacement = phi->GetRedundantReplacement();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000781
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000782 if (replacement != NULL) {
783 // Iterate through the uses and replace them all.
784 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
785 HValue* value = it.value();
786 value->SetOperandAt(it.index(), replacement);
787 if (value->IsPhi()) worklist.Add(HPhi::cast(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000788 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000789 block->RemovePhi(phi);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000790 }
791 }
792}
793
794
795void HGraph::EliminateUnreachablePhis() {
796 HPhase phase("Unreachable phi elimination", this);
797
798 // Initialize worklist.
799 ZoneList<HPhi*> phi_list(blocks_.length());
800 ZoneList<HPhi*> worklist(blocks_.length());
801 for (int i = 0; i < blocks_.length(); ++i) {
802 for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
803 HPhi* phi = blocks_[i]->phis()->at(j);
804 phi_list.Add(phi);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +0000805 // We can't eliminate phis in the receiver position in the environment
806 // because in case of throwing an error we need this value to
807 // construct a stack trace.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000808 if (phi->HasRealUses() || phi->IsReceiver()) {
809 phi->set_is_live(true);
810 worklist.Add(phi);
811 }
812 }
813 }
814
815 // Iteratively mark live phis.
816 while (!worklist.is_empty()) {
817 HPhi* phi = worklist.RemoveLast();
818 for (int i = 0; i < phi->OperandCount(); i++) {
819 HValue* operand = phi->OperandAt(i);
820 if (operand->IsPhi() && !HPhi::cast(operand)->is_live()) {
821 HPhi::cast(operand)->set_is_live(true);
822 worklist.Add(HPhi::cast(operand));
823 }
824 }
825 }
826
827 // Remove unreachable phis.
828 for (int i = 0; i < phi_list.length(); i++) {
829 HPhi* phi = phi_list[i];
830 if (!phi->is_live()) {
831 HBasicBlock* block = phi->block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000832 block->RemovePhi(phi);
833 block->RecordDeletedPhi(phi->merged_index());
834 }
835 }
836}
837
838
839bool HGraph::CollectPhis() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000840 int block_count = blocks_.length();
841 phi_list_ = new ZoneList<HPhi*>(block_count);
842 for (int i = 0; i < block_count; ++i) {
843 for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
844 HPhi* phi = blocks_[i]->phis()->at(j);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000845 phi_list_->Add(phi);
846 // We don't support phi uses of arguments for now.
847 if (phi->CheckFlag(HValue::kIsArguments)) return false;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000848 // Check for the hole value (from an uninitialized const).
849 for (int k = 0; k < phi->OperandCount(); k++) {
850 if (phi->OperandAt(k) == GetConstantHole()) return false;
851 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000852 }
853 }
854 return true;
855}
856
857
858void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
859 BitVector in_worklist(GetMaximumValueID());
860 for (int i = 0; i < worklist->length(); ++i) {
861 ASSERT(!in_worklist.Contains(worklist->at(i)->id()));
862 in_worklist.Add(worklist->at(i)->id());
863 }
864
865 while (!worklist->is_empty()) {
866 HValue* current = worklist->RemoveLast();
867 in_worklist.Remove(current->id());
868 if (current->UpdateInferredType()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +0000869 for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
870 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000871 if (!in_worklist.Contains(use->id())) {
872 in_worklist.Add(use->id());
873 worklist->Add(use);
874 }
875 }
876 }
877 }
878}
879
880
881class HRangeAnalysis BASE_EMBEDDED {
882 public:
883 explicit HRangeAnalysis(HGraph* graph) : graph_(graph), changed_ranges_(16) {}
884
885 void Analyze();
886
887 private:
888 void TraceRange(const char* msg, ...);
889 void Analyze(HBasicBlock* block);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000890 void InferControlFlowRange(HTest* test, HBasicBlock* dest);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000891 void InferControlFlowRange(Token::Value op, HValue* value, HValue* other);
892 void InferPhiRange(HPhi* phi);
893 void InferRange(HValue* value);
894 void RollBackTo(int index);
895 void AddRange(HValue* value, Range* range);
896
897 HGraph* graph_;
898 ZoneList<HValue*> changed_ranges_;
899};
900
901
902void HRangeAnalysis::TraceRange(const char* msg, ...) {
903 if (FLAG_trace_range) {
904 va_list arguments;
905 va_start(arguments, msg);
906 OS::VPrint(msg, arguments);
907 va_end(arguments);
908 }
909}
910
911
912void HRangeAnalysis::Analyze() {
913 HPhase phase("Range analysis", graph_);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000914 Analyze(graph_->entry_block());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000915}
916
917
918void HRangeAnalysis::Analyze(HBasicBlock* block) {
919 TraceRange("Analyzing block B%d\n", block->block_id());
920
921 int last_changed_range = changed_ranges_.length() - 1;
922
923 // Infer range based on control flow.
924 if (block->predecessors()->length() == 1) {
925 HBasicBlock* pred = block->predecessors()->first();
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000926 if (pred->end()->IsTest()) {
927 InferControlFlowRange(HTest::cast(pred->end()), block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000928 }
929 }
930
931 // Process phi instructions.
932 for (int i = 0; i < block->phis()->length(); ++i) {
933 HPhi* phi = block->phis()->at(i);
934 InferPhiRange(phi);
935 }
936
937 // Go through all instructions of the current block.
938 HInstruction* instr = block->first();
939 while (instr != block->end()) {
940 InferRange(instr);
941 instr = instr->next();
942 }
943
944 // Continue analysis in all dominated blocks.
945 for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
946 Analyze(block->dominated_blocks()->at(i));
947 }
948
949 RollBackTo(last_changed_range);
950}
951
952
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000953void HRangeAnalysis::InferControlFlowRange(HTest* test, HBasicBlock* dest) {
954 ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
955 if (test->value()->IsCompare()) {
956 HCompare* compare = HCompare::cast(test->value());
whesse@chromium.org5c297712011-03-10 14:17:25 +0000957 if (compare->GetInputRepresentation().IsInteger32()) {
958 Token::Value op = compare->token();
959 if (test->SecondSuccessor() == dest) {
960 op = Token::NegateCompareOp(op);
961 }
962 Token::Value inverted_op = Token::InvertCompareOp(op);
963 InferControlFlowRange(op, compare->left(), compare->right());
964 InferControlFlowRange(inverted_op, compare->right(), compare->left());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000965 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000966 }
967}
968
969
970// We know that value [op] other. Use this information to update the range on
971// value.
972void HRangeAnalysis::InferControlFlowRange(Token::Value op,
973 HValue* value,
974 HValue* other) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000975 Range temp_range;
976 Range* range = other->range() != NULL ? other->range() : &temp_range;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000977 Range* new_range = NULL;
978
979 TraceRange("Control flow range infer %d %s %d\n",
980 value->id(),
981 Token::Name(op),
982 other->id());
983
984 if (op == Token::EQ || op == Token::EQ_STRICT) {
985 // The same range has to apply for value.
986 new_range = range->Copy();
987 } else if (op == Token::LT || op == Token::LTE) {
988 new_range = range->CopyClearLower();
989 if (op == Token::LT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000990 new_range->AddConstant(-1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000991 }
992 } else if (op == Token::GT || op == Token::GTE) {
993 new_range = range->CopyClearUpper();
994 if (op == Token::GT) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000995 new_range->AddConstant(1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000996 }
997 }
998
999 if (new_range != NULL && !new_range->IsMostGeneric()) {
1000 AddRange(value, new_range);
1001 }
1002}
1003
1004
1005void HRangeAnalysis::InferPhiRange(HPhi* phi) {
1006 // TODO(twuerthinger): Infer loop phi ranges.
1007 InferRange(phi);
1008}
1009
1010
1011void HRangeAnalysis::InferRange(HValue* value) {
1012 ASSERT(!value->HasRange());
1013 if (!value->representation().IsNone()) {
1014 value->ComputeInitialRange();
1015 Range* range = value->range();
1016 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
1017 value->id(),
1018 value->Mnemonic(),
1019 range->lower(),
1020 range->upper());
1021 }
1022}
1023
1024
1025void HRangeAnalysis::RollBackTo(int index) {
1026 for (int i = index + 1; i < changed_ranges_.length(); ++i) {
1027 changed_ranges_[i]->RemoveLastAddedRange();
1028 }
1029 changed_ranges_.Rewind(index + 1);
1030}
1031
1032
1033void HRangeAnalysis::AddRange(HValue* value, Range* range) {
1034 Range* original_range = value->range();
1035 value->AddNewRange(range);
1036 changed_ranges_.Add(value);
1037 Range* new_range = value->range();
1038 TraceRange("Updated range of %d set to [%d,%d]\n",
1039 value->id(),
1040 new_range->lower(),
1041 new_range->upper());
1042 if (original_range != NULL) {
1043 TraceRange("Original range was [%d,%d]\n",
1044 original_range->lower(),
1045 original_range->upper());
1046 }
1047 TraceRange("New information was [%d,%d]\n",
1048 range->lower(),
1049 range->upper());
1050}
1051
1052
1053void TraceGVN(const char* msg, ...) {
1054 if (FLAG_trace_gvn) {
1055 va_list arguments;
1056 va_start(arguments, msg);
1057 OS::VPrint(msg, arguments);
1058 va_end(arguments);
1059 }
1060}
1061
1062
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001063HValueMap::HValueMap(Zone* zone, const HValueMap* other)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001064 : array_size_(other->array_size_),
1065 lists_size_(other->lists_size_),
1066 count_(other->count_),
1067 present_flags_(other->present_flags_),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001068 array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
1069 lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001070 free_list_head_(other->free_list_head_) {
1071 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
1072 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
1073}
1074
1075
1076void HValueMap::Kill(int flags) {
1077 int depends_flags = HValue::ConvertChangesToDependsFlags(flags);
1078 if ((present_flags_ & depends_flags) == 0) return;
1079 present_flags_ = 0;
1080 for (int i = 0; i < array_size_; ++i) {
1081 HValue* value = array_[i].value;
1082 if (value != NULL) {
1083 // Clear list of collisions first, so we know if it becomes empty.
1084 int kept = kNil; // List of kept elements.
1085 int next;
1086 for (int current = array_[i].next; current != kNil; current = next) {
1087 next = lists_[current].next;
1088 if ((lists_[current].value->flags() & depends_flags) != 0) {
1089 // Drop it.
1090 count_--;
1091 lists_[current].next = free_list_head_;
1092 free_list_head_ = current;
1093 } else {
1094 // Keep it.
1095 lists_[current].next = kept;
1096 kept = current;
1097 present_flags_ |= lists_[current].value->flags();
1098 }
1099 }
1100 array_[i].next = kept;
1101
1102 // Now possibly drop directly indexed element.
1103 if ((array_[i].value->flags() & depends_flags) != 0) { // Drop it.
1104 count_--;
1105 int head = array_[i].next;
1106 if (head == kNil) {
1107 array_[i].value = NULL;
1108 } else {
1109 array_[i].value = lists_[head].value;
1110 array_[i].next = lists_[head].next;
1111 lists_[head].next = free_list_head_;
1112 free_list_head_ = head;
1113 }
1114 } else {
1115 present_flags_ |= array_[i].value->flags(); // Keep it.
1116 }
1117 }
1118 }
1119}
1120
1121
1122HValue* HValueMap::Lookup(HValue* value) const {
1123 uint32_t hash = static_cast<uint32_t>(value->Hashcode());
1124 uint32_t pos = Bound(hash);
1125 if (array_[pos].value != NULL) {
1126 if (array_[pos].value->Equals(value)) return array_[pos].value;
1127 int next = array_[pos].next;
1128 while (next != kNil) {
1129 if (lists_[next].value->Equals(value)) return lists_[next].value;
1130 next = lists_[next].next;
1131 }
1132 }
1133 return NULL;
1134}
1135
1136
1137void HValueMap::Resize(int new_size) {
1138 ASSERT(new_size > count_);
1139 // Hashing the values into the new array has no more collisions than in the
1140 // old hash map, so we can use the existing lists_ array, if we are careful.
1141
1142 // Make sure we have at least one free element.
1143 if (free_list_head_ == kNil) {
1144 ResizeLists(lists_size_ << 1);
1145 }
1146
1147 HValueMapListElement* new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001148 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001149 memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
1150
1151 HValueMapListElement* old_array = array_;
1152 int old_size = array_size_;
1153
1154 int old_count = count_;
1155 count_ = 0;
1156 // Do not modify present_flags_. It is currently correct.
1157 array_size_ = new_size;
1158 array_ = new_array;
1159
1160 if (old_array != NULL) {
1161 // Iterate over all the elements in lists, rehashing them.
1162 for (int i = 0; i < old_size; ++i) {
1163 if (old_array[i].value != NULL) {
1164 int current = old_array[i].next;
1165 while (current != kNil) {
1166 Insert(lists_[current].value);
1167 int next = lists_[current].next;
1168 lists_[current].next = free_list_head_;
1169 free_list_head_ = current;
1170 current = next;
1171 }
1172 // Rehash the directly stored value.
1173 Insert(old_array[i].value);
1174 }
1175 }
1176 }
1177 USE(old_count);
1178 ASSERT(count_ == old_count);
1179}
1180
1181
1182void HValueMap::ResizeLists(int new_size) {
1183 ASSERT(new_size > lists_size_);
1184
1185 HValueMapListElement* new_lists =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001186 ZONE->NewArray<HValueMapListElement>(new_size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001187 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
1188
1189 HValueMapListElement* old_lists = lists_;
1190 int old_size = lists_size_;
1191
1192 lists_size_ = new_size;
1193 lists_ = new_lists;
1194
1195 if (old_lists != NULL) {
1196 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
1197 }
1198 for (int i = old_size; i < lists_size_; ++i) {
1199 lists_[i].next = free_list_head_;
1200 free_list_head_ = i;
1201 }
1202}
1203
1204
1205void HValueMap::Insert(HValue* value) {
1206 ASSERT(value != NULL);
1207 // Resizing when half of the hashtable is filled up.
1208 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1);
1209 ASSERT(count_ < array_size_);
1210 count_++;
1211 uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
1212 if (array_[pos].value == NULL) {
1213 array_[pos].value = value;
1214 array_[pos].next = kNil;
1215 } else {
1216 if (free_list_head_ == kNil) {
1217 ResizeLists(lists_size_ << 1);
1218 }
1219 int new_element_pos = free_list_head_;
1220 ASSERT(new_element_pos != kNil);
1221 free_list_head_ = lists_[free_list_head_].next;
1222 lists_[new_element_pos].value = value;
1223 lists_[new_element_pos].next = array_[pos].next;
1224 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
1225 array_[pos].next = new_element_pos;
1226 }
1227}
1228
1229
1230class HStackCheckEliminator BASE_EMBEDDED {
1231 public:
1232 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { }
1233
1234 void Process();
1235
1236 private:
1237 void RemoveStackCheck(HBasicBlock* block);
1238
1239 HGraph* graph_;
1240};
1241
1242
1243void HStackCheckEliminator::Process() {
1244 // For each loop block walk the dominator tree from the backwards branch to
1245 // the loop header. If a call instruction is encountered the backwards branch
1246 // is dominated by a call and the stack check in the backwards branch can be
1247 // removed.
1248 for (int i = 0; i < graph_->blocks()->length(); i++) {
1249 HBasicBlock* block = graph_->blocks()->at(i);
1250 if (block->IsLoopHeader()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001251 HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
1252 HBasicBlock* dominator = back_edge;
1253 bool back_edge_dominated_by_call = false;
1254 while (dominator != block && !back_edge_dominated_by_call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001255 HInstruction* instr = dominator->first();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001256 while (instr != NULL && !back_edge_dominated_by_call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001257 if (instr->IsCall()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001258 RemoveStackCheck(back_edge);
1259 back_edge_dominated_by_call = true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001260 }
1261 instr = instr->next();
1262 }
1263 dominator = dominator->dominator();
1264 }
1265 }
1266 }
1267}
1268
1269
1270void HStackCheckEliminator::RemoveStackCheck(HBasicBlock* block) {
1271 HInstruction* instr = block->first();
1272 while (instr != NULL) {
1273 if (instr->IsGoto()) {
1274 HGoto::cast(instr)->set_include_stack_check(false);
1275 return;
1276 }
1277 instr = instr->next();
1278 }
1279}
1280
1281
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001282// Simple sparse set with O(1) add, contains, and clear.
1283class SparseSet {
1284 public:
1285 SparseSet(Zone* zone, int capacity)
1286 : capacity_(capacity),
1287 length_(0),
1288 dense_(zone->NewArray<int>(capacity)),
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001289 sparse_(zone->NewArray<int>(capacity)) {
1290#ifndef NVALGRIND
1291 // Initialize the sparse array to make valgrind happy.
1292 memset(sparse_, 0, sizeof(sparse_[0]) * capacity);
1293#endif
1294 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001295
1296 bool Contains(int n) const {
1297 ASSERT(0 <= n && n < capacity_);
1298 int d = sparse_[n];
1299 return 0 <= d && d < length_ && dense_[d] == n;
1300 }
1301
1302 bool Add(int n) {
1303 if (Contains(n)) return false;
1304 dense_[length_] = n;
1305 sparse_[n] = length_;
1306 ++length_;
1307 return true;
1308 }
1309
1310 void Clear() { length_ = 0; }
1311
1312 private:
1313 int capacity_;
1314 int length_;
1315 int* dense_;
1316 int* sparse_;
1317
1318 DISALLOW_COPY_AND_ASSIGN(SparseSet);
1319};
1320
1321
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001322class HGlobalValueNumberer BASE_EMBEDDED {
1323 public:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001324 explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001325 : graph_(graph),
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001326 info_(info),
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001327 block_side_effects_(graph->blocks()->length()),
1328 loop_side_effects_(graph->blocks()->length()),
1329 visited_on_paths_(graph->zone(), graph->blocks()->length()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001330 ASSERT(info->isolate()->heap()->allow_allocation(false));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001331 block_side_effects_.AddBlock(0, graph_->blocks()->length());
1332 loop_side_effects_.AddBlock(0, graph_->blocks()->length());
1333 }
1334 ~HGlobalValueNumberer() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001335 ASSERT(!info_->isolate()->heap()->allow_allocation(true));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001336 }
1337
1338 void Analyze();
1339
1340 private:
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001341 int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator,
1342 HBasicBlock* dominated);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001343 void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
1344 void ComputeBlockSideEffects();
1345 void LoopInvariantCodeMotion();
1346 void ProcessLoopBlock(HBasicBlock* block,
1347 HBasicBlock* before_loop,
1348 int loop_kills);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001349 bool AllowCodeMotion();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001350 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
1351
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001352 HGraph* graph() { return graph_; }
1353 CompilationInfo* info() { return info_; }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001354 Zone* zone() { return graph_->zone(); }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001355
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001356 HGraph* graph_;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001357 CompilationInfo* info_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001358
1359 // A map of block IDs to their side effects.
1360 ZoneList<int> block_side_effects_;
1361
1362 // A map of loop header block IDs to their loop's side effects.
1363 ZoneList<int> loop_side_effects_;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001364
1365 // Used when collecting side effects on paths from dominator to
1366 // dominated.
1367 SparseSet visited_on_paths_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001368};
1369
1370
1371void HGlobalValueNumberer::Analyze() {
1372 ComputeBlockSideEffects();
1373 if (FLAG_loop_invariant_code_motion) {
1374 LoopInvariantCodeMotion();
1375 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001376 HValueMap* map = new(zone()) HValueMap();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001377 AnalyzeBlock(graph_->entry_block(), map);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001378}
1379
1380
1381void HGlobalValueNumberer::ComputeBlockSideEffects() {
1382 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1383 // Compute side effects for the block.
1384 HBasicBlock* block = graph_->blocks()->at(i);
1385 HInstruction* instr = block->first();
1386 int id = block->block_id();
1387 int side_effects = 0;
1388 while (instr != NULL) {
1389 side_effects |= (instr->flags() & HValue::ChangesFlagsMask());
1390 instr = instr->next();
1391 }
1392 block_side_effects_[id] |= side_effects;
1393
1394 // Loop headers are part of their loop.
1395 if (block->IsLoopHeader()) {
1396 loop_side_effects_[id] |= side_effects;
1397 }
1398
1399 // Propagate loop side effects upwards.
1400 if (block->HasParentLoopHeader()) {
1401 int header_id = block->parent_loop_header()->block_id();
1402 loop_side_effects_[header_id] |=
1403 block->IsLoopHeader() ? loop_side_effects_[id] : side_effects;
1404 }
1405 }
1406}
1407
1408
1409void HGlobalValueNumberer::LoopInvariantCodeMotion() {
1410 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
1411 HBasicBlock* block = graph_->blocks()->at(i);
1412 if (block->IsLoopHeader()) {
1413 int side_effects = loop_side_effects_[block->block_id()];
1414 TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n",
1415 block->block_id(),
1416 side_effects);
1417
1418 HBasicBlock* last = block->loop_information()->GetLastBackEdge();
1419 for (int j = block->block_id(); j <= last->block_id(); ++j) {
1420 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
1421 }
1422 }
1423 }
1424}
1425
1426
1427void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
1428 HBasicBlock* loop_header,
1429 int loop_kills) {
1430 HBasicBlock* pre_header = loop_header->predecessors()->at(0);
1431 int depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
1432 TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
1433 block->block_id(),
1434 depends_flags);
1435 HInstruction* instr = block->first();
1436 while (instr != NULL) {
1437 HInstruction* next = instr->next();
1438 if (instr->CheckFlag(HValue::kUseGVN) &&
1439 (instr->flags() & depends_flags) == 0) {
1440 TraceGVN("Checking instruction %d (%s)\n",
1441 instr->id(),
1442 instr->Mnemonic());
1443 bool inputs_loop_invariant = true;
1444 for (int i = 0; i < instr->OperandCount(); ++i) {
1445 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
1446 inputs_loop_invariant = false;
1447 }
1448 }
1449
1450 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
1451 TraceGVN("Found loop invariant instruction %d\n", instr->id());
1452 // Move the instruction out of the loop.
1453 instr->Unlink();
1454 instr->InsertBefore(pre_header->end());
1455 }
1456 }
1457 instr = next;
1458 }
1459}
1460
kmillikin@chromium.orgc0cfb562011-01-26 10:44:48 +00001461
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001462bool HGlobalValueNumberer::AllowCodeMotion() {
1463 return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount;
1464}
1465
1466
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001467bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
1468 HBasicBlock* loop_header) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001469 // If we've disabled code motion or we're in a block that unconditionally
1470 // deoptimizes, don't move any instructions.
1471 return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001472}
1473
1474
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001475int HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
1476 HBasicBlock* dominator, HBasicBlock* dominated) {
1477 int side_effects = 0;
1478 for (int i = 0; i < dominated->predecessors()->length(); ++i) {
1479 HBasicBlock* block = dominated->predecessors()->at(i);
1480 if (dominator->block_id() < block->block_id() &&
1481 block->block_id() < dominated->block_id() &&
1482 visited_on_paths_.Add(block->block_id())) {
1483 side_effects |= block_side_effects_[block->block_id()];
1484 side_effects |= CollectSideEffectsOnPathsToDominatedBlock(
1485 dominator, block);
1486 }
1487 }
1488 return side_effects;
1489}
1490
1491
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001492void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001493 TraceGVN("Analyzing block B%d%s\n",
1494 block->block_id(),
1495 block->IsLoopHeader() ? " (loop header)" : "");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001496
1497 // If this is a loop header kill everything killed by the loop.
1498 if (block->IsLoopHeader()) {
1499 map->Kill(loop_side_effects_[block->block_id()]);
1500 }
1501
1502 // Go through all instructions of the current block.
1503 HInstruction* instr = block->first();
1504 while (instr != NULL) {
1505 HInstruction* next = instr->next();
1506 int flags = (instr->flags() & HValue::ChangesFlagsMask());
1507 if (flags != 0) {
1508 ASSERT(!instr->CheckFlag(HValue::kUseGVN));
1509 // Clear all instructions in the map that are affected by side effects.
1510 map->Kill(flags);
1511 TraceGVN("Instruction %d kills\n", instr->id());
1512 } else if (instr->CheckFlag(HValue::kUseGVN)) {
1513 HValue* other = map->Lookup(instr);
1514 if (other != NULL) {
1515 ASSERT(instr->Equals(other) && other->Equals(instr));
1516 TraceGVN("Replacing value %d (%s) with value %d (%s)\n",
1517 instr->id(),
1518 instr->Mnemonic(),
1519 other->id(),
1520 other->Mnemonic());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001521 instr->DeleteAndReplaceWith(other);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001522 } else {
1523 map->Add(instr);
1524 }
1525 }
1526 instr = next;
1527 }
1528
1529 // Recursively continue analysis for all immediately dominated blocks.
1530 int length = block->dominated_blocks()->length();
1531 for (int i = 0; i < length; ++i) {
1532 HBasicBlock* dominated = block->dominated_blocks()->at(i);
1533 // No need to copy the map for the last child in the dominator tree.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001534 HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001535
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001536 // Kill everything killed on any path between this block and the
1537 // dominated block.
1538 // We don't have to traverse these paths if the value map is
1539 // already empty.
1540 // If the range of block ids (block_id, dominated_id) is empty
1541 // there are no such paths.
1542 if (!successor_map->IsEmpty() &&
1543 block->block_id() + 1 < dominated->block_id()) {
1544 visited_on_paths_.Clear();
1545 successor_map->Kill(CollectSideEffectsOnPathsToDominatedBlock(block,
1546 dominated));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001547 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001548 AnalyzeBlock(dominated, successor_map);
1549 }
1550}
1551
1552
1553class HInferRepresentation BASE_EMBEDDED {
1554 public:
1555 explicit HInferRepresentation(HGraph* graph)
1556 : graph_(graph), worklist_(8), in_worklist_(graph->GetMaximumValueID()) {}
1557
1558 void Analyze();
1559
1560 private:
1561 Representation TryChange(HValue* current);
1562 void AddToWorklist(HValue* current);
1563 void InferBasedOnInputs(HValue* current);
1564 void AddDependantsToWorklist(HValue* current);
1565 void InferBasedOnUses(HValue* current);
1566
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001567 Zone* zone() { return graph_->zone(); }
1568
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001569 HGraph* graph_;
1570 ZoneList<HValue*> worklist_;
1571 BitVector in_worklist_;
1572};
1573
1574
1575void HInferRepresentation::AddToWorklist(HValue* current) {
1576 if (current->representation().IsSpecialization()) return;
1577 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
1578 if (in_worklist_.Contains(current->id())) return;
1579 worklist_.Add(current);
1580 in_worklist_.Add(current->id());
1581}
1582
1583
1584// This method tries to specialize the representation type of the value
1585// given as a parameter. The value is asked to infer its representation type
1586// based on its inputs. If the inferred type is more specialized, then this
1587// becomes the new representation type of the node.
1588void HInferRepresentation::InferBasedOnInputs(HValue* current) {
1589 Representation r = current->representation();
1590 if (r.IsSpecialization()) return;
1591 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
1592 Representation inferred = current->InferredRepresentation();
1593 if (inferred.IsSpecialization()) {
1594 current->ChangeRepresentation(inferred);
1595 AddDependantsToWorklist(current);
1596 }
1597}
1598
1599
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001600void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
1601 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1602 AddToWorklist(it.value());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001603 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001604 for (int i = 0; i < value->OperandCount(); ++i) {
1605 AddToWorklist(value->OperandAt(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001606 }
1607}
1608
1609
1610// This method calculates whether specializing the representation of the value
1611// given as the parameter has a benefit in terms of less necessary type
1612// conversions. If there is a benefit, then the representation of the value is
1613// specialized.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001614void HInferRepresentation::InferBasedOnUses(HValue* value) {
1615 Representation r = value->representation();
1616 if (r.IsSpecialization() || value->HasNoUses()) return;
1617 ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
1618 Representation new_rep = TryChange(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001619 if (!new_rep.IsNone()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001620 if (!value->representation().Equals(new_rep)) {
1621 value->ChangeRepresentation(new_rep);
1622 AddDependantsToWorklist(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001623 }
1624 }
1625}
1626
1627
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001628Representation HInferRepresentation::TryChange(HValue* value) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001629 // Array of use counts for each representation.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001630 int use_count[Representation::kNumRepresentations] = { 0 };
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001631
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001632 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1633 HValue* use = it.value();
1634 Representation rep = use->RequiredInputRepresentation(it.index());
1635 if (rep.IsNone()) continue;
1636 if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
1637 ++use_count[rep.kind()];
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001638 }
1639 int tagged_count = use_count[Representation::kTagged];
1640 int double_count = use_count[Representation::kDouble];
1641 int int32_count = use_count[Representation::kInteger32];
1642 int non_tagged_count = double_count + int32_count;
1643
1644 // If a non-loop phi has tagged uses, don't convert it to untagged.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001645 if (value->IsPhi() && !value->block()->IsLoopHeader()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001646 if (tagged_count > 0) return Representation::None();
1647 }
1648
1649 if (non_tagged_count >= tagged_count) {
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001650 if (int32_count > 0) {
1651 if (!value->IsPhi() || value->IsConvertibleToInteger()) {
1652 return Representation::Integer32();
1653 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001654 }
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001655 if (double_count > 0) return Representation::Double();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001656 }
1657 return Representation::None();
1658}
1659
1660
1661void HInferRepresentation::Analyze() {
1662 HPhase phase("Infer representations", graph_);
1663
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001664 // (1) Initialize bit vectors and count real uses. Each phi gets a
1665 // bit-vector of length <number of phis>.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001666 const ZoneList<HPhi*>* phi_list = graph_->phi_list();
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001667 int phi_count = phi_list->length();
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001668 ZoneList<BitVector*> connected_phis(phi_count);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001669 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001670 phi_list->at(i)->InitRealUses(i);
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001671 BitVector* connected_set = new(zone()) BitVector(phi_count);
1672 connected_set->Add(i);
1673 connected_phis.Add(connected_set);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001674 }
1675
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001676 // (2) Do a fixed point iteration to find the set of connected phis. A
1677 // phi is connected to another phi if its value is used either directly or
1678 // indirectly through a transitive closure of the def-use relation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001679 bool change = true;
1680 while (change) {
1681 change = false;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001682 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001683 HPhi* phi = phi_list->at(i);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001684 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1685 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001686 if (use->IsPhi()) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001687 int id = HPhi::cast(use)->phi_id();
1688 change = change ||
1689 connected_phis[i]->UnionIsChanged(*connected_phis[id]);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001690 }
1691 }
1692 }
1693 }
1694
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001695 // (3) Sum up the non-phi use counts of all connected phis. Don't include
1696 // the non-phi uses of the phi itself.
1697 for (int i = 0; i < phi_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001698 HPhi* phi = phi_list->at(i);
1699 for (BitVector::Iterator it(connected_phis.at(i));
1700 !it.Done();
1701 it.Advance()) {
1702 int index = it.Current();
1703 if (index != i) {
1704 HPhi* it_use = phi_list->at(it.Current());
1705 phi->AddNonPhiUsesFrom(it_use);
1706 }
1707 }
1708 }
1709
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001710 // (4) Compute phis that definitely can't be converted to integer
1711 // without deoptimization and mark them to avoid unnecessary deoptimization.
1712 change = true;
1713 while (change) {
1714 change = false;
1715 for (int i = 0; i < phi_count; ++i) {
1716 HPhi* phi = phi_list->at(i);
1717 for (int j = 0; j < phi->OperandCount(); ++j) {
1718 if (phi->IsConvertibleToInteger() &&
1719 !phi->OperandAt(j)->IsConvertibleToInteger()) {
1720 phi->set_is_convertible_to_integer(false);
1721 change = true;
1722 break;
1723 }
1724 }
1725 }
1726 }
1727
1728
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001729 for (int i = 0; i < graph_->blocks()->length(); ++i) {
1730 HBasicBlock* block = graph_->blocks()->at(i);
1731 const ZoneList<HPhi*>* phis = block->phis();
1732 for (int j = 0; j < phis->length(); ++j) {
1733 AddToWorklist(phis->at(j));
1734 }
1735
1736 HInstruction* current = block->first();
1737 while (current != NULL) {
1738 AddToWorklist(current);
1739 current = current->next();
1740 }
1741 }
1742
1743 while (!worklist_.is_empty()) {
1744 HValue* current = worklist_.RemoveLast();
1745 in_worklist_.Remove(current->id());
1746 InferBasedOnInputs(current);
1747 InferBasedOnUses(current);
1748 }
1749}
1750
1751
1752void HGraph::InitializeInferredTypes() {
1753 HPhase phase("Inferring types", this);
1754 InitializeInferredTypes(0, this->blocks_.length() - 1);
1755}
1756
1757
1758void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
1759 for (int i = from_inclusive; i <= to_inclusive; ++i) {
1760 HBasicBlock* block = blocks_[i];
1761
1762 const ZoneList<HPhi*>* phis = block->phis();
1763 for (int j = 0; j < phis->length(); j++) {
1764 phis->at(j)->UpdateInferredType();
1765 }
1766
1767 HInstruction* current = block->first();
1768 while (current != NULL) {
1769 current->UpdateInferredType();
1770 current = current->next();
1771 }
1772
1773 if (block->IsLoopHeader()) {
1774 HBasicBlock* last_back_edge =
1775 block->loop_information()->GetLastBackEdge();
1776 InitializeInferredTypes(i + 1, last_back_edge->block_id());
1777 // Skip all blocks already processed by the recursive call.
1778 i = last_back_edge->block_id();
1779 // Update phis of the loop header now after the whole loop body is
1780 // guaranteed to be processed.
1781 ZoneList<HValue*> worklist(block->phis()->length());
1782 for (int j = 0; j < block->phis()->length(); ++j) {
1783 worklist.Add(block->phis()->at(j));
1784 }
1785 InferTypes(&worklist);
1786 }
1787 }
1788}
1789
1790
1791void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
1792 HValue* current = value;
1793 while (current != NULL) {
1794 if (visited->Contains(current->id())) return;
1795
1796 // For phis, we must propagate the check to all of its inputs.
1797 if (current->IsPhi()) {
1798 visited->Add(current->id());
1799 HPhi* phi = HPhi::cast(current);
1800 for (int i = 0; i < phi->OperandCount(); ++i) {
1801 PropagateMinusZeroChecks(phi->OperandAt(i), visited);
1802 }
1803 break;
1804 }
1805
1806 // For multiplication and division, we must propagate to the left and
1807 // the right side.
1808 if (current->IsMul()) {
1809 HMul* mul = HMul::cast(current);
1810 mul->EnsureAndPropagateNotMinusZero(visited);
1811 PropagateMinusZeroChecks(mul->left(), visited);
1812 PropagateMinusZeroChecks(mul->right(), visited);
1813 } else if (current->IsDiv()) {
1814 HDiv* div = HDiv::cast(current);
1815 div->EnsureAndPropagateNotMinusZero(visited);
1816 PropagateMinusZeroChecks(div->left(), visited);
1817 PropagateMinusZeroChecks(div->right(), visited);
1818 }
1819
1820 current = current->EnsureAndPropagateNotMinusZero(visited);
1821 }
1822}
1823
1824
1825void HGraph::InsertRepresentationChangeForUse(HValue* value,
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001826 HValue* use_value,
1827 int use_index,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001828 Representation to) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001829 // Insert the representation change right before its use. For phi-uses we
1830 // insert at the end of the corresponding predecessor.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001831 HInstruction* next = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001832 if (use_value->IsPhi()) {
1833 next = use_value->block()->predecessors()->at(use_index)->end();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00001834 } else {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001835 next = HInstruction::cast(use_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001836 }
1837
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001838 // For constants we try to make the representation change at compile
1839 // time. When a representation change is not possible without loss of
1840 // information we treat constants like normal instructions and insert the
1841 // change instructions for them.
1842 HInstruction* new_value = NULL;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001843 bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001844 bool deoptimize_on_undefined =
1845 use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001846 if (value->IsConstant()) {
1847 HConstant* constant = HConstant::cast(value);
1848 // Try to create a new copy of the constant with the new representation.
1849 new_value = is_truncating
1850 ? constant->CopyToTruncatedInt32()
1851 : constant->CopyToRepresentation(to);
1852 }
1853
1854 if (new_value == NULL) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001855 new_value = new(zone()) HChange(value, value->representation(), to,
1856 is_truncating, deoptimize_on_undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001857 }
1858
1859 new_value->InsertBefore(next);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001860 use_value->SetOperandAt(use_index, new_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001861}
1862
1863
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001864void HGraph::InsertRepresentationChangesForValue(HValue* value) {
1865 Representation r = value->representation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001866 if (r.IsNone()) return;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001867 if (value->HasNoUses()) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001868
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001869 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
1870 HValue* use_value = it.value();
1871 int use_index = it.index();
1872 Representation req = use_value->RequiredInputRepresentation(use_index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001873 if (req.IsNone() || req.Equals(r)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001874 InsertRepresentationChangeForUse(value, use_value, use_index, req);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001875 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001876 if (value->HasNoUses()) {
1877 ASSERT(value->IsConstant());
1878 value->DeleteAndReplaceWith(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001879 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00001880
1881 // The only purpose of a HForceRepresentation is to represent the value
1882 // after the (possible) HChange instruction. We make it disappear.
1883 if (value->IsForceRepresentation()) {
1884 value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
1885 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001886}
1887
1888
1889void HGraph::InsertRepresentationChanges() {
1890 HPhase phase("Insert representation changes", this);
1891
1892
1893 // Compute truncation flag for phis: Initially assume that all
1894 // int32-phis allow truncation and iteratively remove the ones that
1895 // are used in an operation that does not allow a truncating
1896 // conversion.
1897 // TODO(fschneider): Replace this with a worklist-based iteration.
1898 for (int i = 0; i < phi_list()->length(); i++) {
1899 HPhi* phi = phi_list()->at(i);
1900 if (phi->representation().IsInteger32()) {
1901 phi->SetFlag(HValue::kTruncatingToInt32);
1902 }
1903 }
1904 bool change = true;
1905 while (change) {
1906 change = false;
1907 for (int i = 0; i < phi_list()->length(); i++) {
1908 HPhi* phi = phi_list()->at(i);
1909 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001910 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1911 HValue* use = it.value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001912 if (!use->CheckFlag(HValue::kTruncatingToInt32)) {
1913 phi->ClearFlag(HValue::kTruncatingToInt32);
1914 change = true;
1915 break;
1916 }
1917 }
1918 }
1919 }
1920
1921 for (int i = 0; i < blocks_.length(); ++i) {
1922 // Process phi instructions first.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001923 const ZoneList<HPhi*>* phis = blocks_[i]->phis();
1924 for (int j = 0; j < phis->length(); j++) {
1925 InsertRepresentationChangesForValue(phis->at(j));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001926 }
1927
1928 // Process normal instructions.
1929 HInstruction* current = blocks_[i]->first();
1930 while (current != NULL) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001931 InsertRepresentationChangesForValue(current);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001932 current = current->next();
1933 }
1934 }
1935}
1936
1937
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001938void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) {
1939 if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return;
1940 phi->SetFlag(HValue::kDeoptimizeOnUndefined);
1941 for (int i = 0; i < phi->OperandCount(); ++i) {
1942 HValue* input = phi->OperandAt(i);
1943 if (input->IsPhi()) {
1944 RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi::cast(input));
1945 }
1946 }
1947}
1948
1949
1950void HGraph::MarkDeoptimizeOnUndefined() {
1951 HPhase phase("MarkDeoptimizeOnUndefined", this);
1952 // Compute DeoptimizeOnUndefined flag for phis.
1953 // Any phi that can reach a use with DeoptimizeOnUndefined set must
1954 // have DeoptimizeOnUndefined set. Currently only HCompare, with
1955 // double input representation, has this flag set.
1956 // The flag is used by HChange tagged->double, which must deoptimize
1957 // if one of its uses has this flag set.
1958 for (int i = 0; i < phi_list()->length(); i++) {
1959 HPhi* phi = phi_list()->at(i);
1960 if (phi->representation().IsDouble()) {
1961 for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
1962 if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
1963 RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
1964 break;
1965 }
1966 }
1967 }
1968 }
1969}
1970
1971
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001972void HGraph::ComputeMinusZeroChecks() {
1973 BitVector visited(GetMaximumValueID());
1974 for (int i = 0; i < blocks_.length(); ++i) {
1975 for (HInstruction* current = blocks_[i]->first();
1976 current != NULL;
1977 current = current->next()) {
1978 if (current->IsChange()) {
1979 HChange* change = HChange::cast(current);
1980 // Propagate flags for negative zero checks upwards from conversions
1981 // int32-to-tagged and int32-to-double.
1982 Representation from = change->value()->representation();
1983 ASSERT(from.Equals(change->from()));
1984 if (from.IsInteger32()) {
1985 ASSERT(change->to().IsTagged() || change->to().IsDouble());
1986 ASSERT(visited.IsEmpty());
1987 PropagateMinusZeroChecks(change->value(), &visited);
1988 visited.Clear();
1989 }
1990 }
1991 }
1992 }
1993}
1994
1995
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001996// Implementation of utility class to encapsulate the translation state for
1997// a (possibly inlined) function.
1998FunctionState::FunctionState(HGraphBuilder* owner,
1999 CompilationInfo* info,
2000 TypeFeedbackOracle* oracle)
2001 : owner_(owner),
2002 compilation_info_(info),
2003 oracle_(oracle),
2004 call_context_(NULL),
2005 function_return_(NULL),
2006 test_context_(NULL),
2007 outer_(owner->function_state()) {
2008 if (outer_ != NULL) {
2009 // State for an inline function.
2010 if (owner->ast_context()->IsTest()) {
2011 HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
2012 HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
2013 if_true->MarkAsInlineReturnTarget();
2014 if_false->MarkAsInlineReturnTarget();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002015 Expression* cond = TestContext::cast(owner->ast_context())->condition();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002016 // The AstContext constructor pushed on the context stack. This newed
2017 // instance is the reason that AstContext can't be BASE_EMBEDDED.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002018 test_context_ = new TestContext(owner, cond, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002019 } else {
2020 function_return_ = owner->graph()->CreateBasicBlock();
2021 function_return()->MarkAsInlineReturnTarget();
2022 }
2023 // Set this after possibly allocating a new TestContext above.
2024 call_context_ = owner->ast_context();
2025 }
2026
2027 // Push on the state stack.
2028 owner->set_function_state(this);
2029}
2030
2031
2032FunctionState::~FunctionState() {
2033 delete test_context_;
2034 owner_->set_function_state(outer_);
2035}
2036
2037
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002038// Implementation of utility classes to represent an expression's context in
2039// the AST.
2040AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002041 : owner_(owner),
2042 kind_(kind),
2043 outer_(owner->ast_context()),
2044 for_typeof_(false) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002045 owner->set_ast_context(this); // Push.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002046#ifdef DEBUG
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002047 original_length_ = owner->environment()->length();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002048#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002049}
2050
2051
2052AstContext::~AstContext() {
2053 owner_->set_ast_context(outer_); // Pop.
2054}
2055
2056
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002057EffectContext::~EffectContext() {
2058 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002059 owner()->current_block() == NULL ||
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002060 owner()->environment()->length() == original_length_);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002061}
2062
2063
2064ValueContext::~ValueContext() {
2065 ASSERT(owner()->HasStackOverflow() ||
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002066 owner()->current_block() == NULL ||
lrn@chromium.org5d00b602011-01-05 09:51:43 +00002067 owner()->environment()->length() == original_length_ + 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002068}
2069
2070
2071void EffectContext::ReturnValue(HValue* value) {
2072 // The value is simply ignored.
2073}
2074
2075
2076void ValueContext::ReturnValue(HValue* value) {
2077 // The value is tracked in the bailout environment, and communicated
2078 // through the environment as the result of the expression.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002079 if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
2080 owner()->Bailout("bad value context for arguments value");
2081 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002082 owner()->Push(value);
2083}
2084
2085
2086void TestContext::ReturnValue(HValue* value) {
2087 BuildBranch(value);
2088}
2089
2090
2091void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
2092 owner()->AddInstruction(instr);
2093 if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
2094}
2095
2096
2097void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002098 if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
2099 owner()->Bailout("bad value context for arguments object value");
2100 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002101 owner()->AddInstruction(instr);
2102 owner()->Push(instr);
2103 if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
2104}
2105
2106
2107void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) {
2108 HGraphBuilder* builder = owner();
2109 builder->AddInstruction(instr);
2110 // We expect a simulate after every expression with side effects, though
2111 // this one isn't actually needed (and wouldn't work if it were targeted).
2112 if (instr->HasSideEffects()) {
2113 builder->Push(instr);
2114 builder->AddSimulate(ast_id);
2115 builder->Pop();
2116 }
2117 BuildBranch(instr);
2118}
2119
2120
2121void TestContext::BuildBranch(HValue* value) {
2122 // We expect the graph to be in edge-split form: there is no edge that
2123 // connects a branch node to a join node. We conservatively ensure that
2124 // property by always adding an empty block on the outgoing edges of this
2125 // branch.
2126 HGraphBuilder* builder = owner();
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002127 if (value->CheckFlag(HValue::kIsArguments)) {
2128 builder->Bailout("arguments object value in a test context");
2129 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002130 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
2131 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002132 HTest* test = new(zone()) HTest(value, empty_true, empty_false);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002133 builder->current_block()->Finish(test);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002134
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002135 empty_true->Goto(if_true(), false);
2136 empty_false->Goto(if_false(), false);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002137 builder->set_current_block(NULL);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002138}
2139
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002140
2141// HGraphBuilder infrastructure for bailing out and checking bailouts.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002142#define CHECK_BAILOUT(call) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002143 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002144 call; \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002145 if (HasStackOverflow()) return; \
2146 } while (false)
2147
2148
danno@chromium.org160a7b02011-04-18 15:51:38 +00002149#define CHECK_ALIVE(call) \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002150 do { \
danno@chromium.org160a7b02011-04-18 15:51:38 +00002151 call; \
2152 if (HasStackOverflow() || current_block() == NULL) return; \
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002153 } while (false)
2154
2155
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002156void HGraphBuilder::Bailout(const char* reason) {
2157 if (FLAG_trace_bailout) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002158 SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString());
2159 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002160 }
2161 SetStackOverflow();
2162}
2163
2164
2165void HGraphBuilder::VisitForEffect(Expression* expr) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002166 EffectContext for_effect(this);
2167 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002168}
2169
2170
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002171void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
2172 ValueContext for_value(this, flag);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002173 Visit(expr);
2174}
2175
2176
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002177void HGraphBuilder::VisitForTypeOf(Expression* expr) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00002178 ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002179 for_value.set_for_typeof(true);
2180 Visit(expr);
2181}
2182
2183
2184
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002185void HGraphBuilder::VisitForControl(Expression* expr,
2186 HBasicBlock* true_block,
2187 HBasicBlock* false_block) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002188 TestContext for_test(this, expr, true_block, false_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002189 Visit(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002190}
2191
2192
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002193void HGraphBuilder::VisitArgument(Expression* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002194 CHECK_ALIVE(VisitForValue(expr));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002195 Push(AddInstruction(new(zone()) HPushArgument(Pop())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002196}
2197
2198
2199void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
2200 for (int i = 0; i < arguments->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002201 CHECK_ALIVE(VisitArgument(arguments->at(i)));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002202 }
2203}
2204
2205
2206void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
2207 for (int i = 0; i < exprs->length(); ++i) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002208 CHECK_ALIVE(VisitForValue(exprs->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002209 }
2210}
2211
2212
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002213HGraph* HGraphBuilder::CreateGraph() {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002214 graph_ = new(zone()) HGraph(info());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002215 if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
2216
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002217 {
2218 HPhase phase("Block building");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002219 current_block_ = graph()->entry_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002220
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002221 Scope* scope = info()->scope();
2222 if (scope->HasIllegalRedeclaration()) {
2223 Bailout("function with illegal redeclaration");
2224 return NULL;
2225 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002226 SetupScope(scope);
2227 VisitDeclarations(scope->declarations());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002228 AddInstruction(new(zone()) HStackCheck());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002229
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002230 // Add an edge to the body entry. This is warty: the graph's start
2231 // environment will be used by the Lithium translation as the initial
2232 // environment on graph entry, but it has now been mutated by the
2233 // Hydrogen translation of the instructions in the start block. This
2234 // environment uses values which have not been defined yet. These
2235 // Hydrogen instructions will then be replayed by the Lithium
2236 // translation, so they cannot have an environment effect. The edge to
2237 // the body's entry block (along with some special logic for the start
2238 // block in HInstruction::InsertAfter) seals the start block from
2239 // getting unwanted instructions inserted.
2240 //
2241 // TODO(kmillikin): Fix this. Stop mutating the initial environment.
2242 // Make the Hydrogen instructions in the initial block into Hydrogen
2243 // values (but not instructions), present in the initial environment and
2244 // not replayed by the Lithium translation.
2245 HEnvironment* initial_env = environment()->CopyWithoutHistory();
2246 HBasicBlock* body_entry = CreateBasicBlock(initial_env);
2247 current_block()->Goto(body_entry);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002248 body_entry->SetJoinId(AstNode::kFunctionEntryId);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002249 set_current_block(body_entry);
2250 VisitStatements(info()->function()->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002251 if (HasStackOverflow()) return NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002252
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002253 if (current_block() != NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002254 HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002255 current_block()->FinishExit(instr);
2256 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002257 }
2258 }
2259
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002260 graph()->OrderBlocks();
2261 graph()->AssignDominators();
2262 graph()->EliminateRedundantPhis();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002263 if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002264 if (!graph()->CollectPhis()) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002265 Bailout("Unsupported phi-use");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002266 return NULL;
2267 }
2268
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002269 HInferRepresentation rep(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002270 rep.Analyze();
2271
2272 if (FLAG_use_range) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002273 HRangeAnalysis rangeAnalysis(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002274 rangeAnalysis.Analyze();
2275 }
2276
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002277 graph()->InitializeInferredTypes();
2278 graph()->Canonicalize();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002279 graph()->MarkDeoptimizeOnUndefined();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002280 graph()->InsertRepresentationChanges();
2281 graph()->ComputeMinusZeroChecks();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002282
2283 // Eliminate redundant stack checks on backwards branches.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002284 HStackCheckEliminator sce(graph());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002285 sce.Process();
2286
2287 // Perform common subexpression elimination and loop-invariant code motion.
2288 if (FLAG_use_gvn) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002289 HPhase phase("Global value numbering", graph());
2290 HGlobalValueNumberer gvn(graph(), info());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002291 gvn.Analyze();
2292 }
2293
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002294 // Replace the results of check instructions with the original value, if the
2295 // result is used. This is safe now, since we don't do code motion after this
2296 // point. It enables better register allocation since the value produced by
2297 // check instructions is really a copy of the original value.
2298 graph()->ReplaceCheckedValues();
2299
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002300 return graph();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002301}
2302
2303
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002304void HGraph::ReplaceCheckedValues() {
2305 HPhase phase("Replace checked values", this);
2306 for (int i = 0; i < blocks()->length(); ++i) {
2307 HInstruction* instr = blocks()->at(i)->first();
2308 while (instr != NULL) {
2309 if (instr->IsBoundsCheck()) {
2310 // Replace all uses of the checked value with the original input.
2311 ASSERT(instr->UseCount() > 0);
2312 instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index());
2313 }
2314 instr = instr->next();
2315 }
2316 }
2317}
2318
2319
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002320HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002321 ASSERT(current_block() != NULL);
2322 current_block()->AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002323 return instr;
2324}
2325
2326
2327void HGraphBuilder::AddSimulate(int id) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002328 ASSERT(current_block() != NULL);
2329 current_block()->AddSimulate(id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002330}
2331
2332
2333void HGraphBuilder::AddPhi(HPhi* instr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002334 ASSERT(current_block() != NULL);
2335 current_block()->AddPhi(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002336}
2337
2338
2339void HGraphBuilder::PushAndAdd(HInstruction* instr) {
2340 Push(instr);
2341 AddInstruction(instr);
2342}
2343
2344
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002345template <int V>
2346HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002347 int count = call->argument_count();
2348 ZoneList<HValue*> arguments(count);
2349 for (int i = 0; i < count; ++i) {
2350 arguments.Add(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002351 }
2352
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002353 while (!arguments.is_empty()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002354 AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002355 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002356 return call;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002357}
2358
2359
2360void HGraphBuilder::SetupScope(Scope* scope) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002361 HConstant* undefined_constant = new(zone()) HConstant(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002362 isolate()->factory()->undefined_value(), Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002363 AddInstruction(undefined_constant);
2364 graph_->set_undefined_constant(undefined_constant);
2365
2366 // Set the initial values of parameters including "this". "This" has
2367 // parameter index 0.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002368 ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
2369
2370 for (int i = 0; i < environment()->parameter_count(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002371 HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002372 environment()->Bind(i, parameter);
2373 }
2374
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002375 // First special is HContext.
2376 HInstruction* context = AddInstruction(new(zone()) HContext);
2377 environment()->BindContext(context);
2378
2379 // Initialize specials and locals to undefined.
2380 for (int i = environment()->parameter_count() + 1;
2381 i < environment()->length();
2382 ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002383 environment()->Bind(i, undefined_constant);
2384 }
2385
2386 // Handle the arguments and arguments shadow variables specially (they do
2387 // not have declarations).
2388 if (scope->arguments() != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002389 if (!scope->arguments()->IsStackAllocated() ||
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002390 (scope->arguments_shadow() != NULL &&
2391 !scope->arguments_shadow()->IsStackAllocated())) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002392 return Bailout("context-allocated arguments");
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002393 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002394 HArgumentsObject* object = new(zone()) HArgumentsObject;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002395 AddInstruction(object);
2396 graph()->SetArgumentsObject(object);
2397 environment()->Bind(scope->arguments(), object);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002398 if (scope->arguments_shadow() != NULL) {
2399 environment()->Bind(scope->arguments_shadow(), object);
2400 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002401 }
2402}
2403
2404
2405void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
2406 for (int i = 0; i < statements->length(); i++) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002407 CHECK_ALIVE(Visit(statements->at(i)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002408 }
2409}
2410
2411
2412HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
2413 HBasicBlock* b = graph()->CreateBasicBlock();
2414 b->SetInitialEnvironment(env);
2415 return b;
2416}
2417
2418
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002419HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002420 HBasicBlock* header = graph()->CreateBasicBlock();
2421 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
2422 header->SetInitialEnvironment(entry_env);
2423 header->AttachLoopInformation();
2424 return header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002425}
2426
2427
2428void HGraphBuilder::VisitBlock(Block* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002429 ASSERT(!HasStackOverflow());
2430 ASSERT(current_block() != NULL);
2431 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002432 BreakAndContinueInfo break_info(stmt);
2433 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002434 CHECK_BAILOUT(VisitStatements(stmt->statements()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002435 }
2436 HBasicBlock* break_block = break_info.break_block();
2437 if (break_block != NULL) {
2438 if (current_block() != NULL) current_block()->Goto(break_block);
2439 break_block->SetJoinId(stmt->ExitId());
2440 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002441 }
2442}
2443
2444
2445void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002446 ASSERT(!HasStackOverflow());
2447 ASSERT(current_block() != NULL);
2448 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002449 VisitForEffect(stmt->expression());
2450}
2451
2452
2453void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002454 ASSERT(!HasStackOverflow());
2455 ASSERT(current_block() != NULL);
2456 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002457}
2458
2459
2460void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002461 ASSERT(!HasStackOverflow());
2462 ASSERT(current_block() != NULL);
2463 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002464 if (stmt->condition()->ToBooleanIsTrue()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002465 AddSimulate(stmt->ThenId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002466 Visit(stmt->then_statement());
2467 } else if (stmt->condition()->ToBooleanIsFalse()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002468 AddSimulate(stmt->ElseId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002469 Visit(stmt->else_statement());
2470 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002471 HBasicBlock* cond_true = graph()->CreateBasicBlock();
2472 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002473 CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002474
danno@chromium.org160a7b02011-04-18 15:51:38 +00002475 if (cond_true->HasPredecessor()) {
2476 cond_true->SetJoinId(stmt->ThenId());
2477 set_current_block(cond_true);
2478 CHECK_BAILOUT(Visit(stmt->then_statement()));
2479 cond_true = current_block();
2480 } else {
2481 cond_true = NULL;
2482 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002483
danno@chromium.org160a7b02011-04-18 15:51:38 +00002484 if (cond_false->HasPredecessor()) {
2485 cond_false->SetJoinId(stmt->ElseId());
2486 set_current_block(cond_false);
2487 CHECK_BAILOUT(Visit(stmt->else_statement()));
2488 cond_false = current_block();
2489 } else {
2490 cond_false = NULL;
2491 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002492
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002493 HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002494 set_current_block(join);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002495 }
2496}
2497
2498
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002499HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
2500 BreakableStatement* stmt,
2501 BreakType type) {
2502 BreakAndContinueScope* current = this;
2503 while (current != NULL && current->info()->target() != stmt) {
2504 current = current->next();
2505 }
2506 ASSERT(current != NULL); // Always found (unless stack is malformed).
2507 HBasicBlock* block = NULL;
2508 switch (type) {
2509 case BREAK:
2510 block = current->info()->break_block();
2511 if (block == NULL) {
2512 block = current->owner()->graph()->CreateBasicBlock();
2513 current->info()->set_break_block(block);
2514 }
2515 break;
2516
2517 case CONTINUE:
2518 block = current->info()->continue_block();
2519 if (block == NULL) {
2520 block = current->owner()->graph()->CreateBasicBlock();
2521 current->info()->set_continue_block(block);
2522 }
2523 break;
2524 }
2525
2526 return block;
2527}
2528
2529
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002530void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002531 ASSERT(!HasStackOverflow());
2532 ASSERT(current_block() != NULL);
2533 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002534 HBasicBlock* continue_block = break_scope()->Get(stmt->target(), CONTINUE);
2535 current_block()->Goto(continue_block);
2536 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002537}
2538
2539
2540void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002541 ASSERT(!HasStackOverflow());
2542 ASSERT(current_block() != NULL);
2543 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002544 HBasicBlock* break_block = break_scope()->Get(stmt->target(), BREAK);
2545 current_block()->Goto(break_block);
2546 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002547}
2548
2549
2550void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002551 ASSERT(!HasStackOverflow());
2552 ASSERT(current_block() != NULL);
2553 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002554 AstContext* context = call_context();
2555 if (context == NULL) {
2556 // Not an inlined return, so an actual one.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002557 CHECK_ALIVE(VisitForValue(stmt->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002558 HValue* result = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002559 current_block()->FinishExit(new(zone()) HReturn(result));
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002560 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002561 } else {
2562 // Return from an inlined function, visit the subexpression in the
2563 // expression context of the call.
2564 if (context->IsTest()) {
2565 TestContext* test = TestContext::cast(context);
2566 VisitForControl(stmt->expression(),
2567 test->if_true(),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002568 test->if_false());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002569 } else if (context->IsEffect()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002570 CHECK_ALIVE(VisitForEffect(stmt->expression()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002571 current_block()->Goto(function_return(), false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002572 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002573 ASSERT(context->IsValue());
danno@chromium.org160a7b02011-04-18 15:51:38 +00002574 CHECK_ALIVE(VisitForValue(stmt->expression()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002575 HValue* return_value = environment()->Pop();
2576 current_block()->AddLeaveInlined(return_value, function_return());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002577 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002578 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002579 }
2580}
2581
2582
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002583void HGraphBuilder::VisitEnterWithContextStatement(
2584 EnterWithContextStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002585 ASSERT(!HasStackOverflow());
2586 ASSERT(current_block() != NULL);
2587 ASSERT(current_block()->HasPredecessor());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002588 return Bailout("EnterWithContextStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002589}
2590
2591
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002592void HGraphBuilder::VisitExitContextStatement(ExitContextStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002593 ASSERT(!HasStackOverflow());
2594 ASSERT(current_block() != NULL);
2595 ASSERT(current_block()->HasPredecessor());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002596 return Bailout("ExitContextStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002597}
2598
2599
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002600void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002601 ASSERT(!HasStackOverflow());
2602 ASSERT(current_block() != NULL);
2603 ASSERT(current_block()->HasPredecessor());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002604 // We only optimize switch statements with smi-literal smi comparisons,
2605 // with a bounded number of clauses.
2606 const int kCaseClauseLimit = 128;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002607 ZoneList<CaseClause*>* clauses = stmt->cases();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002608 int clause_count = clauses->length();
2609 if (clause_count > kCaseClauseLimit) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002610 return Bailout("SwitchStatement: too many clauses");
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002611 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002612
danno@chromium.org160a7b02011-04-18 15:51:38 +00002613 CHECK_ALIVE(VisitForValue(stmt->tag()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002614 AddSimulate(stmt->EntryId());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002615 HValue* tag_value = Pop();
2616 HBasicBlock* first_test_block = current_block();
2617
2618 // 1. Build all the tests, with dangling true branches. Unconditionally
2619 // deoptimize if we encounter a non-smi comparison.
2620 for (int i = 0; i < clause_count; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002621 CaseClause* clause = clauses->at(i);
2622 if (clause->is_default()) continue;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002623 if (!clause->label()->IsSmiLiteral()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002624 return Bailout("SwitchStatement: non-literal switch label");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002625 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002626
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002627 // Unconditionally deoptimize on the first non-smi compare.
2628 clause->RecordTypeFeedback(oracle());
2629 if (!clause->IsSmiCompare()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002630 // Finish with deoptimize and add uses of enviroment values to
2631 // account for invisible uses.
2632 current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002633 set_current_block(NULL);
2634 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002635 }
2636
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002637 // Otherwise generate a compare and branch.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002638 CHECK_ALIVE(VisitForValue(clause->label()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002639 HValue* label_value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002640 HCompare* compare =
2641 new(zone()) HCompare(tag_value, label_value, Token::EQ_STRICT);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002642 compare->SetInputRepresentation(Representation::Integer32());
2643 ASSERT(!compare->HasSideEffects());
2644 AddInstruction(compare);
2645 HBasicBlock* body_block = graph()->CreateBasicBlock();
2646 HBasicBlock* next_test_block = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002647 HTest* branch = new(zone()) HTest(compare, body_block, next_test_block);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002648 current_block()->Finish(branch);
2649 set_current_block(next_test_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002650 }
2651
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002652 // Save the current block to use for the default or to join with the
2653 // exit. This block is NULL if we deoptimized.
2654 HBasicBlock* last_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002655
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002656 // 2. Loop over the clauses and the linked list of tests in lockstep,
2657 // translating the clause bodies.
2658 HBasicBlock* curr_test_block = first_test_block;
2659 HBasicBlock* fall_through_block = NULL;
2660 BreakAndContinueInfo break_info(stmt);
2661 { BreakAndContinueScope push(&break_info, this);
2662 for (int i = 0; i < clause_count; ++i) {
2663 CaseClause* clause = clauses->at(i);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002664
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002665 // Identify the block where normal (non-fall-through) control flow
2666 // goes to.
2667 HBasicBlock* normal_block = NULL;
fschneider@chromium.org13da64d2011-05-18 12:07:24 +00002668 if (clause->is_default()) {
2669 if (last_block != NULL) {
2670 normal_block = last_block;
2671 last_block = NULL; // Cleared to indicate we've handled it.
2672 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002673 } else if (!curr_test_block->end()->IsDeoptimize()) {
2674 normal_block = curr_test_block->end()->FirstSuccessor();
2675 curr_test_block = curr_test_block->end()->SecondSuccessor();
2676 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002677
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002678 // Identify a block to emit the body into.
2679 if (normal_block == NULL) {
2680 if (fall_through_block == NULL) {
2681 // (a) Unreachable.
2682 if (clause->is_default()) {
2683 continue; // Might still be reachable clause bodies.
2684 } else {
2685 break;
2686 }
2687 } else {
2688 // (b) Reachable only as fall through.
2689 set_current_block(fall_through_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002690 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002691 } else if (fall_through_block == NULL) {
2692 // (c) Reachable only normally.
2693 set_current_block(normal_block);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002694 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002695 // (d) Reachable both ways.
2696 HBasicBlock* join = CreateJoin(fall_through_block,
2697 normal_block,
2698 clause->EntryId());
2699 set_current_block(join);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002700 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002701
danno@chromium.org160a7b02011-04-18 15:51:38 +00002702 CHECK_BAILOUT(VisitStatements(clause->statements()));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002703 fall_through_block = current_block();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002704 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002705 }
2706
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002707 // Create an up-to-3-way join. Use the break block if it exists since
2708 // it's already a join block.
2709 HBasicBlock* break_block = break_info.break_block();
2710 if (break_block == NULL) {
2711 set_current_block(CreateJoin(fall_through_block,
2712 last_block,
2713 stmt->ExitId()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002714 } else {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002715 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
2716 if (last_block != NULL) last_block->Goto(break_block);
2717 break_block->SetJoinId(stmt->ExitId());
2718 set_current_block(break_block);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002719 }
2720}
2721
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002722
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002723bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002724 return statement->OsrEntryId() == info()->osr_ast_id();
2725}
2726
2727
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002728void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002729 if (!HasOsrEntryAt(statement)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002730
2731 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
2732 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
2733 HValue* true_value = graph()->GetConstantTrue();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002734 HTest* test = new(zone()) HTest(true_value, non_osr_entry, osr_entry);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002735 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002736
2737 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
2738 non_osr_entry->Goto(loop_predecessor);
2739
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002740 set_current_block(osr_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002741 int osr_entry_id = statement->OsrEntryId();
2742 // We want the correct environment at the OsrEntry instruction. Build
2743 // it explicitly. The expression stack should be empty.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002744 ASSERT(environment()->ExpressionStackIsEmpty());
2745 for (int i = 0; i < environment()->length(); ++i) {
2746 HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
2747 AddInstruction(osr_value);
2748 environment()->Bind(i, osr_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002749 }
2750
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002751 AddSimulate(osr_entry_id);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002752 AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002753 HContext* context = new(zone()) HContext;
2754 AddInstruction(context);
2755 environment()->BindContext(context);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002756 current_block()->Goto(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002757 loop_predecessor->SetJoinId(statement->EntryId());
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002758 set_current_block(loop_predecessor);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002759}
2760
2761
2762void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002763 ASSERT(!HasStackOverflow());
2764 ASSERT(current_block() != NULL);
2765 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002766 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002767 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002768 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002769 current_block()->Goto(loop_entry, false);
2770 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002771
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002772 BreakAndContinueInfo break_info(stmt);
2773 { BreakAndContinueScope push(&break_info, this);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002774 CHECK_BAILOUT(Visit(stmt->body()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002775 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002776 HBasicBlock* body_exit =
2777 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002778 HBasicBlock* loop_successor = NULL;
2779 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002780 set_current_block(body_exit);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002781 // The block for a true condition, the actual predecessor block of the
2782 // back edge.
2783 body_exit = graph()->CreateBasicBlock();
2784 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002785 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
2786 if (body_exit->HasPredecessor()) {
2787 body_exit->SetJoinId(stmt->BackEdgeId());
2788 } else {
2789 body_exit = NULL;
2790 }
2791 if (loop_successor->HasPredecessor()) {
2792 loop_successor->SetJoinId(stmt->ExitId());
2793 } else {
2794 loop_successor = NULL;
2795 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002796 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002797 HBasicBlock* loop_exit = CreateLoop(stmt,
2798 loop_entry,
2799 body_exit,
2800 loop_successor,
2801 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002802 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002803}
2804
2805
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002806void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002807 ASSERT(!HasStackOverflow());
2808 ASSERT(current_block() != NULL);
2809 ASSERT(current_block()->HasPredecessor());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002810 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002811 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002812 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002813 current_block()->Goto(loop_entry, false);
2814 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002815
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002816 // If the condition is constant true, do not generate a branch.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002817 HBasicBlock* loop_successor = NULL;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002818 if (!stmt->cond()->ToBooleanIsTrue()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002819 HBasicBlock* body_entry = graph()->CreateBasicBlock();
2820 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002821 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
2822 if (body_entry->HasPredecessor()) {
2823 body_entry->SetJoinId(stmt->BodyId());
2824 set_current_block(body_entry);
2825 }
2826 if (loop_successor->HasPredecessor()) {
2827 loop_successor->SetJoinId(stmt->ExitId());
2828 } else {
2829 loop_successor = NULL;
2830 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002831 }
2832
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002833 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002834 if (current_block() != NULL) {
2835 BreakAndContinueScope push(&break_info, this);
2836 CHECK_BAILOUT(Visit(stmt->body()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002837 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002838 HBasicBlock* body_exit =
2839 JoinContinue(stmt, current_block(), break_info.continue_block());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002840 HBasicBlock* loop_exit = CreateLoop(stmt,
2841 loop_entry,
2842 body_exit,
2843 loop_successor,
2844 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002845 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002846}
2847
2848
2849void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002850 ASSERT(!HasStackOverflow());
2851 ASSERT(current_block() != NULL);
2852 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002853 if (stmt->init() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002854 CHECK_ALIVE(Visit(stmt->init()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002855 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002856 ASSERT(current_block() != NULL);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00002857 PreProcessOsrEntry(stmt);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002858 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002859 current_block()->Goto(loop_entry, false);
2860 set_current_block(loop_entry);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002861
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002862 HBasicBlock* loop_successor = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002863 if (stmt->cond() != NULL) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002864 HBasicBlock* body_entry = graph()->CreateBasicBlock();
2865 loop_successor = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002866 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
2867 if (body_entry->HasPredecessor()) {
2868 body_entry->SetJoinId(stmt->BodyId());
2869 set_current_block(body_entry);
2870 }
2871 if (loop_successor->HasPredecessor()) {
2872 loop_successor->SetJoinId(stmt->ExitId());
2873 } else {
2874 loop_successor = NULL;
2875 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002876 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002877
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002878 BreakAndContinueInfo break_info(stmt);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002879 if (current_block() != NULL) {
2880 BreakAndContinueScope push(&break_info, this);
2881 CHECK_BAILOUT(Visit(stmt->body()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002882 }
2883 HBasicBlock* body_exit =
2884 JoinContinue(stmt, current_block(), break_info.continue_block());
2885
2886 if (stmt->next() != NULL && body_exit != NULL) {
2887 set_current_block(body_exit);
danno@chromium.org160a7b02011-04-18 15:51:38 +00002888 CHECK_BAILOUT(Visit(stmt->next()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002889 body_exit = current_block();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002890 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002891
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002892 HBasicBlock* loop_exit = CreateLoop(stmt,
2893 loop_entry,
2894 body_exit,
2895 loop_successor,
2896 break_info.break_block());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002897 set_current_block(loop_exit);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002898}
2899
2900
2901void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002902 ASSERT(!HasStackOverflow());
2903 ASSERT(current_block() != NULL);
2904 ASSERT(current_block()->HasPredecessor());
2905 return Bailout("ForInStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002906}
2907
2908
2909void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002910 ASSERT(!HasStackOverflow());
2911 ASSERT(current_block() != NULL);
2912 ASSERT(current_block()->HasPredecessor());
2913 return Bailout("TryCatchStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002914}
2915
2916
2917void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002918 ASSERT(!HasStackOverflow());
2919 ASSERT(current_block() != NULL);
2920 ASSERT(current_block()->HasPredecessor());
2921 return Bailout("TryFinallyStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002922}
2923
2924
2925void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002926 ASSERT(!HasStackOverflow());
2927 ASSERT(current_block() != NULL);
2928 ASSERT(current_block()->HasPredecessor());
2929 return Bailout("DebuggerStatement");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002930}
2931
2932
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002933static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
2934 Code* unoptimized_code, FunctionLiteral* expr) {
2935 int start_position = expr->start_position();
2936 RelocIterator it(unoptimized_code);
2937 for (;!it.done(); it.next()) {
2938 RelocInfo* rinfo = it.rinfo();
2939 if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
2940 Object* obj = rinfo->target_object();
2941 if (obj->IsSharedFunctionInfo()) {
2942 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
2943 if (shared->start_position() == start_position) {
2944 return Handle<SharedFunctionInfo>(shared);
2945 }
2946 }
2947 }
2948
2949 return Handle<SharedFunctionInfo>();
2950}
2951
2952
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002953void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002954 ASSERT(!HasStackOverflow());
2955 ASSERT(current_block() != NULL);
2956 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002957 Handle<SharedFunctionInfo> shared_info =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002958 SearchSharedFunctionInfo(info()->shared_info()->code(),
2959 expr);
2960 if (shared_info.is_null()) {
2961 shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
2962 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00002963 // We also have a stack overflow if the recursive compilation did.
2964 if (HasStackOverflow()) return;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002965 HFunctionLiteral* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00002966 new(zone()) HFunctionLiteral(shared_info, expr->pretenure());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002967 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002968}
2969
2970
2971void HGraphBuilder::VisitSharedFunctionInfoLiteral(
2972 SharedFunctionInfoLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002973 ASSERT(!HasStackOverflow());
2974 ASSERT(current_block() != NULL);
2975 ASSERT(current_block()->HasPredecessor());
2976 return Bailout("SharedFunctionInfoLiteral");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002977}
2978
2979
2980void HGraphBuilder::VisitConditional(Conditional* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00002981 ASSERT(!HasStackOverflow());
2982 ASSERT(current_block() != NULL);
2983 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002984 HBasicBlock* cond_true = graph()->CreateBasicBlock();
2985 HBasicBlock* cond_false = graph()->CreateBasicBlock();
danno@chromium.org160a7b02011-04-18 15:51:38 +00002986 CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002987
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002988 // Visit the true and false subexpressions in the same AST context as the
2989 // whole expression.
danno@chromium.org160a7b02011-04-18 15:51:38 +00002990 if (cond_true->HasPredecessor()) {
2991 cond_true->SetJoinId(expr->ThenId());
2992 set_current_block(cond_true);
2993 CHECK_BAILOUT(Visit(expr->then_expression()));
2994 cond_true = current_block();
2995 } else {
2996 cond_true = NULL;
2997 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002998
danno@chromium.org160a7b02011-04-18 15:51:38 +00002999 if (cond_false->HasPredecessor()) {
3000 cond_false->SetJoinId(expr->ElseId());
3001 set_current_block(cond_false);
3002 CHECK_BAILOUT(Visit(expr->else_expression()));
3003 cond_false = current_block();
3004 } else {
3005 cond_false = NULL;
3006 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003007
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003008 if (!ast_context()->IsTest()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003009 HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003010 set_current_block(join);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003011 if (join != NULL && !ast_context()->IsEffect()) {
3012 ast_context()->ReturnValue(Pop());
3013 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003014 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003015}
3016
3017
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003018HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
3019 Variable* var, LookupResult* lookup, bool is_store) {
3020 if (var->is_this() || !info()->has_global_object()) {
3021 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003022 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003023 Handle<GlobalObject> global(info()->global_object());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003024 global->Lookup(*var->name(), lookup);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003025 if (!lookup->IsProperty() ||
3026 lookup->type() != NORMAL ||
3027 (is_store && lookup->IsReadOnly()) ||
3028 lookup->holder() != *global) {
3029 return kUseGeneric;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003030 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003031
3032 return kUseCell;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003033}
3034
3035
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003036HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
3037 ASSERT(var->IsContextSlot());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003038 HValue* context = environment()->LookupContext();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003039 int length = info()->scope()->ContextChainLength(var->scope());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003040 while (length-- > 0) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003041 HInstruction* context_instruction = new(zone()) HOuterContext(context);
3042 AddInstruction(context_instruction);
3043 context = context_instruction;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003044 }
3045 return context;
3046}
3047
3048
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003049void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003050 ASSERT(!HasStackOverflow());
3051 ASSERT(current_block() != NULL);
3052 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003053 Variable* variable = expr->AsVariable();
3054 if (variable == NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003055 return Bailout("reference to rewritten variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003056 } else if (variable->IsStackAllocated()) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003057 HValue* value = environment()->Lookup(variable);
3058 if (variable->mode() == Variable::CONST &&
3059 value == graph()->GetConstantHole()) {
3060 return Bailout("reference to uninitialized const variable");
3061 }
3062 ast_context()->ReturnValue(value);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003063 } else if (variable->IsContextSlot()) {
3064 if (variable->mode() == Variable::CONST) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003065 return Bailout("reference to const context slot");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003066 }
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003067 HValue* context = BuildContextChainWalk(variable);
3068 int index = variable->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003069 HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, index);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003070 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003071 } else if (variable->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003072 LookupResult lookup;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003073 GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003074
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003075 if (type == kUseCell &&
3076 info()->global_object()->IsAccessCheckNeeded()) {
3077 type = kUseGeneric;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003078 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003079
3080 if (type == kUseCell) {
3081 Handle<GlobalObject> global(info()->global_object());
3082 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
3083 bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003084 HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003085 ast_context()->ReturnInstruction(instr, expr->id());
3086 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003087 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003088 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003089 AddInstruction(global_object);
3090 HLoadGlobalGeneric* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003091 new(zone()) HLoadGlobalGeneric(context,
3092 global_object,
3093 variable->name(),
3094 ast_context()->is_for_typeof());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003095 instr->set_position(expr->position());
3096 ASSERT(instr->HasSideEffects());
3097 ast_context()->ReturnInstruction(instr, expr->id());
3098 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003099 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003100 return Bailout("reference to a variable which requires dynamic lookup");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003101 }
3102}
3103
3104
3105void HGraphBuilder::VisitLiteral(Literal* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003106 ASSERT(!HasStackOverflow());
3107 ASSERT(current_block() != NULL);
3108 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003109 HConstant* instr =
3110 new(zone()) HConstant(expr->handle(), Representation::Tagged());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003111 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003112}
3113
3114
3115void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003116 ASSERT(!HasStackOverflow());
3117 ASSERT(current_block() != NULL);
3118 ASSERT(current_block()->HasPredecessor());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003119 HRegExpLiteral* instr = new(zone()) HRegExpLiteral(expr->pattern(),
3120 expr->flags(),
3121 expr->literal_index());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003122 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003123}
3124
3125
3126void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003127 ASSERT(!HasStackOverflow());
3128 ASSERT(current_block() != NULL);
3129 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003130 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003131 HObjectLiteral* literal =
3132 new(zone()) HObjectLiteral(context,
3133 expr->constant_properties(),
3134 expr->fast_elements(),
3135 expr->literal_index(),
3136 expr->depth(),
3137 expr->has_function());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003138 // The object is expected in the bailout environment during computation
3139 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003140 PushAndAdd(literal);
3141
3142 expr->CalculateEmitStore();
3143
3144 for (int i = 0; i < expr->properties()->length(); i++) {
3145 ObjectLiteral::Property* property = expr->properties()->at(i);
3146 if (property->IsCompileTimeValue()) continue;
3147
3148 Literal* key = property->key();
3149 Expression* value = property->value();
3150
3151 switch (property->kind()) {
3152 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3153 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
3154 // Fall through.
3155 case ObjectLiteral::Property::COMPUTED:
3156 if (key->handle()->IsSymbol()) {
3157 if (property->emit_store()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003158 CHECK_ALIVE(VisitForValue(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003159 HValue* value = Pop();
3160 Handle<String> name = Handle<String>::cast(key->handle());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003161 HStoreNamedGeneric* store =
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003162 new(zone()) HStoreNamedGeneric(
3163 context,
3164 literal,
3165 name,
3166 value,
3167 function_strict_mode());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003168 AddInstruction(store);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003169 AddSimulate(key->id());
3170 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003171 CHECK_ALIVE(VisitForEffect(value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003172 }
3173 break;
3174 }
3175 // Fall through.
3176 case ObjectLiteral::Property::PROTOTYPE:
3177 case ObjectLiteral::Property::SETTER:
3178 case ObjectLiteral::Property::GETTER:
danno@chromium.org160a7b02011-04-18 15:51:38 +00003179 return Bailout("Object literal with complex property");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003180 default: UNREACHABLE();
3181 }
3182 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003183
3184 if (expr->has_function()) {
3185 // Return the result of the transformation to fast properties
3186 // instead of the original since this operation changes the map
3187 // of the object. This makes sure that the original object won't
3188 // be used by other optimized code before it is transformed
3189 // (e.g. because of code motion).
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003190 HToFastProperties* result = new(zone()) HToFastProperties(Pop());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003191 AddInstruction(result);
3192 ast_context()->ReturnValue(result);
3193 } else {
3194 ast_context()->ReturnValue(Pop());
3195 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003196}
3197
3198
3199void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003200 ASSERT(!HasStackOverflow());
3201 ASSERT(current_block() != NULL);
3202 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003203 ZoneList<Expression*>* subexprs = expr->values();
3204 int length = subexprs->length();
3205
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003206 HArrayLiteral* literal = new(zone()) HArrayLiteral(expr->constant_elements(),
3207 length,
3208 expr->literal_index(),
3209 expr->depth());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003210 // The array is expected in the bailout environment during computation
3211 // of the property values and is the value of the entire expression.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003212 PushAndAdd(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003213
3214 HLoadElements* elements = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003215
3216 for (int i = 0; i < length; i++) {
3217 Expression* subexpr = subexprs->at(i);
3218 // If the subexpression is a literal or a simple materialized literal it
3219 // is already set in the cloned array.
3220 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
3221
danno@chromium.org160a7b02011-04-18 15:51:38 +00003222 CHECK_ALIVE(VisitForValue(subexpr));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003223 HValue* value = Pop();
danno@chromium.org160a7b02011-04-18 15:51:38 +00003224 if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003225
3226 // Load the elements array before the first store.
3227 if (elements == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003228 elements = new(zone()) HLoadElements(literal);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003229 AddInstruction(elements);
3230 }
3231
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003232 HValue* key = AddInstruction(
3233 new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
3234 Representation::Integer32()));
3235 AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003236 AddSimulate(expr->GetIdForElement(i));
3237 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003238 ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003239}
3240
3241
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003242// Sets the lookup result and returns true if the store can be inlined.
3243static bool ComputeStoredField(Handle<Map> type,
3244 Handle<String> name,
3245 LookupResult* lookup) {
3246 type->LookupInDescriptors(NULL, *name, lookup);
3247 if (!lookup->IsPropertyOrTransition()) return false;
3248 if (lookup->type() == FIELD) return true;
3249 return (lookup->type() == MAP_TRANSITION) &&
3250 (type->unused_property_fields() > 0);
3251}
3252
3253
3254static int ComputeStoredFieldIndex(Handle<Map> type,
3255 Handle<String> name,
3256 LookupResult* lookup) {
3257 ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION);
3258 if (lookup->type() == FIELD) {
3259 return lookup->GetLocalFieldIndexFromMap(*type);
3260 } else {
3261 Map* transition = lookup->GetTransitionMapFromMap(*type);
3262 return transition->PropertyIndexFor(*name) - type->inobject_properties();
3263 }
3264}
3265
3266
3267HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
3268 Handle<String> name,
3269 HValue* value,
3270 Handle<Map> type,
3271 LookupResult* lookup,
3272 bool smi_and_map_check) {
3273 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003274 AddInstruction(new(zone()) HCheckNonSmi(object));
3275 AddInstruction(new(zone()) HCheckMap(object, type));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003276 }
3277
3278 int index = ComputeStoredFieldIndex(type, name, lookup);
3279 bool is_in_object = index < 0;
3280 int offset = index * kPointerSize;
3281 if (index < 0) {
3282 // Negative property indices are in-object properties, indexed
3283 // from the end of the fixed part of the object.
3284 offset += type->instance_size();
3285 } else {
3286 offset += FixedArray::kHeaderSize;
3287 }
3288 HStoreNamedField* instr =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003289 new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003290 if (lookup->type() == MAP_TRANSITION) {
3291 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
3292 instr->set_transition(transition);
whesse@chromium.org023421e2010-12-21 12:19:12 +00003293 // TODO(fschneider): Record the new map type of the object in the IR to
3294 // enable elimination of redundant checks after the transition store.
3295 instr->SetFlag(HValue::kChangesMaps);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003296 }
3297 return instr;
3298}
3299
3300
3301HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
3302 Handle<String> name,
3303 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003304 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003305 return new(zone()) HStoreNamedGeneric(
3306 context,
3307 object,
3308 name,
3309 value,
3310 function_strict_mode());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003311}
3312
3313
3314HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object,
3315 HValue* value,
3316 Expression* expr) {
3317 Property* prop = (expr->AsProperty() != NULL)
3318 ? expr->AsProperty()
3319 : expr->AsAssignment()->target()->AsProperty();
3320 Literal* key = prop->key()->AsLiteral();
3321 Handle<String> name = Handle<String>::cast(key->handle());
3322 ASSERT(!name.is_null());
3323
3324 LookupResult lookup;
3325 ZoneMapList* types = expr->GetReceiverTypes();
3326 bool is_monomorphic = expr->IsMonomorphic() &&
3327 ComputeStoredField(types->first(), name, &lookup);
3328
3329 return is_monomorphic
3330 ? BuildStoreNamedField(object, name, value, types->first(), &lookup,
3331 true) // Needs smi and map check.
3332 : BuildStoreNamedGeneric(object, name, value);
3333}
3334
3335
3336void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
3337 HValue* object,
3338 HValue* value,
3339 ZoneMapList* types,
3340 Handle<String> name) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003341 // TODO(ager): We should recognize when the prototype chains for different
3342 // maps are identical. In that case we can avoid repeatedly generating the
3343 // same prototype map checks.
3344 int count = 0;
3345 HBasicBlock* join = NULL;
3346 for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003347 Handle<Map> map = types->at(i);
3348 LookupResult lookup;
3349 if (ComputeStoredField(map, name, &lookup)) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003350 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003351 AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003352 join = graph()->CreateBasicBlock();
3353 }
3354 ++count;
3355 HBasicBlock* if_true = graph()->CreateBasicBlock();
3356 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003357 HCompareMap* compare =
3358 new(zone()) HCompareMap(object, map, if_true, if_false);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003359 current_block()->Finish(compare);
3360
3361 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003362 HInstruction* instr =
3363 BuildStoreNamedField(object, name, value, map, &lookup, false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003364 instr->set_position(expr->position());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003365 // Goto will add the HSimulate for the store.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003366 AddInstruction(instr);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003367 if (!ast_context()->IsEffect()) Push(value);
3368 current_block()->Goto(join);
3369
3370 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003371 }
3372 }
3373
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003374 // Finish up. Unconditionally deoptimize if we've handled all the maps we
3375 // know about and do not want to handle ones we've never seen. Otherwise
3376 // use a generic IC.
3377 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003378 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003379 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003380 HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003381 instr->set_position(expr->position());
3382 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003383
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003384 if (join != NULL) {
3385 if (!ast_context()->IsEffect()) Push(value);
3386 current_block()->Goto(join);
3387 } else {
3388 // The HSimulate for the store should not see the stored value in
3389 // effect contexts (it is not materialized at expr->id() in the
3390 // unoptimized code).
3391 if (instr->HasSideEffects()) {
3392 if (ast_context()->IsEffect()) {
3393 AddSimulate(expr->id());
3394 } else {
3395 Push(value);
3396 AddSimulate(expr->id());
3397 Drop(1);
3398 }
3399 }
3400 ast_context()->ReturnValue(value);
3401 return;
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00003402 }
lrn@chromium.org8541d772010-12-15 12:05:09 +00003403 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00003404
3405 ASSERT(join != NULL);
3406 join->SetJoinId(expr->id());
3407 set_current_block(join);
3408 if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003409}
3410
3411
3412void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
3413 Property* prop = expr->target()->AsProperty();
3414 ASSERT(prop != NULL);
3415 expr->RecordTypeFeedback(oracle());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003416 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003417
3418 HValue* value = NULL;
3419 HInstruction* instr = NULL;
3420
3421 if (prop->key()->IsPropertyName()) {
3422 // Named store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003423 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003424 value = Pop();
3425 HValue* object = Pop();
3426
3427 Literal* key = prop->key()->AsLiteral();
3428 Handle<String> name = Handle<String>::cast(key->handle());
3429 ASSERT(!name.is_null());
3430
3431 ZoneMapList* types = expr->GetReceiverTypes();
3432 LookupResult lookup;
3433
3434 if (expr->IsMonomorphic()) {
3435 instr = BuildStoreNamed(object, value, expr);
3436
3437 } else if (types != NULL && types->length() > 1) {
3438 HandlePolymorphicStoreNamedField(expr, object, value, types, name);
3439 return;
3440
3441 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003442 instr = BuildStoreNamedGeneric(object, name, value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003443 }
3444
3445 } else {
3446 // Keyed store.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003447 CHECK_ALIVE(VisitForValue(prop->key()));
3448 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003449 value = Pop();
3450 HValue* key = Pop();
3451 HValue* object = Pop();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003452 instr = BuildStoreKeyed(object, key, value, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003453 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003454 Push(value);
3455 instr->set_position(expr->position());
3456 AddInstruction(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003457 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
3458 ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003459}
3460
3461
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003462// Because not every expression has a position and there is not common
3463// superclass of Assignment and CountOperation, we cannot just pass the
3464// owning expression instead of position and ast_id separately.
3465void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003466 HValue* value,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003467 int position,
3468 int ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003469 LookupResult lookup;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003470 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
3471 if (type == kUseCell) {
3472 bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
3473 Handle<GlobalObject> global(info()->global_object());
3474 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003475 HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003476 instr->set_position(position);
3477 AddInstruction(instr);
3478 if (instr->HasSideEffects()) AddSimulate(ast_id);
3479 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003480 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003481 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
3482 AddInstruction(global_object);
3483 HStoreGlobalGeneric* instr =
3484 new(zone()) HStoreGlobalGeneric(context,
3485 global_object,
3486 var->name(),
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003487 value,
3488 function_strict_mode());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003489 instr->set_position(position);
3490 AddInstruction(instr);
3491 ASSERT(instr->HasSideEffects());
3492 if (instr->HasSideEffects()) AddSimulate(ast_id);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003493 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003494}
3495
3496
3497void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
3498 Expression* target = expr->target();
3499 VariableProxy* proxy = target->AsVariableProxy();
3500 Variable* var = proxy->AsVariable();
3501 Property* prop = target->AsProperty();
3502 ASSERT(var == NULL || prop == NULL);
3503
3504 // We have a second position recorded in the FullCodeGenerator to have
3505 // type feedback for the binary operation.
3506 BinaryOperation* operation = expr->binary_operation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003507
3508 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003509 if (var->mode() == Variable::CONST) {
3510 return Bailout("unsupported const compound assignment");
3511 }
3512
danno@chromium.org160a7b02011-04-18 15:51:38 +00003513 CHECK_ALIVE(VisitForValue(operation));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003514
3515 if (var->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003516 HandleGlobalVariableAssignment(var,
3517 Top(),
3518 expr->position(),
3519 expr->AssignmentId());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003520 } else if (var->IsStackAllocated()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003521 Bind(var, Top());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003522 } else if (var->IsContextSlot()) {
3523 HValue* context = BuildContextChainWalk(var);
3524 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003525 HStoreContextSlot* instr =
3526 new(zone()) HStoreContextSlot(context, index, Top());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003527 AddInstruction(instr);
3528 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
3529 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003530 return Bailout("compound assignment to lookup slot");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003531 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003532 ast_context()->ReturnValue(Pop());
3533
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003534 } else if (prop != NULL) {
3535 prop->RecordTypeFeedback(oracle());
3536
3537 if (prop->key()->IsPropertyName()) {
3538 // Named property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003539 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003540 HValue* obj = Top();
3541
3542 HInstruction* load = NULL;
3543 if (prop->IsMonomorphic()) {
3544 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
3545 Handle<Map> map = prop->GetReceiverTypes()->first();
3546 load = BuildLoadNamed(obj, prop, map, name);
3547 } else {
3548 load = BuildLoadNamedGeneric(obj, prop);
3549 }
3550 PushAndAdd(load);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003551 if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003552
danno@chromium.org160a7b02011-04-18 15:51:38 +00003553 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003554 HValue* right = Pop();
3555 HValue* left = Pop();
3556
3557 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3558 PushAndAdd(instr);
3559 if (instr->HasSideEffects()) AddSimulate(operation->id());
3560
3561 HInstruction* store = BuildStoreNamed(obj, instr, prop);
3562 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003563 // Drop the simulated receiver and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003564 Drop(2);
3565 Push(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003566 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
3567 ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003568
3569 } else {
3570 // Keyed property.
danno@chromium.org160a7b02011-04-18 15:51:38 +00003571 CHECK_ALIVE(VisitForValue(prop->obj()));
3572 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003573 HValue* obj = environment()->ExpressionStackAt(1);
3574 HValue* key = environment()->ExpressionStackAt(0);
3575
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003576 HInstruction* load = BuildLoadKeyed(obj, key, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003577 PushAndAdd(load);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003578 if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003579
danno@chromium.org160a7b02011-04-18 15:51:38 +00003580 CHECK_ALIVE(VisitForValue(expr->value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003581 HValue* right = Pop();
3582 HValue* left = Pop();
3583
3584 HInstruction* instr = BuildBinaryOperation(operation, left, right);
3585 PushAndAdd(instr);
3586 if (instr->HasSideEffects()) AddSimulate(operation->id());
3587
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003588 expr->RecordTypeFeedback(oracle());
3589 HInstruction* store = BuildStoreKeyed(obj, key, instr, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003590 AddInstruction(store);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003591 // Drop the simulated receiver, key, and value. Return the value.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003592 Drop(3);
3593 Push(instr);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003594 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
3595 ast_context()->ReturnValue(Pop());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003596 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003597
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003598 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003599 return Bailout("invalid lhs in compound assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003600 }
3601}
3602
3603
3604void HGraphBuilder::VisitAssignment(Assignment* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003605 ASSERT(!HasStackOverflow());
3606 ASSERT(current_block() != NULL);
3607 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003608 VariableProxy* proxy = expr->target()->AsVariableProxy();
3609 Variable* var = proxy->AsVariable();
3610 Property* prop = expr->target()->AsProperty();
3611 ASSERT(var == NULL || prop == NULL);
3612
3613 if (expr->is_compound()) {
3614 HandleCompoundAssignment(expr);
3615 return;
3616 }
3617
3618 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003619 if (var->mode() == Variable::CONST) {
3620 if (expr->op() != Token::INIT_CONST) {
3621 return Bailout("non-initializer assignment to const");
3622 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003623 if (!var->IsStackAllocated()) {
3624 return Bailout("assignment to const context slot");
3625 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003626 // We insert a use of the old value to detect unsupported uses of const
3627 // variables (e.g. initialization inside a loop).
3628 HValue* old_value = environment()->Lookup(var);
3629 AddInstruction(new HUseConst(old_value));
3630 }
3631
danno@chromium.org160a7b02011-04-18 15:51:38 +00003632 if (proxy->IsArguments()) return Bailout("assignment to arguments");
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003633
3634 // Handle the assignment.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003635 if (var->IsStackAllocated()) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003636 // We do not allow the arguments object to occur in a context where it
3637 // may escape, but assignments to stack-allocated locals are
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00003638 // permitted.
3639 CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
3640 HValue* value = Pop();
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003641 Bind(var, value);
3642 ast_context()->ReturnValue(value);
3643
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003644 } else if (var->IsContextSlot()) {
3645 ASSERT(var->mode() != Variable::CONST);
danno@chromium.org160a7b02011-04-18 15:51:38 +00003646 CHECK_ALIVE(VisitForValue(expr->value()));
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003647 HValue* context = BuildContextChainWalk(var);
3648 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003649 HStoreContextSlot* instr =
3650 new(zone()) HStoreContextSlot(context, index, Top());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003651 AddInstruction(instr);
3652 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
3653 ast_context()->ReturnValue(Pop());
3654
3655 } else if (var->is_global()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003656 CHECK_ALIVE(VisitForValue(expr->value()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003657 HandleGlobalVariableAssignment(var,
3658 Top(),
3659 expr->position(),
3660 expr->AssignmentId());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00003661 ast_context()->ReturnValue(Pop());
3662
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00003663 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003664 return Bailout("assignment to LOOKUP or const CONTEXT variable");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003665 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003666
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003667 } else if (prop != NULL) {
3668 HandlePropertyAssignment(expr);
3669 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003670 return Bailout("invalid left-hand side in assignment");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003671 }
3672}
3673
3674
3675void HGraphBuilder::VisitThrow(Throw* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003676 ASSERT(!HasStackOverflow());
3677 ASSERT(current_block() != NULL);
3678 ASSERT(current_block()->HasPredecessor());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003679 // We don't optimize functions with invalid left-hand sides in
3680 // assignments, count operations, or for-in. Consequently throw can
3681 // currently only occur in an effect context.
3682 ASSERT(ast_context()->IsEffect());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003683 CHECK_ALIVE(VisitForValue(expr->exception()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003684
3685 HValue* value = environment()->Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003686 HThrow* instr = new(zone()) HThrow(value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003687 instr->set_position(expr->position());
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003688 AddInstruction(instr);
3689 AddSimulate(expr->id());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003690 current_block()->FinishExit(new(zone()) HAbnormalExit);
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00003691 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003692}
3693
3694
whesse@chromium.org023421e2010-12-21 12:19:12 +00003695HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
3696 Property* expr,
3697 Handle<Map> type,
3698 LookupResult* lookup,
3699 bool smi_and_map_check) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003700 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003701 AddInstruction(new(zone()) HCheckNonSmi(object));
3702 AddInstruction(new(zone()) HCheckMap(object, type));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003703 }
3704
3705 int index = lookup->GetLocalFieldIndexFromMap(*type);
3706 if (index < 0) {
3707 // Negative property indices are in-object properties, indexed
3708 // from the end of the fixed part of the object.
3709 int offset = (index * kPointerSize) + type->instance_size();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003710 return new(zone()) HLoadNamedField(object, true, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003711 } else {
3712 // Non-negative property indices are in the properties array.
3713 int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003714 return new(zone()) HLoadNamedField(object, false, offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003715 }
3716}
3717
3718
3719HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
3720 Property* expr) {
3721 ASSERT(expr->key()->IsPropertyName());
3722 Handle<Object> name = expr->key()->AsLiteral()->handle();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003723 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003724 return new(zone()) HLoadNamedGeneric(context, obj, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003725}
3726
3727
3728HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
3729 Property* expr,
3730 Handle<Map> map,
3731 Handle<String> name) {
3732 LookupResult lookup;
3733 map->LookupInDescriptors(NULL, *name, &lookup);
3734 if (lookup.IsProperty() && lookup.type() == FIELD) {
3735 return BuildLoadNamedField(obj,
3736 expr,
3737 map,
3738 &lookup,
3739 true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003740 } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003741 AddInstruction(new(zone()) HCheckNonSmi(obj));
3742 AddInstruction(new(zone()) HCheckMap(obj, map));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003743 Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003744 return new(zone()) HConstant(function, Representation::Tagged());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003745 } else {
3746 return BuildLoadNamedGeneric(obj, expr);
3747 }
3748}
3749
3750
3751HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
3752 HValue* key) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003753 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003754 return new(zone()) HLoadKeyedGeneric(context, object, key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003755}
3756
3757
3758HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
3759 HValue* key,
3760 Property* expr) {
3761 ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003762 AddInstruction(new(zone()) HCheckNonSmi(object));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003763 Handle<Map> map = expr->GetMonomorphicReceiverType();
3764 ASSERT(map->has_fast_elements());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003765 AddInstruction(new(zone()) HCheckMap(object, map));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003766 bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003767 HLoadElements* elements = new(zone()) HLoadElements(object);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003768 HInstruction* length = NULL;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003769 HInstruction* checked_key = NULL;
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003770 if (is_array) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003771 length = AddInstruction(new(zone()) HJSArrayLength(object));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003772 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003773 AddInstruction(elements);
3774 } else {
3775 AddInstruction(elements);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003776 length = AddInstruction(new(zone()) HFixedArrayLength(elements));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003777 checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003778 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003779 return new(zone()) HLoadKeyedFastElement(elements, checked_key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003780}
3781
3782
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003783HInstruction* HGraphBuilder::BuildLoadKeyedSpecializedArrayElement(
3784 HValue* object,
3785 HValue* key,
3786 Property* expr) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003787 ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003788 AddInstruction(new(zone()) HCheckNonSmi(object));
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003789 Handle<Map> map = expr->GetMonomorphicReceiverType();
3790 ASSERT(!map->has_fast_elements());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003791 ASSERT(map->has_external_array_elements());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003792 AddInstruction(new(zone()) HCheckMap(object, map));
3793 HLoadElements* elements = new(zone()) HLoadElements(object);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003794 AddInstruction(elements);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003795 HInstruction* length = new(zone()) HExternalArrayLength(elements);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003796 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003797 HInstruction* checked_key =
3798 AddInstruction(new(zone()) HBoundsCheck(key, length));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003799 HLoadExternalArrayPointer* external_elements =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003800 new(zone()) HLoadExternalArrayPointer(elements);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003801 AddInstruction(external_elements);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003802 HLoadKeyedSpecializedArrayElement* pixel_array_value =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003803 new(zone()) HLoadKeyedSpecializedArrayElement(
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003804 external_elements, checked_key, map->elements_kind());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00003805 return pixel_array_value;
3806}
3807
3808
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003809HInstruction* HGraphBuilder::BuildLoadKeyed(HValue* obj,
3810 HValue* key,
3811 Property* prop) {
3812 if (prop->IsMonomorphic()) {
3813 Handle<Map> receiver_type(prop->GetMonomorphicReceiverType());
3814 // An object has either fast elements or pixel array elements, but never
3815 // both. Pixel array maps that are assigned to pixel array elements are
3816 // always created with the fast elements flag cleared.
3817 if (receiver_type->has_external_array_elements()) {
3818 return BuildLoadKeyedSpecializedArrayElement(obj, key, prop);
3819 } else if (receiver_type->has_fast_elements()) {
3820 return BuildLoadKeyedFastElement(obj, key, prop);
3821 }
3822 }
3823 return BuildLoadKeyedGeneric(obj, key);
3824}
3825
3826
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003827HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
3828 HValue* key,
3829 HValue* value) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003830 HValue* context = environment()->LookupContext();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003831 return new(zone()) HStoreKeyedGeneric(
3832 context,
3833 object,
3834 key,
3835 value,
3836 function_strict_mode());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003837}
3838
3839
3840HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
3841 HValue* key,
3842 HValue* val,
3843 Expression* expr) {
3844 ASSERT(expr->IsMonomorphic());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003845 AddInstruction(new(zone()) HCheckNonSmi(object));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003846 Handle<Map> map = expr->GetMonomorphicReceiverType();
3847 ASSERT(map->has_fast_elements());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003848 AddInstruction(new(zone()) HCheckMap(object, map));
3849 HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
3850 AddInstruction(new(zone()) HCheckMap(
3851 elements, isolate()->factory()->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003852 bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
3853 HInstruction* length = NULL;
3854 if (is_array) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003855 length = AddInstruction(new(zone()) HJSArrayLength(object));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003856 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003857 length = AddInstruction(new(zone()) HFixedArrayLength(elements));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003858 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003859 HInstruction* checked_key =
3860 AddInstruction(new(zone()) HBoundsCheck(key, length));
3861 return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003862}
3863
3864
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003865HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003866 HValue* object,
3867 HValue* key,
3868 HValue* val,
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003869 Expression* expr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003870 ASSERT(expr->IsMonomorphic());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003871 AddInstruction(new(zone()) HCheckNonSmi(object));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003872 Handle<Map> map = expr->GetMonomorphicReceiverType();
3873 ASSERT(!map->has_fast_elements());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003874 ASSERT(map->has_external_array_elements());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003875 AddInstruction(new(zone()) HCheckMap(object, map));
3876 HLoadElements* elements = new(zone()) HLoadElements(object);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003877 AddInstruction(elements);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003878 HInstruction* length = AddInstruction(
3879 new(zone()) HExternalArrayLength(elements));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003880 HInstruction* checked_key =
3881 AddInstruction(new(zone()) HBoundsCheck(key, length));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003882 HLoadExternalArrayPointer* external_elements =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003883 new(zone()) HLoadExternalArrayPointer(elements);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003884 AddInstruction(external_elements);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003885 JSObject::ElementsKind elements_kind = map->elements_kind();
3886 switch (elements_kind) {
3887 case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00003888 HClampToUint8* clamp = new(zone()) HClampToUint8(val);
3889 AddInstruction(clamp);
3890 val = clamp;
3891 break;
3892 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003893 case JSObject::EXTERNAL_BYTE_ELEMENTS:
3894 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3895 case JSObject::EXTERNAL_SHORT_ELEMENTS:
3896 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3897 case JSObject::EXTERNAL_INT_ELEMENTS:
3898 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00003899 HToInt32* floor_val = new(zone()) HToInt32(val);
3900 AddInstruction(floor_val);
3901 val = floor_val;
3902 break;
3903 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003904 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
3905 case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
3906 break;
3907
3908 case JSObject::FAST_ELEMENTS:
3909 case JSObject::FAST_DOUBLE_ELEMENTS:
3910 case JSObject::DICTIONARY_ELEMENTS:
3911 UNREACHABLE();
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00003912 break;
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003913 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003914 return new(zone()) HStoreKeyedSpecializedArrayElement(
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003915 external_elements,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003916 checked_key,
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003917 val,
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003918 map->elements_kind());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00003919}
3920
3921
3922HInstruction* HGraphBuilder::BuildStoreKeyed(HValue* object,
3923 HValue* key,
3924 HValue* value,
3925 Expression* expr) {
3926 if (expr->IsMonomorphic()) {
3927 Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
3928 // An object has either fast elements or external array elements, but
3929 // never both. Pixel array maps that are assigned to pixel array elements
3930 // are always created with the fast elements flag cleared.
3931 if (receiver_type->has_external_array_elements()) {
3932 return BuildStoreKeyedSpecializedArrayElement(object,
3933 key,
3934 value,
3935 expr);
3936 } else if (receiver_type->has_fast_elements()) {
3937 return BuildStoreKeyedFastElement(object, key, value, expr);
3938 }
3939 }
3940 return BuildStoreKeyedGeneric(object, key, value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003941}
3942
3943
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003944bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
3945 VariableProxy* proxy = expr->obj()->AsVariableProxy();
3946 if (proxy == NULL) return false;
3947 if (!proxy->var()->IsStackAllocated()) return false;
3948 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
3949 return false;
3950 }
3951
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00003952 // Our implementation of arguments (based on this stack frame or an
3953 // adapter below it) does not work for inlined functions.
3954 if (function_state()->outer() != NULL) {
3955 Bailout("arguments access in inlined function");
3956 return true;
3957 }
3958
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003959 HInstruction* result = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003960 if (expr->key()->IsPropertyName()) {
3961 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
3962 if (!name->IsEqualTo(CStrVector("length"))) return false;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003963 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
3964 result = new(zone()) HArgumentsLength(elements);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003965 } else {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003966 Push(graph()->GetArgumentsObject());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003967 VisitForValue(expr->key());
danno@chromium.org160a7b02011-04-18 15:51:38 +00003968 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003969 HValue* key = Pop();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003970 Drop(1); // Arguments object.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003971 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
3972 HInstruction* length = AddInstruction(
3973 new(zone()) HArgumentsLength(elements));
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003974 HInstruction* checked_key =
3975 AddInstruction(new(zone()) HBoundsCheck(key, length));
3976 result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003977 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003978 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003979 return true;
3980}
3981
3982
3983void HGraphBuilder::VisitProperty(Property* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00003984 ASSERT(!HasStackOverflow());
3985 ASSERT(current_block() != NULL);
3986 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003987 expr->RecordTypeFeedback(oracle());
3988
3989 if (TryArgumentsAccess(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003990
danno@chromium.org160a7b02011-04-18 15:51:38 +00003991 CHECK_ALIVE(VisitForValue(expr->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003992
3993 HInstruction* instr = NULL;
3994 if (expr->IsArrayLength()) {
3995 HValue* array = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003996 AddInstruction(new(zone()) HCheckNonSmi(array));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003997 AddInstruction(HCheckInstanceType::NewIsJSArray(array));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00003998 instr = new(zone()) HJSArrayLength(array);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00003999
ager@chromium.org378b34e2011-01-28 08:04:38 +00004000 } else if (expr->IsStringLength()) {
4001 HValue* string = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004002 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004003 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004004 instr = new(zone()) HStringLength(string);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004005 } else if (expr->IsStringAccess()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004006 CHECK_ALIVE(VisitForValue(expr->key()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004007 HValue* index = Pop();
4008 HValue* string = Pop();
4009 HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
4010 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004011 instr = new(zone()) HStringCharFromCode(char_code);
ager@chromium.org378b34e2011-01-28 08:04:38 +00004012
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00004013 } else if (expr->IsFunctionPrototype()) {
4014 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004015 AddInstruction(new(zone()) HCheckNonSmi(function));
4016 instr = new(zone()) HLoadFunctionPrototype(function);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004017
4018 } else if (expr->key()->IsPropertyName()) {
4019 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
4020 ZoneMapList* types = expr->GetReceiverTypes();
4021
4022 HValue* obj = Pop();
4023 if (expr->IsMonomorphic()) {
4024 instr = BuildLoadNamed(obj, expr, types->first(), name);
4025 } else if (types != NULL && types->length() > 1) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004026 AddInstruction(new(zone()) HCheckNonSmi(obj));
4027 instr = new(zone()) HLoadNamedFieldPolymorphic(obj, types, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004028 } else {
4029 instr = BuildLoadNamedGeneric(obj, expr);
4030 }
4031
4032 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004033 CHECK_ALIVE(VisitForValue(expr->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004034
4035 HValue* key = Pop();
4036 HValue* obj = Pop();
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004037 instr = BuildLoadKeyed(obj, key, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004038 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004039 instr->set_position(expr->position());
4040 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004041}
4042
4043
4044void HGraphBuilder::AddCheckConstantFunction(Call* expr,
4045 HValue* receiver,
4046 Handle<Map> receiver_map,
4047 bool smi_and_map_check) {
4048 // Constant functions have the nice property that the map will change if they
4049 // are overwritten. Therefore it is enough to check the map of the holder and
4050 // its prototypes.
4051 if (smi_and_map_check) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004052 AddInstruction(new(zone()) HCheckNonSmi(receiver));
4053 AddInstruction(new(zone()) HCheckMap(receiver, receiver_map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004054 }
4055 if (!expr->holder().is_null()) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004056 AddInstruction(new(zone()) HCheckPrototypeMaps(
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004057 Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
4058 expr->holder()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004059 }
4060}
4061
4062
4063void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
4064 HValue* receiver,
4065 ZoneMapList* types,
4066 Handle<String> name) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004067 // TODO(ager): We should recognize when the prototype chains for different
4068 // maps are identical. In that case we can avoid repeatedly generating the
4069 // same prototype map checks.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004070 int argument_count = expr->arguments()->length() + 1; // Includes receiver.
4071 int count = 0;
4072 HBasicBlock* join = NULL;
4073 for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004074 Handle<Map> map = types->at(i);
4075 if (expr->ComputeTarget(map, name)) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004076 if (count == 0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004077 // Only needed once.
4078 AddInstruction(new(zone()) HCheckNonSmi(receiver));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004079 join = graph()->CreateBasicBlock();
4080 }
4081 ++count;
4082 HBasicBlock* if_true = graph()->CreateBasicBlock();
4083 HBasicBlock* if_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004084 HCompareMap* compare =
4085 new(zone()) HCompareMap(receiver, map, if_true, if_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004086 current_block()->Finish(compare);
4087
4088 set_current_block(if_true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004089 AddCheckConstantFunction(expr, receiver, map, false);
4090 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
4091 PrintF("Trying to inline the polymorphic call to %s\n",
4092 *name->ToCString());
4093 }
danno@chromium.org160a7b02011-04-18 15:51:38 +00004094 if (FLAG_polymorphic_inlining && TryInline(expr)) {
4095 // Trying to inline will signal that we should bailout from the
4096 // entire compilation by setting stack overflow on the visitor.
4097 if (HasStackOverflow()) return;
4098 } else {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004099 HCallConstantFunction* call =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004100 new(zone()) HCallConstantFunction(expr->target(), argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004101 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004102 PreProcessCall(call);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004103 AddInstruction(call);
4104 if (!ast_context()->IsEffect()) Push(call);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004105 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004106
4107 if (current_block() != NULL) current_block()->Goto(join);
4108 set_current_block(if_false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004109 }
4110 }
4111
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004112 // Finish up. Unconditionally deoptimize if we've handled all the maps we
4113 // know about and do not want to handle ones we've never seen. Otherwise
4114 // use a generic IC.
4115 if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004116 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004117 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004118 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004119 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004120 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004121 PreProcessCall(call);
lrn@chromium.org8541d772010-12-15 12:05:09 +00004122
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004123 if (join != NULL) {
4124 AddInstruction(call);
4125 if (!ast_context()->IsEffect()) Push(call);
4126 current_block()->Goto(join);
4127 } else {
4128 ast_context()->ReturnInstruction(call, expr->id());
4129 return;
kmillikin@chromium.orgc278c962011-01-06 08:52:11 +00004130 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004131 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004132
4133 // We assume that control flow is always live after an expression. So
4134 // even without predecessors to the join block, we set it as the exit
4135 // block and continue by adding instructions there.
4136 ASSERT(join != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004137 if (join->HasPredecessor()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004138 set_current_block(join);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004139 join->SetJoinId(expr->id());
4140 if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004141 } else {
4142 set_current_block(NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004143 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004144}
4145
4146
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004147void HGraphBuilder::TraceInline(Handle<JSFunction> target,
4148 Handle<JSFunction> caller,
4149 const char* reason) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004150 if (FLAG_trace_inlining) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004151 SmartPointer<char> target_name = target->shared()->DebugName()->ToCString();
4152 SmartPointer<char> caller_name = caller->shared()->DebugName()->ToCString();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004153 if (reason == NULL) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004154 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004155 } else {
4156 PrintF("Did not inline %s called from %s (%s).\n",
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004157 *target_name, *caller_name, reason);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004158 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004159 }
4160}
4161
4162
4163bool HGraphBuilder::TryInline(Call* expr) {
4164 if (!FLAG_use_inlining) return false;
4165
danno@chromium.org40cb8782011-05-25 07:58:50 +00004166 // The function call we are inlining is a method call if the call
4167 // is a property call.
4168 CallKind call_kind = (expr->expression()->AsProperty() == NULL)
4169 ? CALL_AS_FUNCTION
4170 : CALL_AS_METHOD;
4171
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004172 // Precondition: call is monomorphic and we have found a target with the
4173 // appropriate arity.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004174 Handle<JSFunction> caller = info()->closure();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004175 Handle<JSFunction> target = expr->target();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004176 Handle<SharedFunctionInfo> target_shared(target->shared());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004177
4178 // Do a quick check on source code length to avoid parsing large
4179 // inlining candidates.
4180 if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004181 TraceInline(target, caller, "target text too big");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004182 return false;
4183 }
4184
4185 // Target must be inlineable.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004186 if (!target->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004187 TraceInline(target, caller, "target not inlineable");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004188 return false;
4189 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004190
4191 // No context change required.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004192 CompilationInfo* outer_info = info();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004193 if (target->context() != outer_info->closure()->context() ||
4194 outer_info->scope()->contains_with() ||
4195 outer_info->scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004196 TraceInline(target, caller, "target requires context change");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004197 return false;
4198 }
4199
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004200 // Don't inline deeper than kMaxInliningLevels calls.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004201 HEnvironment* env = environment();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004202 int current_level = 1;
4203 while (env->outer() != NULL) {
4204 if (current_level == Compiler::kMaxInliningLevels) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004205 TraceInline(target, caller, "inline depth limit reached");
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004206 return false;
4207 }
4208 current_level++;
4209 env = env->outer();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004210 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004211
4212 // Don't inline recursive functions.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004213 if (*target_shared == outer_info->closure()->shared()) {
4214 TraceInline(target, caller, "target is recursive");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004215 return false;
4216 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004217
4218 // We don't want to add more than a certain number of nodes from inlining.
4219 if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004220 TraceInline(target, caller, "cumulative AST node limit reached");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004221 return false;
4222 }
4223
4224 int count_before = AstNode::Count();
4225
4226 // Parse and allocate variables.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004227 CompilationInfo target_info(target);
4228 if (!ParserApi::Parse(&target_info) ||
4229 !Scope::Analyze(&target_info)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004230 if (target_info.isolate()->has_pending_exception()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004231 // Parse or scope error, never optimize this function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004232 SetStackOverflow();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004233 target_shared->DisableOptimization(*target);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004234 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004235 TraceInline(target, caller, "parse failure");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004236 return false;
4237 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004238
4239 if (target_info.scope()->num_heap_slots() > 0) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004240 TraceInline(target, caller, "target has context-allocated variables");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004241 return false;
4242 }
4243 FunctionLiteral* function = target_info.function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004244
4245 // Count the number of AST nodes added by inlining this call.
4246 int nodes_added = AstNode::Count() - count_before;
4247 if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004248 TraceInline(target, caller, "target AST is too large");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004249 return false;
4250 }
4251
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004252 // Don't inline functions that uses the arguments object or that
4253 // have a mismatching number of parameters.
4254 int arity = expr->arguments()->length();
4255 if (function->scope()->arguments() != NULL ||
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004256 arity != target_shared->formal_parameter_count()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004257 TraceInline(target, caller, "target requires special argument handling");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004258 return false;
4259 }
4260
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004261 // All declarations must be inlineable.
4262 ZoneList<Declaration*>* decls = target_info.scope()->declarations();
4263 int decl_count = decls->length();
4264 for (int i = 0; i < decl_count; ++i) {
4265 if (!decls->at(i)->IsInlineable()) {
4266 TraceInline(target, caller, "target has non-trivial declaration");
4267 return false;
4268 }
4269 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004270 // All statements in the body must be inlineable.
4271 for (int i = 0, count = function->body()->length(); i < count; ++i) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004272 if (!function->body()->at(i)->IsInlineable()) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004273 TraceInline(target, caller, "target contains unsupported syntax");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004274 return false;
4275 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004276 }
4277
4278 // Generate the deoptimization data for the unoptimized version of
4279 // the target function if we don't already have it.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004280 if (!target_shared->has_deoptimization_support()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004281 // Note that we compile here using the same AST that we will use for
4282 // generating the optimized inline code.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004283 target_info.EnableDeoptimizationSupport();
4284 if (!FullCodeGenerator::MakeCode(&target_info)) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004285 TraceInline(target, caller, "could not generate deoptimization info");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004286 return false;
4287 }
4288 target_shared->EnableDeoptimizationSupport(*target_info.code());
4289 Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
4290 &target_info,
4291 target_shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004292 }
4293
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004294 // ----------------------------------------------------------------
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004295 // After this point, we've made a decision to inline this function (so
4296 // TryInline should always return true).
4297
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004298 // Save the pending call context and type feedback oracle. Set up new ones
4299 // for the inlined function.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004300 ASSERT(target_shared->has_deoptimization_support());
4301 TypeFeedbackOracle target_oracle(
4302 Handle<Code>(target_shared->code()),
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004303 Handle<Context>(target->context()->global_context()));
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004304 FunctionState target_state(this, &target_info, &target_oracle);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004305
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004306 HConstant* undefined = graph()->GetConstantUndefined();
4307 HEnvironment* inner_env =
lrn@chromium.org1c092762011-05-09 09:42:16 +00004308 environment()->CopyForInlining(target,
4309 function,
4310 HEnvironment::HYDROGEN,
danno@chromium.org40cb8782011-05-25 07:58:50 +00004311 undefined,
4312 call_kind);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004313 HBasicBlock* body_entry = CreateBasicBlock(inner_env);
4314 current_block()->Goto(body_entry);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004315 body_entry->SetJoinId(expr->ReturnId());
4316 set_current_block(body_entry);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004317 AddInstruction(new(zone()) HEnterInlined(target,
4318 function,
4319 call_kind));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004320 VisitDeclarations(target_info.scope()->declarations());
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004321 VisitStatements(function->body());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004322 if (HasStackOverflow()) {
4323 // Bail out if the inline function did, as we cannot residualize a call
4324 // instead.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004325 TraceInline(target, caller, "inline graph construction failed");
4326 target_shared->DisableOptimization(*target);
4327 inline_bailout_ = true;
danno@chromium.org160a7b02011-04-18 15:51:38 +00004328 return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004329 }
4330
4331 // Update inlined nodes count.
4332 inlined_count_ += nodes_added;
4333
ager@chromium.orgea91cc52011-05-23 06:06:11 +00004334 TraceInline(target, caller, NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004335
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004336 if (current_block() != NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004337 // Add a return of undefined if control can fall off the body. In a
4338 // test context, undefined is false.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004339 if (inlined_test_context() == NULL) {
4340 ASSERT(function_return() != NULL);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004341 ASSERT(call_context()->IsEffect() || call_context()->IsValue());
4342 if (call_context()->IsEffect()) {
4343 current_block()->Goto(function_return(), false);
4344 } else {
4345 current_block()->AddLeaveInlined(undefined, function_return());
4346 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004347 } else {
4348 // The graph builder assumes control can reach both branches of a
4349 // test, so we materialize the undefined value and test it rather than
4350 // simply jumping to the false target.
4351 //
4352 // TODO(3168478): refactor to avoid this.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004353 HBasicBlock* empty_true = graph()->CreateBasicBlock();
4354 HBasicBlock* empty_false = graph()->CreateBasicBlock();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004355 HTest* test = new(zone()) HTest(undefined, empty_true, empty_false);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004356 current_block()->Finish(test);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004357
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004358 empty_true->Goto(inlined_test_context()->if_true(), false);
4359 empty_false->Goto(inlined_test_context()->if_false(), false);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004360 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004361 }
4362
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004363 // Fix up the function exits.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004364 if (inlined_test_context() != NULL) {
4365 HBasicBlock* if_true = inlined_test_context()->if_true();
4366 HBasicBlock* if_false = inlined_test_context()->if_false();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004367
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004368 // Pop the return test context from the expression context stack.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004369 ASSERT(ast_context() == inlined_test_context());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004370 ClearInlinedTestContext();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004371
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004372 // Forward to the real test context.
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004373 if (if_true->HasPredecessor()) {
4374 if_true->SetJoinId(expr->id());
4375 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
4376 if_true->Goto(true_target, false);
4377 }
4378 if (if_false->HasPredecessor()) {
4379 if_false->SetJoinId(expr->id());
4380 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
4381 if_false->Goto(false_target, false);
4382 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004383 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004384
danno@chromium.org160a7b02011-04-18 15:51:38 +00004385 } else if (function_return()->HasPredecessor()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004386 function_return()->SetJoinId(expr->id());
4387 set_current_block(function_return());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004388 } else {
4389 set_current_block(NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004390 }
4391
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004392 return true;
4393}
4394
4395
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004396bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
4397 HValue* receiver,
4398 Handle<Map> receiver_map,
4399 CheckType check_type) {
4400 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004401 // Try to inline calls like Math.* as operations in the calling function.
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004402 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004403 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004404 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
4405 switch (id) {
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004406 case kStringCharCodeAt:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004407 case kStringCharAt:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004408 if (argument_count == 2 && check_type == STRING_CHECK) {
4409 HValue* index = Pop();
4410 HValue* string = Pop();
4411 ASSERT(!expr->holder().is_null());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004412 AddInstruction(new(zone()) HCheckPrototypeMaps(
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004413 oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
4414 expr->holder()));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00004415 HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
4416 if (id == kStringCharCodeAt) {
4417 ast_context()->ReturnInstruction(char_code, expr->id());
4418 return true;
4419 }
4420 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004421 HStringCharFromCode* result =
4422 new(zone()) HStringCharFromCode(char_code);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004423 ast_context()->ReturnInstruction(result, expr->id());
4424 return true;
4425 }
4426 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004427 case kMathRound:
4428 case kMathFloor:
4429 case kMathAbs:
4430 case kMathSqrt:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004431 case kMathLog:
whesse@chromium.org023421e2010-12-21 12:19:12 +00004432 case kMathSin:
4433 case kMathCos:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004434 if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
4435 AddCheckConstantFunction(expr, receiver, receiver_map, true);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004436 HValue* argument = Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004437 Drop(1); // Receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004438 HUnaryMathOperation* op = new(zone()) HUnaryMathOperation(argument, id);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004439 op->set_position(expr->position());
4440 ast_context()->ReturnInstruction(op, expr->id());
4441 return true;
4442 }
4443 break;
4444 case kMathPow:
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004445 if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
4446 AddCheckConstantFunction(expr, receiver, receiver_map, true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004447 HValue* right = Pop();
4448 HValue* left = Pop();
4449 Pop(); // Pop receiver.
4450 HInstruction* result = NULL;
4451 // Use sqrt() if exponent is 0.5 or -0.5.
4452 if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
4453 double exponent = HConstant::cast(right)->DoubleValue();
4454 if (exponent == 0.5) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004455 result = new(zone()) HUnaryMathOperation(left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004456 } else if (exponent == -0.5) {
4457 HConstant* double_one =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004458 new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
4459 Representation::Double());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004460 AddInstruction(double_one);
4461 HUnaryMathOperation* square_root =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004462 new(zone()) HUnaryMathOperation(left, kMathPowHalf);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004463 AddInstruction(square_root);
4464 // MathPowHalf doesn't have side effects so there's no need for
4465 // an environment simulation here.
4466 ASSERT(!square_root->HasSideEffects());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004467 result = new(zone()) HDiv(double_one, square_root);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004468 } else if (exponent == 2.0) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004469 result = new(zone()) HMul(left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004470 }
4471 } else if (right->IsConstant() &&
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004472 HConstant::cast(right)->HasInteger32Value() &&
4473 HConstant::cast(right)->Integer32Value() == 2) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004474 result = new(zone()) HMul(left, left);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004475 }
4476
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004477 if (result == NULL) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004478 result = new(zone()) HPower(left, right);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004479 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004480 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004481 return true;
4482 }
4483 break;
4484 default:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004485 // Not yet supported for inlining.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004486 break;
4487 }
4488 return false;
4489}
4490
4491
4492bool HGraphBuilder::TryCallApply(Call* expr) {
4493 Expression* callee = expr->expression();
4494 Property* prop = callee->AsProperty();
4495 ASSERT(prop != NULL);
4496
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004497 if (info()->scope()->arguments() == NULL) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004498
4499 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
4500 if (!name->IsEqualTo(CStrVector("apply"))) return false;
4501
4502 ZoneList<Expression*>* args = expr->arguments();
4503 if (args->length() != 2) return false;
4504
4505 VariableProxy* arg_two = args->at(1)->AsVariableProxy();
whesse@chromium.org023421e2010-12-21 12:19:12 +00004506 if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004507 HValue* arg_two_value = environment()->Lookup(arg_two->var());
4508 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
4509
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004510 if (!expr->IsMonomorphic() ||
4511 expr->check_type() != RECEIVER_MAP_CHECK) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004512
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00004513 // Our implementation of arguments (based on this stack frame or an
4514 // adapter below it) does not work for inlined functions.
4515 if (function_state()->outer() != NULL) {
4516 Bailout("Function.prototype.apply optimization in inlined function");
4517 return true;
4518 }
4519
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004520 // Found pattern f.apply(receiver, arguments).
4521 VisitForValue(prop->obj());
danno@chromium.org160a7b02011-04-18 15:51:38 +00004522 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004523 HValue* function = Pop();
4524 VisitForValue(args->at(0));
danno@chromium.org160a7b02011-04-18 15:51:38 +00004525 if (HasStackOverflow() || current_block() == NULL) return true;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004526 HValue* receiver = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004527 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
4528 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004529 AddCheckConstantFunction(expr,
4530 function,
4531 expr->GetReceiverTypes()->first(),
4532 true);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004533 HInstruction* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004534 new(zone()) HApplyArguments(function, receiver, length, elements);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004535 result->set_position(expr->position());
4536 ast_context()->ReturnInstruction(result, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004537 return true;
4538}
4539
4540
4541void HGraphBuilder::VisitCall(Call* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004542 ASSERT(!HasStackOverflow());
4543 ASSERT(current_block() != NULL);
4544 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004545 Expression* callee = expr->expression();
4546 int argument_count = expr->arguments()->length() + 1; // Plus receiver.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004547 HInstruction* call = NULL;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004548
4549 Property* prop = callee->AsProperty();
4550 if (prop != NULL) {
4551 if (!prop->key()->IsPropertyName()) {
4552 // Keyed function call.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004553 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004554
danno@chromium.org160a7b02011-04-18 15:51:38 +00004555 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004556 // Push receiver and key like the non-optimized code generator expects it.
4557 HValue* key = Pop();
4558 HValue* receiver = Pop();
4559 Push(key);
4560 Push(receiver);
4561
danno@chromium.org160a7b02011-04-18 15:51:38 +00004562 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004563
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004564 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004565 call = PreProcessCall(
4566 new(zone()) HCallKeyed(context, key, argument_count));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004567 call->set_position(expr->position());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004568 Drop(1); // Key.
4569 ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004570 return;
4571 }
4572
4573 // Named function call.
danno@chromium.org40cb8782011-05-25 07:58:50 +00004574 expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004575
4576 if (TryCallApply(expr)) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004577
danno@chromium.org160a7b02011-04-18 15:51:38 +00004578 CHECK_ALIVE(VisitForValue(prop->obj()));
4579 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004580
4581 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
4582
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004583 ZoneMapList* types = expr->GetReceiverTypes();
4584
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004585 HValue* receiver =
4586 environment()->ExpressionStackAt(expr->arguments()->length());
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004587 if (expr->IsMonomorphic()) {
4588 Handle<Map> receiver_map =
4589 (types == NULL) ? Handle<Map>::null() : types->first();
4590 if (TryInlineBuiltinFunction(expr,
4591 receiver,
4592 receiver_map,
4593 expr->check_type())) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004594 return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004595 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004596
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004597 if (CallStubCompiler::HasCustomCallGenerator(*expr->target()) ||
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004598 expr->check_type() != RECEIVER_MAP_CHECK) {
4599 // When the target has a custom call IC generator, use the IC,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004600 // because it is likely to generate better code. Also use the IC
4601 // when a primitive receiver check is required.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004602 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004603 call = PreProcessCall(
4604 new(zone()) HCallNamed(context, name, argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004605 } else {
4606 AddCheckConstantFunction(expr, receiver, receiver_map, true);
4607
danno@chromium.org160a7b02011-04-18 15:51:38 +00004608 if (TryInline(expr)) return;
4609 call = PreProcessCall(
4610 new(zone()) HCallConstantFunction(expr->target(),
4611 argument_count));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00004612 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004613 } else if (types != NULL && types->length() > 1) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00004614 ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004615 HandlePolymorphicCallNamed(expr, receiver, types, name);
4616 return;
4617
4618 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004619 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004620 call = PreProcessCall(
4621 new(zone()) HCallNamed(context, name, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004622 }
4623
4624 } else {
4625 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
4626 bool global_call = (var != NULL) && var->is_global() && !var->is_this();
4627
4628 if (!global_call) {
4629 ++argument_count;
danno@chromium.org160a7b02011-04-18 15:51:38 +00004630 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004631 }
4632
4633 if (global_call) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004634 bool known_global_function = false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004635 // If there is a global property cell for the name at compile time and
4636 // access check is not enabled we assume that the function will not change
4637 // and generate optimized code for calling the function.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004638 LookupResult lookup;
4639 GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
4640 if (type == kUseCell &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004641 !info()->global_object()->IsAccessCheckNeeded()) {
4642 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004643 known_global_function = expr->ComputeGlobalTarget(global, &lookup);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00004644 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004645 if (known_global_function) {
4646 // Push the global object instead of the global receiver because
4647 // code generated by the full code generator expects it.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004648 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004649 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004650 PushAndAdd(global_object);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004651 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004652
danno@chromium.org160a7b02011-04-18 15:51:38 +00004653 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004654 HValue* function = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004655 AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004656
4657 // Replace the global object with the global receiver.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004658 HGlobalReceiver* global_receiver =
4659 new(zone()) HGlobalReceiver(global_object);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004660 // Index of the receiver from the top of the expression stack.
4661 const int receiver_index = argument_count - 1;
4662 AddInstruction(global_receiver);
4663 ASSERT(environment()->ExpressionStackAt(receiver_index)->
4664 IsGlobalObject());
4665 environment()->SetExpressionStackAt(receiver_index, global_receiver);
4666
danno@chromium.org160a7b02011-04-18 15:51:38 +00004667 if (TryInline(expr)) return;
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004668 call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
danno@chromium.org160a7b02011-04-18 15:51:38 +00004669 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004670 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004671 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004672 PushAndAdd(new(zone()) HGlobalObject(context));
danno@chromium.org160a7b02011-04-18 15:51:38 +00004673 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004674
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004675 call = PreProcessCall(new(zone()) HCallGlobal(context,
danno@chromium.org40cb8782011-05-25 07:58:50 +00004676 var->name(),
4677 argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004678 }
4679
4680 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004681 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004682 HGlobalObject* global_object = new(zone()) HGlobalObject(context);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00004683 AddInstruction(global_object);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004684 PushAndAdd(new(zone()) HGlobalReceiver(global_object));
danno@chromium.org160a7b02011-04-18 15:51:38 +00004685 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004686
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004687 call = PreProcessCall(new(zone()) HCallFunction(context, argument_count));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004688 }
4689 }
4690
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004691 call->set_position(expr->position());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004692 ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004693}
4694
4695
4696void HGraphBuilder::VisitCallNew(CallNew* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004697 ASSERT(!HasStackOverflow());
4698 ASSERT(current_block() != NULL);
4699 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004700 // The constructor function is also used as the receiver argument to the
4701 // JS construct call builtin.
danno@chromium.org160a7b02011-04-18 15:51:38 +00004702 CHECK_ALIVE(VisitForValue(expr->expression()));
4703 CHECK_ALIVE(VisitExpressions(expr->arguments()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004704
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004705 HValue* context = environment()->LookupContext();
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004706
4707 // The constructor is both an operand to the instruction and an argument
4708 // to the construct call.
4709 int arg_count = expr->arguments()->length() + 1; // Plus constructor.
4710 HValue* constructor = environment()->ExpressionStackAt(arg_count - 1);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004711 HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004712 call->set_position(expr->position());
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00004713 PreProcessCall(call);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004714 ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004715}
4716
4717
4718// Support for generating inlined runtime functions.
4719
4720// Lookup table for generators for runtime calls that are generated inline.
4721// Elements of the table are member pointers to functions of HGraphBuilder.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004722#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004723 &HGraphBuilder::Generate##Name,
4724
4725const HGraphBuilder::InlineFunctionGenerator
4726 HGraphBuilder::kInlineFunctionGenerators[] = {
4727 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
4728 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
4729};
4730#undef INLINE_FUNCTION_GENERATOR_ADDRESS
4731
4732
4733void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004734 ASSERT(!HasStackOverflow());
4735 ASSERT(current_block() != NULL);
4736 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004737 if (expr->is_jsruntime()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004738 return Bailout("call to a JavaScript runtime function");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004739 }
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004740
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004741 const Runtime::Function* function = expr->function();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004742 ASSERT(function != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004743 if (function->intrinsic_type == Runtime::INLINE) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004744 ASSERT(expr->name()->length() > 0);
4745 ASSERT(expr->name()->Get(0) == '_');
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004746 // Call to an inline function.
4747 int lookup_index = static_cast<int>(function->function_id) -
4748 static_cast<int>(Runtime::kFirstInlineFunction);
4749 ASSERT(lookup_index >= 0);
4750 ASSERT(static_cast<size_t>(lookup_index) <
4751 ARRAY_SIZE(kInlineFunctionGenerators));
4752 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
4753
4754 // Call the inline code generator using the pointer-to-member.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004755 (this->*generator)(expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004756 } else {
4757 ASSERT(function->intrinsic_type == Runtime::RUNTIME);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004758 CHECK_ALIVE(VisitArgumentList(expr->arguments()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004759
4760 Handle<String> name = expr->name();
4761 int argument_count = expr->arguments()->length();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004762 HCallRuntime* call =
4763 new(zone()) HCallRuntime(name, function, argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004764 call->set_position(RelocInfo::kNoPosition);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00004765 Drop(argument_count);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004766 ast_context()->ReturnInstruction(call, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004767 }
4768}
4769
4770
4771void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004772 ASSERT(!HasStackOverflow());
4773 ASSERT(current_block() != NULL);
4774 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004775 switch (expr->op()) {
4776 case Token::DELETE: return VisitDelete(expr);
4777 case Token::VOID: return VisitVoid(expr);
4778 case Token::TYPEOF: return VisitTypeof(expr);
4779 case Token::ADD: return VisitAdd(expr);
4780 case Token::SUB: return VisitSub(expr);
4781 case Token::BIT_NOT: return VisitBitNot(expr);
4782 case Token::NOT: return VisitNot(expr);
4783 default: UNREACHABLE();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004784 }
4785}
4786
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004787void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
4788 Property* prop = expr->expression()->AsProperty();
4789 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
4790 if (prop == NULL && var == NULL) {
4791 // Result of deleting non-property, non-variable reference is true.
4792 // Evaluate the subexpression for side effects.
4793 CHECK_ALIVE(VisitForEffect(expr->expression()));
4794 ast_context()->ReturnValue(graph()->GetConstantTrue());
4795 } else if (var != NULL &&
4796 !var->is_global() &&
4797 var->AsSlot() != NULL &&
4798 var->AsSlot()->type() != Slot::LOOKUP) {
4799 // Result of deleting non-global, non-dynamic variables is false.
4800 // The subexpression does not have side effects.
4801 ast_context()->ReturnValue(graph()->GetConstantFalse());
4802 } else if (prop != NULL) {
4803 if (prop->is_synthetic()) {
4804 // Result of deleting parameters is false, even when they rewrite
4805 // to accesses on the arguments object.
4806 ast_context()->ReturnValue(graph()->GetConstantFalse());
4807 } else {
4808 CHECK_ALIVE(VisitForValue(prop->obj()));
4809 CHECK_ALIVE(VisitForValue(prop->key()));
4810 HValue* key = Pop();
4811 HValue* obj = Pop();
4812 HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
4813 ast_context()->ReturnInstruction(instr, expr->id());
4814 }
4815 } else if (var->is_global()) {
4816 Bailout("delete with global variable");
4817 } else {
4818 Bailout("delete with non-global variable");
4819 }
4820}
4821
4822
4823void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
4824 CHECK_ALIVE(VisitForEffect(expr->expression()));
4825 ast_context()->ReturnValue(graph()->GetConstantUndefined());
4826}
4827
4828
4829void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
4830 CHECK_ALIVE(VisitForTypeOf(expr->expression()));
4831 HValue* value = Pop();
4832 ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
4833}
4834
4835
4836void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
4837 CHECK_ALIVE(VisitForValue(expr->expression()));
4838 HValue* value = Pop();
4839 HInstruction* instr = new(zone()) HMul(value, graph_->GetConstant1());
4840 ast_context()->ReturnInstruction(instr, expr->id());
4841}
4842
4843
4844void HGraphBuilder::VisitSub(UnaryOperation* expr) {
4845 CHECK_ALIVE(VisitForValue(expr->expression()));
4846 HValue* value = Pop();
4847 HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004848 TypeInfo info = oracle()->UnaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004849 if (info.IsUninitialized()) {
4850 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004851 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004852 info = TypeInfo::Unknown();
4853 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004854 Representation rep = ToRepresentation(info);
4855 TraceRepresentation(expr->op(), info, instr, rep);
4856 instr->AssumeRepresentation(rep);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004857 ast_context()->ReturnInstruction(instr, expr->id());
4858}
4859
4860
4861void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
4862 CHECK_ALIVE(VisitForValue(expr->expression()));
4863 HValue* value = Pop();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004864 TypeInfo info = oracle()->UnaryType(expr);
4865 if (info.IsUninitialized()) {
4866 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00004867 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004868 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004869 HInstruction* instr = new(zone()) HBitNot(value);
4870 ast_context()->ReturnInstruction(instr, expr->id());
4871}
4872
4873
4874void HGraphBuilder::VisitNot(UnaryOperation* expr) {
4875 // TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
4876 if (ast_context()->IsTest()) {
4877 TestContext* context = TestContext::cast(ast_context());
4878 VisitForControl(expr->expression(),
4879 context->if_false(),
4880 context->if_true());
4881 return;
4882 }
4883
4884 if (ast_context()->IsEffect()) {
4885 VisitForEffect(expr->expression());
4886 return;
4887 }
4888
4889 ASSERT(ast_context()->IsValue());
4890 HBasicBlock* materialize_false = graph()->CreateBasicBlock();
4891 HBasicBlock* materialize_true = graph()->CreateBasicBlock();
4892 CHECK_BAILOUT(VisitForControl(expr->expression(),
4893 materialize_false,
4894 materialize_true));
4895
4896 if (materialize_false->HasPredecessor()) {
4897 materialize_false->SetJoinId(expr->expression()->id());
4898 set_current_block(materialize_false);
4899 Push(graph()->GetConstantFalse());
4900 } else {
4901 materialize_false = NULL;
4902 }
4903
4904 if (materialize_true->HasPredecessor()) {
4905 materialize_true->SetJoinId(expr->expression()->id());
4906 set_current_block(materialize_true);
4907 Push(graph()->GetConstantTrue());
4908 } else {
4909 materialize_true = NULL;
4910 }
4911
4912 HBasicBlock* join =
4913 CreateJoin(materialize_false, materialize_true, expr->id());
4914 set_current_block(join);
4915 if (join != NULL) ast_context()->ReturnValue(Pop());
4916}
4917
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004918
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004919HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004920 CountOperation* expr) {
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004921 // The input to the count operation is on top of the expression stack.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004922 TypeInfo info = oracle()->IncrementType(expr);
4923 Representation rep = ToRepresentation(info);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00004924 if (rep.IsTagged()) {
4925 rep = Representation::Integer32();
4926 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004927
4928 if (returns_original_input) {
4929 // We need an explicit HValue representing ToNumber(input). The
4930 // actual HChange instruction we need is (sometimes) added in a later
4931 // phase, so it is not available now to be used as an input to HAdd and
4932 // as the return value.
4933 HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
4934 AddInstruction(number_input);
4935 Push(number_input);
4936 }
4937
4938 // The addition has no side effects, so we do not need
4939 // to simulate the expression stack after this instruction.
4940 // Any later failures deopt to the load of the input or earlier.
4941 HConstant* delta = (expr->op() == Token::INC)
4942 ? graph_->GetConstant1()
4943 : graph_->GetConstantMinus1();
4944 HInstruction* instr = new(zone()) HAdd(Top(), delta);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004945 TraceRepresentation(expr->op(), info, instr, rep);
4946 instr->AssumeRepresentation(rep);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004947 AddInstruction(instr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004948 return instr;
4949}
4950
4951
4952void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004953 ASSERT(!HasStackOverflow());
4954 ASSERT(current_block() != NULL);
4955 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00004956 Expression* target = expr->expression();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004957 VariableProxy* proxy = target->AsVariableProxy();
4958 Variable* var = proxy->AsVariable();
4959 Property* prop = target->AsProperty();
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004960 if (var == NULL && prop == NULL) {
4961 return Bailout("invalid lhs in count operation");
4962 }
4963
4964 // Match the full code generator stack by simulating an extra stack
4965 // element for postfix operations in a non-effect context. The return
4966 // value is ToNumber(input).
4967 bool returns_original_input =
4968 expr->is_postfix() && !ast_context()->IsEffect();
4969 HValue* input = NULL; // ToNumber(original_input).
4970 HValue* after = NULL; // The result after incrementing or decrementing.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004971
4972 if (var != NULL) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004973 if (var->mode() == Variable::CONST) {
4974 return Bailout("unsupported count operation with const");
4975 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004976 // Argument of the count operation is a variable, not a property.
4977 ASSERT(prop == NULL);
danno@chromium.org160a7b02011-04-18 15:51:38 +00004978 CHECK_ALIVE(VisitForValue(target));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004979
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004980 after = BuildIncrement(returns_original_input, expr);
4981 input = returns_original_input ? Top() : Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004982 Push(after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004983
4984 if (var->is_global()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004985 HandleGlobalVariableAssignment(var,
4986 after,
4987 expr->position(),
4988 expr->AssignmentId());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00004989 } else if (var->IsStackAllocated()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004990 Bind(var, after);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00004991 } else if (var->IsContextSlot()) {
4992 HValue* context = BuildContextChainWalk(var);
4993 int index = var->AsSlot()->index();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00004994 HStoreContextSlot* instr =
4995 new(zone()) HStoreContextSlot(context, index, after);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00004996 AddInstruction(instr);
4997 if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
4998 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00004999 return Bailout("lookup variable in count operation");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005000 }
5001
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005002 } else {
5003 // Argument of the count operation is a property.
5004 ASSERT(prop != NULL);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005005 prop->RecordTypeFeedback(oracle());
5006
5007 if (prop->key()->IsPropertyName()) {
5008 // Named property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005009 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005010
danno@chromium.org160a7b02011-04-18 15:51:38 +00005011 CHECK_ALIVE(VisitForValue(prop->obj()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005012 HValue* obj = Top();
5013
5014 HInstruction* load = NULL;
5015 if (prop->IsMonomorphic()) {
5016 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
5017 Handle<Map> map = prop->GetReceiverTypes()->first();
5018 load = BuildLoadNamed(obj, prop, map, name);
5019 } else {
5020 load = BuildLoadNamedGeneric(obj, prop);
5021 }
5022 PushAndAdd(load);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005023 if (load->HasSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005024
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005025 after = BuildIncrement(returns_original_input, expr);
5026 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005027
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005028 HInstruction* store = BuildStoreNamed(obj, after, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005029 AddInstruction(store);
5030
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005031 // Overwrite the receiver in the bailout environment with the result
5032 // of the operation, and the placeholder with the original value if
5033 // necessary.
5034 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005035 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005036 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005037
5038 } else {
5039 // Keyed property.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005040 if (returns_original_input) Push(graph_->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005041
danno@chromium.org160a7b02011-04-18 15:51:38 +00005042 CHECK_ALIVE(VisitForValue(prop->obj()));
5043 CHECK_ALIVE(VisitForValue(prop->key()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005044 HValue* obj = environment()->ExpressionStackAt(1);
5045 HValue* key = environment()->ExpressionStackAt(0);
5046
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005047 HInstruction* load = BuildLoadKeyed(obj, key, prop);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005048 PushAndAdd(load);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005049 if (load->HasSideEffects()) AddSimulate(expr->CountId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005050
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005051 after = BuildIncrement(returns_original_input, expr);
5052 input = Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005053
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00005054 expr->RecordTypeFeedback(oracle());
5055 HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005056 AddInstruction(store);
5057
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005058 // Drop the key from the bailout environment. Overwrite the receiver
5059 // with the result of the operation, and the placeholder with the
5060 // original value if necessary.
5061 Drop(1);
5062 environment()->SetExpressionStackAt(0, after);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005063 if (returns_original_input) environment()->SetExpressionStackAt(1, input);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005064 if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005065 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005066 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005067
5068 Drop(returns_original_input ? 2 : 1);
5069 ast_context()->ReturnValue(expr->is_postfix() ? input : after);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005070}
5071
5072
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005073HCompareSymbolEq* HGraphBuilder::BuildSymbolCompare(HValue* left,
5074 HValue* right,
5075 Token::Value op) {
5076 ASSERT(op == Token::EQ || op == Token::EQ_STRICT);
5077 AddInstruction(new(zone()) HCheckNonSmi(left));
5078 AddInstruction(HCheckInstanceType::NewIsSymbol(left));
5079 AddInstruction(new(zone()) HCheckNonSmi(right));
5080 AddInstruction(HCheckInstanceType::NewIsSymbol(right));
5081 return new(zone()) HCompareSymbolEq(left, right, op);
5082}
5083
5084
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005085HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
5086 HValue* index) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005087 AddInstruction(new(zone()) HCheckNonSmi(string));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005088 AddInstruction(HCheckInstanceType::NewIsString(string));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005089 HStringLength* length = new(zone()) HStringLength(string);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005090 AddInstruction(length);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005091 HInstruction* checked_index =
5092 AddInstruction(new(zone()) HBoundsCheck(index, length));
5093 return new(zone()) HStringCharCodeAt(string, checked_index);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005094}
5095
5096
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005097HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
5098 HValue* left,
5099 HValue* right) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005100 TypeInfo info = oracle()->BinaryType(expr);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005101 if (info.IsUninitialized()) {
5102 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005103 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005104 info = TypeInfo::Unknown();
5105 }
5106 HInstruction* instr = NULL;
5107 switch (expr->op()) {
5108 case Token::ADD:
5109 if (info.IsString()) {
5110 AddInstruction(new(zone()) HCheckNonSmi(left));
5111 AddInstruction(HCheckInstanceType::NewIsString(left));
5112 AddInstruction(new(zone()) HCheckNonSmi(right));
5113 AddInstruction(HCheckInstanceType::NewIsString(right));
5114 instr = new(zone()) HStringAdd(left, right);
5115 } else {
5116 instr = new(zone()) HAdd(left, right);
5117 }
5118 break;
5119 case Token::SUB:
5120 instr = new(zone()) HSub(left, right);
5121 break;
5122 case Token::MUL:
5123 instr = new(zone()) HMul(left, right);
5124 break;
5125 case Token::MOD:
5126 instr = new(zone()) HMod(left, right);
5127 break;
5128 case Token::DIV:
5129 instr = new(zone()) HDiv(left, right);
5130 break;
5131 case Token::BIT_XOR:
5132 instr = new(zone()) HBitXor(left, right);
5133 break;
5134 case Token::BIT_AND:
5135 instr = new(zone()) HBitAnd(left, right);
5136 break;
5137 case Token::BIT_OR:
5138 instr = new(zone()) HBitOr(left, right);
5139 break;
5140 case Token::SAR:
5141 instr = new(zone()) HSar(left, right);
5142 break;
5143 case Token::SHR:
5144 instr = new(zone()) HShr(left, right);
5145 break;
5146 case Token::SHL:
5147 instr = new(zone()) HShl(left, right);
5148 break;
5149 default:
5150 UNREACHABLE();
5151 }
5152
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005153 // If we hit an uninitialized binary op stub we will get type info
5154 // for a smi operation. If one of the operands is a constant string
5155 // do not generate code assuming it is a smi operation.
5156 if (info.IsSmi() &&
5157 ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) ||
5158 (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
5159 return instr;
5160 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005161 Representation rep = ToRepresentation(info);
5162 // We only generate either int32 or generic tagged bitwise operations.
5163 if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) {
5164 rep = Representation::Integer32();
5165 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005166 TraceRepresentation(expr->op(), info, instr, rep);
5167 instr->AssumeRepresentation(rep);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005168 return instr;
5169}
5170
5171
5172// Check for the form (%_ClassOf(foo) === 'BarClass').
5173static bool IsClassOfTest(CompareOperation* expr) {
5174 if (expr->op() != Token::EQ_STRICT) return false;
5175 CallRuntime* call = expr->left()->AsCallRuntime();
5176 if (call == NULL) return false;
5177 Literal* literal = expr->right()->AsLiteral();
5178 if (literal == NULL) return false;
5179 if (!literal->handle()->IsString()) return false;
5180 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false;
5181 ASSERT(call->arguments()->length() == 1);
5182 return true;
5183}
5184
5185
5186void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005187 ASSERT(!HasStackOverflow());
5188 ASSERT(current_block() != NULL);
5189 ASSERT(current_block()->HasPredecessor());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005190 switch (expr->op()) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005191 case Token::COMMA:
5192 return VisitComma(expr);
5193 case Token::OR:
5194 case Token::AND:
5195 return VisitLogicalExpression(expr);
5196 default:
5197 return VisitArithmeticExpression(expr);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005198 }
5199}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005200
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005201
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005202void HGraphBuilder::VisitComma(BinaryOperation* expr) {
5203 CHECK_ALIVE(VisitForEffect(expr->left()));
5204 // Visit the right subexpression in the same AST context as the entire
5205 // expression.
5206 Visit(expr->right());
5207}
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005208
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005209
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005210void HGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
5211 bool is_logical_and = expr->op() == Token::AND;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005212 if (ast_context()->IsTest()) {
5213 TestContext* context = TestContext::cast(ast_context());
5214 // Translate left subexpression.
5215 HBasicBlock* eval_right = graph()->CreateBasicBlock();
5216 if (is_logical_and) {
5217 CHECK_BAILOUT(VisitForControl(expr->left(),
5218 eval_right,
5219 context->if_false()));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005220 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005221 CHECK_BAILOUT(VisitForControl(expr->left(),
5222 context->if_true(),
5223 eval_right));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005224 }
5225
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005226 // Translate right subexpression by visiting it in the same AST
5227 // context as the entire expression.
5228 if (eval_right->HasPredecessor()) {
5229 eval_right->SetJoinId(expr->RightId());
5230 set_current_block(eval_right);
5231 Visit(expr->right());
5232 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005233
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005234 } else if (ast_context()->IsValue()) {
5235 CHECK_ALIVE(VisitForValue(expr->left()));
5236 ASSERT(current_block() != NULL);
5237
5238 // We need an extra block to maintain edge-split form.
5239 HBasicBlock* empty_block = graph()->CreateBasicBlock();
5240 HBasicBlock* eval_right = graph()->CreateBasicBlock();
5241 HTest* test = is_logical_and
5242 ? new(zone()) HTest(Top(), eval_right, empty_block)
5243 : new(zone()) HTest(Top(), empty_block, eval_right);
5244 current_block()->Finish(test);
5245
5246 set_current_block(eval_right);
5247 Drop(1); // Value of the left subexpression.
5248 CHECK_BAILOUT(VisitForValue(expr->right()));
5249
5250 HBasicBlock* join_block =
5251 CreateJoin(empty_block, current_block(), expr->id());
5252 set_current_block(join_block);
5253 ast_context()->ReturnValue(Pop());
5254
5255 } else {
5256 ASSERT(ast_context()->IsEffect());
5257 // In an effect context, we don't need the value of the left subexpression,
5258 // only its control flow and side effects. We need an extra block to
5259 // maintain edge-split form.
5260 HBasicBlock* empty_block = graph()->CreateBasicBlock();
5261 HBasicBlock* right_block = graph()->CreateBasicBlock();
5262 if (is_logical_and) {
5263 CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
5264 } else {
5265 CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
5266 }
5267
5268 // TODO(kmillikin): Find a way to fix this. It's ugly that there are
5269 // actually two empty blocks (one here and one inserted by
5270 // TestContext::BuildBranch, and that they both have an HSimulate though the
5271 // second one is not a merge node, and that we really have no good AST ID to
5272 // put on that first HSimulate.
5273
5274 if (empty_block->HasPredecessor()) {
5275 empty_block->SetJoinId(expr->id());
5276 } else {
5277 empty_block = NULL;
5278 }
5279
5280 if (right_block->HasPredecessor()) {
5281 right_block->SetJoinId(expr->RightId());
5282 set_current_block(right_block);
5283 CHECK_BAILOUT(VisitForEffect(expr->right()));
5284 right_block = current_block();
5285 } else {
5286 right_block = NULL;
5287 }
5288
5289 HBasicBlock* join_block =
5290 CreateJoin(empty_block, right_block, expr->id());
5291 set_current_block(join_block);
5292 // We did not materialize any value in the predecessor environments,
5293 // so there is no need to handle it here.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005294 }
5295}
5296
5297
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005298void HGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005299 CHECK_ALIVE(VisitForValue(expr->left()));
5300 CHECK_ALIVE(VisitForValue(expr->right()));
5301 HValue* right = Pop();
5302 HValue* left = Pop();
5303 HInstruction* instr = BuildBinaryOperation(expr, left, right);
5304 instr->set_position(expr->position());
5305 ast_context()->ReturnInstruction(instr, expr->id());
5306}
5307
5308
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005309void HGraphBuilder::TraceRepresentation(Token::Value op,
5310 TypeInfo info,
5311 HValue* value,
5312 Representation rep) {
5313 if (!FLAG_trace_representation) return;
5314 // TODO(svenpanne) Under which circumstances are we actually not flexible?
5315 // At first glance, this looks a bit weird...
5316 bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
5317 PrintF("Operation %s has type info %s, %schange representation assumption "
5318 "for %s (ID %d) from %s to %s\n",
5319 Token::Name(op),
5320 info.ToString(),
5321 flexible ? "" : " DO NOT ",
5322 value->Mnemonic(),
5323 graph_->GetMaximumValueID(),
5324 value->representation().Mnemonic(),
5325 rep.Mnemonic());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005326}
5327
5328
5329Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
5330 if (info.IsSmi()) return Representation::Integer32();
5331 if (info.IsInteger32()) return Representation::Integer32();
5332 if (info.IsDouble()) return Representation::Double();
5333 if (info.IsNumber()) return Representation::Double();
5334 return Representation::Tagged();
5335}
5336
5337
5338void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005339 ASSERT(!HasStackOverflow());
5340 ASSERT(current_block() != NULL);
5341 ASSERT(current_block()->HasPredecessor());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005342 if (IsClassOfTest(expr)) {
5343 CallRuntime* call = expr->left()->AsCallRuntime();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005344 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005345 HValue* value = Pop();
5346 Literal* literal = expr->right()->AsLiteral();
5347 Handle<String> rhs = Handle<String>::cast(literal->handle());
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005348 HInstruction* instr = new(zone()) HClassOfTest(value, rhs);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005349 instr->set_position(expr->position());
5350 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005351 return;
5352 }
5353
5354 // Check for the pattern: typeof <expression> == <string literal>.
5355 UnaryOperation* left_unary = expr->left()->AsUnaryOperation();
5356 Literal* right_literal = expr->right()->AsLiteral();
5357 if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) &&
5358 left_unary != NULL && left_unary->op() == Token::TYPEOF &&
5359 right_literal != NULL && right_literal->handle()->IsString()) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005360 CHECK_ALIVE(VisitForTypeOf(left_unary->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005361 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005362 HInstruction* instr = new(zone()) HTypeofIs(left,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005363 Handle<String>::cast(right_literal->handle()));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005364 instr->set_position(expr->position());
5365 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005366 return;
5367 }
5368
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005369 TypeInfo type_info = oracle()->CompareType(expr);
5370 // Check if this expression was ever executed according to type feedback.
5371 if (type_info.IsUninitialized()) {
5372 AddInstruction(new(zone()) HSoftDeoptimize);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00005373 current_block()->MarkAsDeoptimizing();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00005374 type_info = TypeInfo::Unknown();
5375 }
5376
danno@chromium.org160a7b02011-04-18 15:51:38 +00005377 CHECK_ALIVE(VisitForValue(expr->left()));
5378 CHECK_ALIVE(VisitForValue(expr->right()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005379
5380 HValue* right = Pop();
5381 HValue* left = Pop();
5382 Token::Value op = expr->op();
5383
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005384 HInstruction* instr = NULL;
5385 if (op == Token::INSTANCEOF) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005386 // Check to see if the rhs of the instanceof is a global function not
5387 // residing in new space. If it is we assume that the function will stay the
5388 // same.
5389 Handle<JSFunction> target = Handle<JSFunction>::null();
5390 Variable* var = expr->right()->AsVariableProxy()->AsVariable();
5391 bool global_function = (var != NULL) && var->is_global() && !var->is_this();
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005392 if (global_function &&
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005393 info()->has_global_object() &&
5394 !info()->global_object()->IsAccessCheckNeeded()) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005395 Handle<String> name = var->name();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005396 Handle<GlobalObject> global(info()->global_object());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005397 LookupResult lookup;
5398 global->Lookup(*name, &lookup);
5399 if (lookup.IsProperty() &&
5400 lookup.type() == NORMAL &&
5401 lookup.GetValue()->IsJSFunction()) {
5402 Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
5403 // If the function is in new space we assume it's more likely to
5404 // change and thus prefer the general IC code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005405 if (!isolate()->heap()->InNewSpace(*candidate)) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005406 target = candidate;
5407 }
5408 }
5409 }
5410
5411 // If the target is not null we have found a known global function that is
5412 // assumed to stay the same for this instanceof.
5413 if (target.is_null()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005414 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005415 instr = new(zone()) HInstanceOf(context, left, right);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005416 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005417 AddInstruction(new(zone()) HCheckFunction(right, target));
5418 instr = new(zone()) HInstanceOfKnownGlobal(left, target);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005419 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005420 } else if (op == Token::IN) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00005421 instr = new(zone()) HIn(left, right);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005422 } else if (type_info.IsNonPrimitive()) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005423 switch (op) {
5424 case Token::EQ:
5425 case Token::EQ_STRICT: {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005426 AddInstruction(new(zone()) HCheckNonSmi(left));
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005427 AddInstruction(HCheckInstanceType::NewIsSpecObject(left));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005428 AddInstruction(new(zone()) HCheckNonSmi(right));
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005429 AddInstruction(HCheckInstanceType::NewIsSpecObject(right));
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005430 instr = new(zone()) HCompareJSObjectEq(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005431 break;
5432 }
5433 default:
danno@chromium.org160a7b02011-04-18 15:51:38 +00005434 return Bailout("Unsupported non-primitive compare");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005435 break;
5436 }
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005437 } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
5438 (op == Token::EQ || op == Token::EQ_STRICT)) {
5439 instr = BuildSymbolCompare(left, right, op);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005440 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005441 HCompare* compare = new(zone()) HCompare(left, right, op);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00005442 Representation r = ToRepresentation(type_info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005443 compare->SetInputRepresentation(r);
5444 instr = compare;
5445 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005446 instr->set_position(expr->position());
5447 ast_context()->ReturnInstruction(instr, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005448}
5449
5450
5451void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005452 ASSERT(!HasStackOverflow());
5453 ASSERT(current_block() != NULL);
5454 ASSERT(current_block()->HasPredecessor());
5455 CHECK_ALIVE(VisitForValue(expr->expression()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005456
5457 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005458 HIsNull* compare = new(zone()) HIsNull(value, expr->is_strict());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005459 ast_context()->ReturnInstruction(compare, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005460}
5461
5462
5463void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005464 ASSERT(!HasStackOverflow());
5465 ASSERT(current_block() != NULL);
5466 ASSERT(current_block()->HasPredecessor());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005467 HThisFunction* self = new(zone()) HThisFunction;
5468 return ast_context()->ReturnInstruction(self, expr->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005469}
5470
5471
5472void HGraphBuilder::VisitDeclaration(Declaration* decl) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005473 // We support only declarations that do not require code generation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005474 Variable* var = decl->proxy()->var();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005475 if (!var->IsStackAllocated() || decl->fun() != NULL) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005476 return Bailout("unsupported declaration");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005477 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005478
5479 if (decl->mode() == Variable::CONST) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00005480 ASSERT(var->IsStackAllocated());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005481 environment()->Bind(var, graph()->GetConstantHole());
5482 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005483}
5484
5485
5486// Generators for inline runtime functions.
5487// Support for types.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005488void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
5489 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005490 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005491 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005492 HIsSmi* result = new(zone()) HIsSmi(value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005493 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005494}
5495
5496
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005497void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
5498 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005499 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005500 HValue* value = Pop();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005501 HHasInstanceType* result =
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00005502 new(zone()) HHasInstanceType(value,
5503 FIRST_SPEC_OBJECT_TYPE,
5504 LAST_SPEC_OBJECT_TYPE);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005505 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005506}
5507
5508
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005509void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
5510 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005511 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005512 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005513 HHasInstanceType* result =
5514 new(zone()) HHasInstanceType(value, JS_FUNCTION_TYPE);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005515 ast_context()->ReturnInstruction(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005516}
5517
5518
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005519void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
5520 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005521 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005522 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005523 HHasCachedArrayIndex* result = new(zone()) HHasCachedArrayIndex(value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005524 ast_context()->ReturnInstruction(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005525}
5526
5527
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005528void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
5529 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005530 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005531 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005532 HHasInstanceType* result = new(zone()) HHasInstanceType(value, JS_ARRAY_TYPE);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005533 ast_context()->ReturnInstruction(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005534}
5535
5536
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005537void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
5538 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005539 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005540 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005541 HHasInstanceType* result =
5542 new(zone()) HHasInstanceType(value, JS_REGEXP_TYPE);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005543 ast_context()->ReturnInstruction(result, call->id());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005544}
5545
5546
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005547void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
5548 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005549 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005550 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005551 HIsObject* test = new(zone()) HIsObject(value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005552 ast_context()->ReturnInstruction(test, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005553}
5554
5555
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005556void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005557 return Bailout("inlined runtime function: IsNonNegativeSmi");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005558}
5559
5560
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005561void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005562 ASSERT(call->arguments()->length() == 1);
5563 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5564 HValue* value = Pop();
5565 ast_context()->ReturnInstruction(new(zone()) HIsUndetectable(value),
5566 call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005567}
5568
5569
5570void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005571 CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005572 return Bailout(
5573 "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005574}
5575
5576
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00005577// Support for construct call checks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005578void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
5579 ASSERT(call->arguments()->length() == 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005580 if (function_state()->outer() != NULL) {
5581 // We are generating graph for inlined function. Currently
5582 // constructor inlining is not supported and we can just return
5583 // false from %_IsConstructCall().
5584 ast_context()->ReturnValue(graph()->GetConstantFalse());
5585 } else {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005586 ast_context()->ReturnInstruction(new(zone()) HIsConstructCall, call->id());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005587 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005588}
5589
5590
5591// Support for arguments.length and arguments[?].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005592void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00005593 // Our implementation of arguments (based on this stack frame or an
5594 // adapter below it) does not work for inlined functions. This runtime
5595 // function is blacklisted by AstNode::IsInlineable.
5596 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005597 ASSERT(call->arguments()->length() == 0);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005598 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
5599 HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005600 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005601}
5602
5603
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005604void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00005605 // Our implementation of arguments (based on this stack frame or an
5606 // adapter below it) does not work for inlined functions. This runtime
5607 // function is blacklisted by AstNode::IsInlineable.
5608 ASSERT(function_state()->outer() == NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005609 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005610 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005611 HValue* index = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005612 HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
5613 HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
5614 HAccessArgumentsAt* result =
5615 new(zone()) HAccessArgumentsAt(elements, length, index);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005616 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005617}
5618
5619
5620// Support for accessing the class and value fields of an object.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005621void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005622 // The special form detected by IsClassOfTest is detected before we get here
5623 // and does not cause a bailout.
danno@chromium.org160a7b02011-04-18 15:51:38 +00005624 return Bailout("inlined runtime function: ClassOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005625}
5626
5627
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005628void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
5629 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005630 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005631 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005632 HValueOf* result = new(zone()) HValueOf(value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005633 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005634}
5635
5636
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005637void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005638 return Bailout("inlined runtime function: SetValueOf");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005639}
5640
5641
5642// Fast support for charCodeAt(n).
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005643void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
5644 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005645 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5646 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00005647 HValue* index = Pop();
5648 HValue* string = Pop();
5649 HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005650 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005651}
5652
5653
5654// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005655void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005656 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005657 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005658 HValue* char_code = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005659 HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005660 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005661}
5662
5663
5664// Fast support for string.charAt(n) and string[n].
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005665void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005666 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005667 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5668 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00005669 HValue* index = Pop();
5670 HValue* string = Pop();
5671 HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
5672 AddInstruction(char_code);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005673 HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005674 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005675}
5676
5677
5678// Fast support for object equality testing.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005679void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
5680 ASSERT(call->arguments()->length() == 2);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005681 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5682 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005683 HValue* right = Pop();
5684 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005685 HCompareJSObjectEq* result = new(zone()) HCompareJSObjectEq(left, right);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005686 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005687}
5688
5689
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005690void HGraphBuilder::GenerateLog(CallRuntime* call) {
5691 // %_Log is ignored in optimized code.
5692 ast_context()->ReturnValue(graph()->GetConstantUndefined());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005693}
5694
5695
5696// Fast support for Math.random().
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005697void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005698 return Bailout("inlined runtime function: RandomHeapNumber");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005699}
5700
5701
5702// Fast support for StringAdd.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005703void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
5704 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005705 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005706 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005707 HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005708 Drop(2);
5709 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005710}
5711
5712
5713// Fast support for SubString.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005714void HGraphBuilder::GenerateSubString(CallRuntime* call) {
5715 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005716 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005717 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005718 HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005719 Drop(3);
5720 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005721}
5722
5723
5724// Fast support for StringCompare.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005725void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
5726 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005727 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005728 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005729 HCallStub* result =
5730 new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005731 Drop(2);
5732 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005733}
5734
5735
5736// Support for direct calls from JavaScript to native RegExp code.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005737void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
5738 ASSERT_EQ(4, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005739 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005740 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005741 HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005742 Drop(4);
5743 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005744}
5745
5746
5747// Construct a RegExp exec result with two in-object properties.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005748void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
5749 ASSERT_EQ(3, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005750 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005751 HValue* context = environment()->LookupContext();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005752 HCallStub* result =
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005753 new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005754 Drop(3);
5755 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005756}
5757
5758
5759// Support for fast native caches.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005760void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005761 return Bailout("inlined runtime function: GetFromCache");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005762}
5763
5764
5765// Fast support for number to string.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005766void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
5767 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005768 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005769 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005770 HCallStub* result =
5771 new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005772 Drop(1);
5773 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005774}
5775
5776
5777// Fast swapping of elements. Takes three expressions, the object and two
5778// indices. This should only be used if the indices are known to be
5779// non-negative and within bounds of the elements array at the call site.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005780void HGraphBuilder::GenerateSwapElements(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005781 return Bailout("inlined runtime function: SwapElements");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005782}
5783
5784
5785// Fast call for custom callbacks.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005786void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005787 // 1 ~ The function to call is not itself an argument to the call.
5788 int arg_count = call->arguments()->length() - 1;
5789 ASSERT(arg_count >= 1); // There's always at least a receiver.
5790
5791 for (int i = 0; i < arg_count; ++i) {
5792 CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
5793 }
5794 CHECK_ALIVE(VisitForValue(call->arguments()->last()));
5795 HValue* function = Pop();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005796 HValue* context = environment()->LookupContext();
danno@chromium.org160a7b02011-04-18 15:51:38 +00005797 HInvokeFunction* result =
5798 new(zone()) HInvokeFunction(context, function, arg_count);
5799 Drop(arg_count);
5800 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005801}
5802
5803
5804// Fast call to math functions.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005805void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
5806 ASSERT_EQ(2, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005807 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
5808 CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005809 HValue* right = Pop();
5810 HValue* left = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005811 HPower* result = new(zone()) HPower(left, right);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005812 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005813}
5814
5815
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005816void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
5817 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005818 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005819 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005820 HCallStub* result =
5821 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005822 result->set_transcendental_type(TranscendentalCache::SIN);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005823 Drop(1);
5824 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005825}
5826
5827
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005828void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
5829 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005830 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005831 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005832 HCallStub* result =
5833 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005834 result->set_transcendental_type(TranscendentalCache::COS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005835 Drop(1);
5836 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005837}
5838
5839
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005840void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
5841 ASSERT_EQ(1, call->arguments()->length());
danno@chromium.org160a7b02011-04-18 15:51:38 +00005842 CHECK_ALIVE(VisitArgumentList(call->arguments()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005843 HValue* context = environment()->LookupContext();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005844 HCallStub* result =
5845 new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005846 result->set_transcendental_type(TranscendentalCache::LOG);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005847 Drop(1);
5848 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005849}
5850
5851
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005852void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005853 return Bailout("inlined runtime function: MathSqrt");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005854}
5855
5856
5857// Check whether two RegExps are equivalent
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005858void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005859 return Bailout("inlined runtime function: IsRegExpEquivalent");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005860}
5861
5862
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005863void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
5864 ASSERT(call->arguments()->length() == 1);
danno@chromium.org160a7b02011-04-18 15:51:38 +00005865 CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00005866 HValue* value = Pop();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005867 HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005868 ast_context()->ReturnInstruction(result, call->id());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005869}
5870
5871
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00005872void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00005873 return Bailout("inlined runtime function: FastAsciiArrayJoin");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005874}
5875
5876
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005877#undef CHECK_BAILOUT
danno@chromium.org160a7b02011-04-18 15:51:38 +00005878#undef CHECK_ALIVE
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005879
5880
5881HEnvironment::HEnvironment(HEnvironment* outer,
5882 Scope* scope,
5883 Handle<JSFunction> closure)
5884 : closure_(closure),
5885 values_(0),
5886 assigned_variables_(4),
5887 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005888 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005889 local_count_(0),
5890 outer_(outer),
5891 pop_count_(0),
5892 push_count_(0),
5893 ast_id_(AstNode::kNoNumber) {
5894 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
5895}
5896
5897
5898HEnvironment::HEnvironment(const HEnvironment* other)
5899 : values_(0),
5900 assigned_variables_(0),
5901 parameter_count_(0),
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005902 specials_count_(1),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005903 local_count_(0),
5904 outer_(NULL),
5905 pop_count_(0),
5906 push_count_(0),
5907 ast_id_(other->ast_id()) {
5908 Initialize(other);
5909}
5910
5911
5912void HEnvironment::Initialize(int parameter_count,
5913 int local_count,
5914 int stack_height) {
5915 parameter_count_ = parameter_count;
5916 local_count_ = local_count;
5917
5918 // Avoid reallocating the temporaries' backing store on the first Push.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005919 int total = parameter_count + specials_count_ + local_count + stack_height;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005920 values_.Initialize(total + 4);
5921 for (int i = 0; i < total; ++i) values_.Add(NULL);
5922}
5923
5924
lrn@chromium.org5d00b602011-01-05 09:51:43 +00005925void HEnvironment::Initialize(const HEnvironment* other) {
5926 closure_ = other->closure();
5927 values_.AddAll(other->values_);
5928 assigned_variables_.AddAll(other->assigned_variables_);
5929 parameter_count_ = other->parameter_count_;
5930 local_count_ = other->local_count_;
5931 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
5932 pop_count_ = other->pop_count_;
5933 push_count_ = other->push_count_;
5934 ast_id_ = other->ast_id_;
5935}
5936
5937
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005938void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
5939 ASSERT(!block->IsLoopHeader());
5940 ASSERT(values_.length() == other->values_.length());
5941
5942 int length = values_.length();
5943 for (int i = 0; i < length; ++i) {
5944 HValue* value = values_[i];
5945 if (value != NULL && value->IsPhi() && value->block() == block) {
5946 // There is already a phi for the i'th value.
5947 HPhi* phi = HPhi::cast(value);
5948 // Assert index is correct and that we haven't missed an incoming edge.
5949 ASSERT(phi->merged_index() == i);
5950 ASSERT(phi->OperandCount() == block->predecessors()->length());
5951 phi->AddInput(other->values_[i]);
5952 } else if (values_[i] != other->values_[i]) {
5953 // There is a fresh value on the incoming edge, a phi is needed.
5954 ASSERT(values_[i] != NULL && other->values_[i] != NULL);
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00005955 HPhi* phi = new(block->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005956 HValue* old_value = values_[i];
5957 for (int j = 0; j < block->predecessors()->length(); j++) {
5958 phi->AddInput(old_value);
5959 }
5960 phi->AddInput(other->values_[i]);
5961 this->values_[i] = phi;
5962 block->AddPhi(phi);
5963 }
5964 }
5965}
5966
5967
lrn@chromium.org5d00b602011-01-05 09:51:43 +00005968void HEnvironment::Bind(int index, HValue* value) {
5969 ASSERT(value != NULL);
5970 if (!assigned_variables_.Contains(index)) {
5971 assigned_variables_.Add(index);
5972 }
5973 values_[index] = value;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005974}
5975
5976
lrn@chromium.org5d00b602011-01-05 09:51:43 +00005977bool HEnvironment::HasExpressionAt(int index) const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005978 return index >= parameter_count_ + specials_count_ + local_count_;
lrn@chromium.org5d00b602011-01-05 09:51:43 +00005979}
5980
5981
5982bool HEnvironment::ExpressionStackIsEmpty() const {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005983 int first_expression = parameter_count() + specials_count() + local_count();
lrn@chromium.org5d00b602011-01-05 09:51:43 +00005984 ASSERT(length() >= first_expression);
5985 return length() == first_expression;
5986}
5987
5988
5989void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
5990 int count = index_from_top + 1;
5991 int index = values_.length() - count;
5992 ASSERT(HasExpressionAt(index));
5993 // The push count must include at least the element in question or else
5994 // the new value will not be included in this environment's history.
5995 if (push_count_ < count) {
5996 // This is the same effect as popping then re-pushing 'count' elements.
5997 pop_count_ += (count - push_count_);
5998 push_count_ = count;
5999 }
6000 values_[index] = value;
6001}
6002
6003
6004void HEnvironment::Drop(int count) {
6005 for (int i = 0; i < count; ++i) {
6006 Pop();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006007 }
6008}
6009
6010
6011HEnvironment* HEnvironment::Copy() const {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006012 return new(closure()->GetIsolate()->zone()) HEnvironment(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006013}
6014
6015
6016HEnvironment* HEnvironment::CopyWithoutHistory() const {
6017 HEnvironment* result = Copy();
6018 result->ClearHistory();
6019 return result;
6020}
6021
6022
6023HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
6024 HEnvironment* new_env = Copy();
6025 for (int i = 0; i < values_.length(); ++i) {
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006026 HPhi* phi = new(loop_header->zone()) HPhi(i);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006027 phi->AddInput(values_[i]);
6028 new_env->values_[i] = phi;
6029 loop_header->AddPhi(phi);
6030 }
6031 new_env->ClearHistory();
6032 return new_env;
6033}
6034
6035
danno@chromium.org40cb8782011-05-25 07:58:50 +00006036HEnvironment* HEnvironment::CopyForInlining(
6037 Handle<JSFunction> target,
6038 FunctionLiteral* function,
6039 CompilationPhase compilation_phase,
6040 HConstant* undefined,
6041 CallKind call_kind) const {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006042 // Outer environment is a copy of this one without the arguments.
6043 int arity = function->scope()->num_parameters();
6044 HEnvironment* outer = Copy();
6045 outer->Drop(arity + 1); // Including receiver.
6046 outer->ClearHistory();
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00006047 Zone* zone = closure()->GetIsolate()->zone();
6048 HEnvironment* inner =
6049 new(zone) HEnvironment(outer, function->scope(), target);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006050 // Get the argument values from the original environment.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006051 if (compilation_phase == HYDROGEN) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006052 for (int i = 0; i <= arity; ++i) { // Include receiver.
6053 HValue* push = ExpressionStackAt(arity - i);
6054 inner->SetValueAt(i, push);
6055 }
6056 } else {
lrn@chromium.org1c092762011-05-09 09:42:16 +00006057 ASSERT(compilation_phase == LITHIUM);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006058 for (int i = 0; i <= arity; ++i) { // Include receiver.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006059 HValue* push = ExpressionStackAt(arity - i);
6060 inner->SetValueAt(i, push);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006061 }
6062 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00006063 // If the function we are inlining is a strict mode function or a
6064 // builtin function, pass undefined as the receiver for function
6065 // calls (instead of the global receiver).
6066 if ((target->shared()->native() || function->strict_mode()) &&
6067 call_kind == CALL_AS_FUNCTION) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00006068 inner->SetValueAt(0, undefined);
6069 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006070 inner->SetValueAt(arity + 1, outer->LookupContext());
6071 for (int i = arity + 2; i < inner->length(); ++i) {
6072 inner->SetValueAt(i, undefined);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006073 }
6074
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00006075 inner->set_ast_id(AstNode::kFunctionEntryId);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006076 return inner;
6077}
6078
6079
6080void HEnvironment::PrintTo(StringStream* stream) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +00006081 for (int i = 0; i < length(); i++) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006082 if (i == 0) stream->Add("parameters\n");
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006083 if (i == parameter_count()) stream->Add("specials\n");
6084 if (i == parameter_count() + specials_count()) stream->Add("locals\n");
6085 if (i == parameter_count() + specials_count() + local_count()) {
6086 stream->Add("expressions");
6087 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006088 HValue* val = values_.at(i);
6089 stream->Add("%d: ", i);
6090 if (val != NULL) {
6091 val->PrintNameTo(stream);
6092 } else {
6093 stream->Add("NULL");
6094 }
6095 stream->Add("\n");
6096 }
6097}
6098
6099
6100void HEnvironment::PrintToStd() {
6101 HeapStringAllocator string_allocator;
6102 StringStream trace(&string_allocator);
6103 PrintTo(&trace);
6104 PrintF("%s", *trace.ToCString());
6105}
6106
6107
6108void HTracer::TraceCompilation(FunctionLiteral* function) {
6109 Tag tag(this, "compilation");
6110 Handle<String> name = function->debug_name();
6111 PrintStringProperty("name", *name->ToCString());
6112 PrintStringProperty("method", *name->ToCString());
6113 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
6114}
6115
6116
6117void HTracer::TraceLithium(const char* name, LChunk* chunk) {
6118 Trace(name, chunk->graph(), chunk);
6119}
6120
6121
6122void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
6123 Trace(name, graph, NULL);
6124}
6125
6126
6127void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
6128 Tag tag(this, "cfg");
6129 PrintStringProperty("name", name);
6130 const ZoneList<HBasicBlock*>* blocks = graph->blocks();
6131 for (int i = 0; i < blocks->length(); i++) {
6132 HBasicBlock* current = blocks->at(i);
6133 Tag block_tag(this, "block");
6134 PrintBlockProperty("name", current->block_id());
6135 PrintIntProperty("from_bci", -1);
6136 PrintIntProperty("to_bci", -1);
6137
6138 if (!current->predecessors()->is_empty()) {
6139 PrintIndent();
6140 trace_.Add("predecessors");
6141 for (int j = 0; j < current->predecessors()->length(); ++j) {
6142 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
6143 }
6144 trace_.Add("\n");
6145 } else {
6146 PrintEmptyProperty("predecessors");
6147 }
6148
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00006149 if (current->end()->SuccessorCount() == 0) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006150 PrintEmptyProperty("successors");
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00006151 } else {
6152 PrintIndent();
6153 trace_.Add("successors");
6154 for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
6155 trace_.Add(" \"B%d\"", it.Current()->block_id());
6156 }
6157 trace_.Add("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006158 }
6159
6160 PrintEmptyProperty("xhandlers");
6161 PrintEmptyProperty("flags");
6162
6163 if (current->dominator() != NULL) {
6164 PrintBlockProperty("dominator", current->dominator()->block_id());
6165 }
6166
6167 if (chunk != NULL) {
6168 int first_index = current->first_instruction_index();
6169 int last_index = current->last_instruction_index();
6170 PrintIntProperty(
6171 "first_lir_id",
6172 LifetimePosition::FromInstructionIndex(first_index).Value());
6173 PrintIntProperty(
6174 "last_lir_id",
6175 LifetimePosition::FromInstructionIndex(last_index).Value());
6176 }
6177
6178 {
6179 Tag states_tag(this, "states");
6180 Tag locals_tag(this, "locals");
6181 int total = current->phis()->length();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006182 PrintIntProperty("size", current->phis()->length());
6183 PrintStringProperty("method", "None");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006184 for (int j = 0; j < total; ++j) {
6185 HPhi* phi = current->phis()->at(j);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006186 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006187 trace_.Add("%d ", phi->merged_index());
6188 phi->PrintNameTo(&trace_);
6189 trace_.Add(" ");
6190 phi->PrintTo(&trace_);
6191 trace_.Add("\n");
6192 }
6193 }
6194
6195 {
6196 Tag HIR_tag(this, "HIR");
6197 HInstruction* instruction = current->first();
6198 while (instruction != NULL) {
6199 int bci = 0;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00006200 int uses = instruction->UseCount();
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006201 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006202 trace_.Add("%d %d ", bci, uses);
6203 instruction->PrintNameTo(&trace_);
6204 trace_.Add(" ");
6205 instruction->PrintTo(&trace_);
6206 trace_.Add(" <|@\n");
6207 instruction = instruction->next();
6208 }
6209 }
6210
6211
6212 if (chunk != NULL) {
6213 Tag LIR_tag(this, "LIR");
6214 int first_index = current->first_instruction_index();
6215 int last_index = current->last_instruction_index();
6216 if (first_index != -1 && last_index != -1) {
6217 const ZoneList<LInstruction*>* instructions = chunk->instructions();
6218 for (int i = first_index; i <= last_index; ++i) {
6219 LInstruction* linstr = instructions->at(i);
6220 if (linstr != NULL) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006221 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006222 trace_.Add("%d ",
6223 LifetimePosition::FromInstructionIndex(i).Value());
6224 linstr->PrintTo(&trace_);
6225 trace_.Add(" <|@\n");
6226 }
6227 }
6228 }
6229 }
6230 }
6231}
6232
6233
6234void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
6235 Tag tag(this, "intervals");
6236 PrintStringProperty("name", name);
6237
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006238 const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006239 for (int i = 0; i < fixed_d->length(); ++i) {
6240 TraceLiveRange(fixed_d->at(i), "fixed");
6241 }
6242
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006243 const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006244 for (int i = 0; i < fixed->length(); ++i) {
6245 TraceLiveRange(fixed->at(i), "fixed");
6246 }
6247
6248 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
6249 for (int i = 0; i < live_ranges->length(); ++i) {
6250 TraceLiveRange(live_ranges->at(i), "object");
6251 }
6252}
6253
6254
6255void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
6256 if (range != NULL && !range->IsEmpty()) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006257 PrintIndent();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006258 trace_.Add("%d %s", range->id(), type);
6259 if (range->HasRegisterAssigned()) {
6260 LOperand* op = range->CreateAssignedOperand();
6261 int assigned_reg = op->index();
6262 if (op->IsDoubleRegister()) {
6263 trace_.Add(" \"%s\"",
6264 DoubleRegister::AllocationIndexToString(assigned_reg));
6265 } else {
6266 ASSERT(op->IsRegister());
6267 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
6268 }
6269 } else if (range->IsSpilled()) {
6270 LOperand* op = range->TopLevel()->GetSpillOperand();
6271 if (op->IsDoubleStackSlot()) {
6272 trace_.Add(" \"double_stack:%d\"", op->index());
6273 } else {
6274 ASSERT(op->IsStackSlot());
6275 trace_.Add(" \"stack:%d\"", op->index());
6276 }
6277 }
6278 int parent_index = -1;
6279 if (range->IsChild()) {
6280 parent_index = range->parent()->id();
6281 } else {
6282 parent_index = range->id();
6283 }
6284 LOperand* op = range->FirstHint();
6285 int hint_index = -1;
6286 if (op != NULL && op->IsUnallocated()) hint_index = op->VirtualRegister();
6287 trace_.Add(" %d %d", parent_index, hint_index);
6288 UseInterval* cur_interval = range->first_interval();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006289 while (cur_interval != NULL && range->Covers(cur_interval->start())) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006290 trace_.Add(" [%d, %d[",
6291 cur_interval->start().Value(),
6292 cur_interval->end().Value());
6293 cur_interval = cur_interval->next();
6294 }
6295
6296 UsePosition* current_pos = range->first_pos();
6297 while (current_pos != NULL) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00006298 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006299 trace_.Add(" %d M", current_pos->pos().Value());
6300 }
6301 current_pos = current_pos->next();
6302 }
6303
6304 trace_.Add(" \"\"\n");
6305 }
6306}
6307
6308
6309void HTracer::FlushToFile() {
6310 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false);
6311 trace_.Reset();
6312}
6313
6314
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006315void HStatistics::Initialize(CompilationInfo* info) {
6316 source_size_ += info->shared_info()->SourceSize();
6317}
6318
6319
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006320void HStatistics::Print() {
6321 PrintF("Timing results:\n");
6322 int64_t sum = 0;
6323 for (int i = 0; i < timing_.length(); ++i) {
6324 sum += timing_[i];
6325 }
6326
6327 for (int i = 0; i < names_.length(); ++i) {
6328 PrintF("%30s", names_[i]);
6329 double ms = static_cast<double>(timing_[i]) / 1000;
6330 double percent = static_cast<double>(timing_[i]) * 100 / sum;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006331 PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
6332
6333 unsigned size = sizes_[i];
6334 double size_percent = static_cast<double>(size) * 100 / total_size_;
6335 PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006336 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006337 double source_size_in_kb = static_cast<double>(source_size_) / 1024;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006338 double normalized_time = source_size_in_kb > 0
6339 ? (static_cast<double>(sum) / 1000) / source_size_in_kb
6340 : 0;
6341 double normalized_bytes = source_size_in_kb > 0
6342 ? total_size_ / source_size_in_kb
6343 : 0;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006344 PrintF("%30s - %7.3f ms %7.3f bytes\n", "Sum",
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006345 normalized_time, normalized_bytes);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006346 PrintF("---------------------------------------------------------------\n");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006347 PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006348 "Total",
6349 static_cast<double>(total_) / 1000,
6350 static_cast<double>(total_) / full_code_gen_);
6351}
6352
6353
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006354void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006355 if (name == HPhase::kFullCodeGen) {
6356 full_code_gen_ += ticks;
6357 } else if (name == HPhase::kTotal) {
6358 total_ += ticks;
6359 } else {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006360 total_size_ += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006361 for (int i = 0; i < names_.length(); ++i) {
6362 if (names_[i] == name) {
6363 timing_[i] += ticks;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006364 sizes_[i] += size;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006365 return;
6366 }
6367 }
6368 names_.Add(name);
6369 timing_.Add(ticks);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006370 sizes_.Add(size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006371 }
6372}
6373
6374
6375const char* const HPhase::kFullCodeGen = "Full code generator";
6376const char* const HPhase::kTotal = "Total";
6377
6378
6379void HPhase::Begin(const char* name,
6380 HGraph* graph,
6381 LChunk* chunk,
6382 LAllocator* allocator) {
6383 name_ = name;
6384 graph_ = graph;
6385 chunk_ = chunk;
6386 allocator_ = allocator;
6387 if (allocator != NULL && chunk_ == NULL) {
6388 chunk_ = allocator->chunk();
6389 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006390 if (FLAG_hydrogen_stats) start_ = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006391 start_allocation_size_ = Zone::allocation_size_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006392}
6393
6394
6395void HPhase::End() const {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00006396 if (FLAG_hydrogen_stats) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006397 int64_t end = OS::Ticks();
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006398 unsigned size = Zone::allocation_size_ - start_allocation_size_;
6399 HStatistics::Instance()->SaveTiming(name_, end - start_, size);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006400 }
6401
6402 if (FLAG_trace_hydrogen) {
6403 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_);
6404 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_);
6405 if (allocator_ != NULL) {
6406 HTracer::Instance()->TraceLiveRanges(name_, allocator_);
6407 }
6408 }
6409
6410#ifdef DEBUG
6411 if (graph_ != NULL) graph_->Verify();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006412 if (allocator_ != NULL) allocator_->Verify();
6413#endif
6414}
6415
6416} } // namespace v8::internal