| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are |
| 4 // met: |
| 5 // |
| 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. |
| 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 |
| 28 #include "hydrogen.h" |
| 29 |
| 30 #include "codegen.h" |
| 31 #include "data-flow.h" |
| 32 #include "full-codegen.h" |
| 33 #include "hashmap.h" |
| 34 #include "lithium-allocator.h" |
| 35 #include "parser.h" |
| 36 #include "scopes.h" |
| 37 |
| 38 #if V8_TARGET_ARCH_IA32 |
| 39 #include "ia32/lithium-codegen-ia32.h" |
| 40 #elif V8_TARGET_ARCH_X64 |
| 41 #include "x64/lithium-codegen-x64.h" |
| 42 #elif V8_TARGET_ARCH_ARM |
| 43 #include "arm/lithium-codegen-arm.h" |
| 44 #else |
| 45 #error Unsupported target architecture. |
| 46 #endif |
| 47 |
| 48 namespace v8 { |
| 49 namespace internal { |
| 50 |
| 51 HBasicBlock::HBasicBlock(HGraph* graph) |
| 52 : block_id_(graph->GetNextBlockID()), |
| 53 graph_(graph), |
| 54 phis_(4), |
| 55 first_(NULL), |
| 56 last_(NULL), |
| 57 end_(NULL), |
| 58 loop_information_(NULL), |
| 59 predecessors_(2), |
| 60 dominator_(NULL), |
| 61 dominated_blocks_(4), |
| 62 last_environment_(NULL), |
| 63 argument_count_(-1), |
| 64 first_instruction_index_(-1), |
| 65 last_instruction_index_(-1), |
| 66 deleted_phis_(4), |
| 67 is_inline_return_target_(false), |
| 68 inverted_(false), |
| 69 deopt_predecessor_(NULL) { |
| 70 } |
| 71 |
| 72 |
| 73 void HBasicBlock::AttachLoopInformation() { |
| 74 ASSERT(!IsLoopHeader()); |
| 75 loop_information_ = new HLoopInformation(this); |
| 76 } |
| 77 |
| 78 |
| 79 void HBasicBlock::DetachLoopInformation() { |
| 80 ASSERT(IsLoopHeader()); |
| 81 loop_information_ = NULL; |
| 82 } |
| 83 |
| 84 |
| 85 void HBasicBlock::AddPhi(HPhi* phi) { |
| 86 ASSERT(!IsStartBlock()); |
| 87 phis_.Add(phi); |
| 88 phi->SetBlock(this); |
| 89 } |
| 90 |
| 91 |
| 92 void HBasicBlock::RemovePhi(HPhi* phi) { |
| 93 ASSERT(phi->block() == this); |
| 94 ASSERT(phis_.Contains(phi)); |
| 95 ASSERT(phi->HasNoUses()); |
| 96 phi->ClearOperands(); |
| 97 phis_.RemoveElement(phi); |
| 98 phi->SetBlock(NULL); |
| 99 } |
| 100 |
| 101 |
| 102 void HBasicBlock::AddInstruction(HInstruction* instr) { |
| 103 ASSERT(!IsStartBlock() || !IsFinished()); |
| 104 ASSERT(!instr->IsLinked()); |
| 105 ASSERT(!IsFinished()); |
| 106 if (first_ == NULL) { |
| 107 HBlockEntry* entry = new HBlockEntry(); |
| 108 entry->InitializeAsFirst(this); |
| 109 first_ = entry; |
| 110 } |
| 111 instr->InsertAfter(GetLastInstruction()); |
| 112 } |
| 113 |
| 114 |
| 115 HInstruction* HBasicBlock::GetLastInstruction() { |
| 116 if (end_ != NULL) return end_->previous(); |
| 117 if (first_ == NULL) return NULL; |
| 118 if (last_ == NULL) last_ = first_; |
| 119 while (last_->next() != NULL) last_ = last_->next(); |
| 120 return last_; |
| 121 } |
| 122 |
| 123 |
| 124 HSimulate* HBasicBlock::CreateSimulate(int id) { |
| 125 ASSERT(HasEnvironment()); |
| 126 HEnvironment* environment = last_environment(); |
| 127 ASSERT(id == AstNode::kNoNumber || |
| 128 environment->closure()->shared()->VerifyBailoutId(id)); |
| 129 |
| 130 int push_count = environment->push_count(); |
| 131 int pop_count = environment->pop_count(); |
| 132 |
| 133 int length = environment->values()->length(); |
| 134 HSimulate* instr = new HSimulate(id, pop_count, length); |
| 135 for (int i = push_count - 1; i >= 0; --i) { |
| 136 instr->AddPushedValue(environment->ExpressionStackAt(i)); |
| 137 } |
| 138 for (int i = 0; i < environment->assigned_variables()->length(); ++i) { |
| 139 int index = environment->assigned_variables()->at(i); |
| 140 instr->AddAssignedValue(index, environment->Lookup(index)); |
| 141 } |
| 142 environment->ClearHistory(); |
| 143 return instr; |
| 144 } |
| 145 |
| 146 |
| 147 void HBasicBlock::Finish(HControlInstruction* end) { |
| 148 ASSERT(!IsFinished()); |
| 149 AddInstruction(end); |
| 150 end_ = end; |
| 151 if (end->FirstSuccessor() != NULL) { |
| 152 end->FirstSuccessor()->RegisterPredecessor(this); |
| 153 if (end->SecondSuccessor() != NULL) { |
| 154 end->SecondSuccessor()->RegisterPredecessor(this); |
| 155 } |
| 156 } |
| 157 } |
| 158 |
| 159 |
| 160 void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) { |
| 161 AddSimulate(AstNode::kNoNumber); |
| 162 HGoto* instr = new HGoto(block); |
| 163 instr->set_include_stack_check(include_stack_check); |
| 164 Finish(instr); |
| 165 } |
| 166 |
| 167 |
| 168 void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { |
| 169 ASSERT(!HasEnvironment()); |
| 170 ASSERT(first() == NULL); |
| 171 UpdateEnvironment(env); |
| 172 } |
| 173 |
| 174 |
| 175 void HBasicBlock::SetJoinId(int id) { |
| 176 int length = predecessors_.length(); |
| 177 ASSERT(length > 0); |
| 178 for (int i = 0; i < length; i++) { |
| 179 HBasicBlock* predecessor = predecessors_[i]; |
| 180 ASSERT(predecessor->end()->IsGoto()); |
| 181 HSimulate* simulate = HSimulate::cast(predecessor->GetLastInstruction()); |
| 182 // We only need to verify the ID once. |
| 183 ASSERT(i != 0 || |
| 184 predecessor->last_environment()->closure()->shared() |
| 185 ->VerifyBailoutId(id)); |
| 186 simulate->set_ast_id(id); |
| 187 } |
| 188 } |
| 189 |
| 190 |
| 191 bool HBasicBlock::Dominates(HBasicBlock* other) const { |
| 192 HBasicBlock* current = other->dominator(); |
| 193 while (current != NULL) { |
| 194 if (current == this) return true; |
| 195 current = current->dominator(); |
| 196 } |
| 197 return false; |
| 198 } |
| 199 |
| 200 |
| 201 void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) { |
| 202 ASSERT(IsLoopHeader()); |
| 203 |
| 204 SetJoinId(stmt->EntryId()); |
| 205 if (predecessors()->length() == 1) { |
| 206 // This is a degenerated loop. |
| 207 DetachLoopInformation(); |
| 208 return; |
| 209 } |
| 210 |
| 211 // Only the first entry into the loop is from outside the loop. All other |
| 212 // entries must be back edges. |
| 213 for (int i = 1; i < predecessors()->length(); ++i) { |
| 214 loop_information()->RegisterBackEdge(predecessors()->at(i)); |
| 215 } |
| 216 } |
| 217 |
| 218 |
| 219 void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) { |
| 220 if (!predecessors_.is_empty()) { |
| 221 // Only loop header blocks can have a predecessor added after |
| 222 // instructions have been added to the block (they have phis for all |
| 223 // values in the environment, these phis may be eliminated later). |
| 224 ASSERT(IsLoopHeader() || first_ == NULL); |
| 225 HEnvironment* incoming_env = pred->last_environment(); |
| 226 if (IsLoopHeader()) { |
| 227 ASSERT(phis()->length() == incoming_env->values()->length()); |
| 228 for (int i = 0; i < phis_.length(); ++i) { |
| 229 phis_[i]->AddInput(incoming_env->values()->at(i)); |
| 230 } |
| 231 } else { |
| 232 last_environment()->AddIncomingEdge(this, pred->last_environment()); |
| 233 } |
| 234 } else if (!HasEnvironment() && !IsFinished()) { |
| 235 ASSERT(!IsLoopHeader()); |
| 236 SetInitialEnvironment(pred->last_environment()->Copy()); |
| 237 } |
| 238 |
| 239 predecessors_.Add(pred); |
| 240 } |
| 241 |
| 242 |
| 243 void HBasicBlock::AddDominatedBlock(HBasicBlock* block) { |
| 244 ASSERT(!dominated_blocks_.Contains(block)); |
| 245 // Keep the list of dominated blocks sorted such that if there is two |
| 246 // succeeding block in this list, the predecessor is before the successor. |
| 247 int index = 0; |
| 248 while (index < dominated_blocks_.length() && |
| 249 dominated_blocks_[index]->block_id() < block->block_id()) { |
| 250 ++index; |
| 251 } |
| 252 dominated_blocks_.InsertAt(index, block); |
| 253 } |
| 254 |
| 255 |
| 256 void HBasicBlock::AssignCommonDominator(HBasicBlock* other) { |
| 257 if (dominator_ == NULL) { |
| 258 dominator_ = other; |
| 259 other->AddDominatedBlock(this); |
| 260 } else if (other->dominator() != NULL) { |
| 261 HBasicBlock* first = dominator_; |
| 262 HBasicBlock* second = other; |
| 263 |
| 264 while (first != second) { |
| 265 if (first->block_id() > second->block_id()) { |
| 266 first = first->dominator(); |
| 267 } else { |
| 268 second = second->dominator(); |
| 269 } |
| 270 ASSERT(first != NULL && second != NULL); |
| 271 } |
| 272 |
| 273 if (dominator_ != first) { |
| 274 ASSERT(dominator_->dominated_blocks_.Contains(this)); |
| 275 dominator_->dominated_blocks_.RemoveElement(this); |
| 276 dominator_ = first; |
| 277 first->AddDominatedBlock(this); |
| 278 } |
| 279 } |
| 280 } |
| 281 |
| 282 |
| 283 int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const { |
| 284 for (int i = 0; i < predecessors_.length(); ++i) { |
| 285 if (predecessors_[i] == predecessor) return i; |
| 286 } |
| 287 UNREACHABLE(); |
| 288 return -1; |
| 289 } |
| 290 |
| 291 |
| 292 #ifdef DEBUG |
| 293 void HBasicBlock::Verify() { |
| 294 // Check that every block is finished. |
| 295 ASSERT(IsFinished()); |
| 296 ASSERT(block_id() >= 0); |
| 297 |
| 298 // Verify that all blocks targetting a branch target, have the same boolean |
| 299 // value on top of their expression stack. |
| 300 if (!cond().is_null()) { |
| 301 ASSERT(predecessors()->length() > 0); |
| 302 for (int i = 1; i < predecessors()->length(); i++) { |
| 303 HBasicBlock* pred = predecessors()->at(i); |
| 304 HValue* top = pred->last_environment()->Top(); |
| 305 ASSERT(top->IsConstant()); |
| 306 Object* a = *HConstant::cast(top)->handle(); |
| 307 Object* b = *cond(); |
| 308 ASSERT(a == b); |
| 309 } |
| 310 } |
| 311 } |
| 312 #endif |
| 313 |
| 314 |
| 315 void HLoopInformation::RegisterBackEdge(HBasicBlock* block) { |
| 316 this->back_edges_.Add(block); |
| 317 AddBlock(block); |
| 318 } |
| 319 |
| 320 |
| 321 HBasicBlock* HLoopInformation::GetLastBackEdge() const { |
| 322 int max_id = -1; |
| 323 HBasicBlock* result = NULL; |
| 324 for (int i = 0; i < back_edges_.length(); ++i) { |
| 325 HBasicBlock* cur = back_edges_[i]; |
| 326 if (cur->block_id() > max_id) { |
| 327 max_id = cur->block_id(); |
| 328 result = cur; |
| 329 } |
| 330 } |
| 331 return result; |
| 332 } |
| 333 |
| 334 |
| 335 void HLoopInformation::AddBlock(HBasicBlock* block) { |
| 336 if (block == loop_header()) return; |
| 337 if (block->parent_loop_header() == loop_header()) return; |
| 338 if (block->parent_loop_header() != NULL) { |
| 339 AddBlock(block->parent_loop_header()); |
| 340 } else { |
| 341 block->set_parent_loop_header(loop_header()); |
| 342 blocks_.Add(block); |
| 343 for (int i = 0; i < block->predecessors()->length(); ++i) { |
| 344 AddBlock(block->predecessors()->at(i)); |
| 345 } |
| 346 } |
| 347 } |
| 348 |
| 349 |
| 350 #ifdef DEBUG |
| 351 |
| 352 // Checks reachability of the blocks in this graph and stores a bit in |
| 353 // the BitVector "reachable()" for every block that can be reached |
| 354 // from the start block of the graph. If "dont_visit" is non-null, the given |
| 355 // block is treated as if it would not be part of the graph. "visited_count()" |
| 356 // returns the number of reachable blocks. |
| 357 class ReachabilityAnalyzer BASE_EMBEDDED { |
| 358 public: |
| 359 ReachabilityAnalyzer(HBasicBlock* entry_block, |
| 360 int block_count, |
| 361 HBasicBlock* dont_visit) |
| 362 : visited_count_(0), |
| 363 stack_(16), |
| 364 reachable_(block_count), |
| 365 dont_visit_(dont_visit) { |
| 366 PushBlock(entry_block); |
| 367 Analyze(); |
| 368 } |
| 369 |
| 370 int visited_count() const { return visited_count_; } |
| 371 const BitVector* reachable() const { return &reachable_; } |
| 372 |
| 373 private: |
| 374 void PushBlock(HBasicBlock* block) { |
| 375 if (block != NULL && block != dont_visit_ && |
| 376 !reachable_.Contains(block->block_id())) { |
| 377 reachable_.Add(block->block_id()); |
| 378 stack_.Add(block); |
| 379 visited_count_++; |
| 380 } |
| 381 } |
| 382 |
| 383 void Analyze() { |
| 384 while (!stack_.is_empty()) { |
| 385 HControlInstruction* end = stack_.RemoveLast()->end(); |
| 386 PushBlock(end->FirstSuccessor()); |
| 387 PushBlock(end->SecondSuccessor()); |
| 388 } |
| 389 } |
| 390 |
| 391 int visited_count_; |
| 392 ZoneList<HBasicBlock*> stack_; |
| 393 BitVector reachable_; |
| 394 HBasicBlock* dont_visit_; |
| 395 }; |
| 396 |
| 397 |
| 398 void HGraph::Verify() const { |
| 399 for (int i = 0; i < blocks_.length(); i++) { |
| 400 HBasicBlock* block = blocks_.at(i); |
| 401 |
| 402 block->Verify(); |
| 403 |
| 404 // Check that every block contains at least one node and that only the last |
| 405 // node is a control instruction. |
| 406 HInstruction* current = block->first(); |
| 407 ASSERT(current != NULL && current->IsBlockEntry()); |
| 408 while (current != NULL) { |
| 409 ASSERT((current->next() == NULL) == current->IsControlInstruction()); |
| 410 ASSERT(current->block() == block); |
| 411 current->Verify(); |
| 412 current = current->next(); |
| 413 } |
| 414 |
| 415 // Check that successors are correctly set. |
| 416 HBasicBlock* first = block->end()->FirstSuccessor(); |
| 417 HBasicBlock* second = block->end()->SecondSuccessor(); |
| 418 ASSERT(second == NULL || first != NULL); |
| 419 |
| 420 // Check that the predecessor array is correct. |
| 421 if (first != NULL) { |
| 422 ASSERT(first->predecessors()->Contains(block)); |
| 423 if (second != NULL) { |
| 424 ASSERT(second->predecessors()->Contains(block)); |
| 425 } |
| 426 } |
| 427 |
| 428 // Check that phis have correct arguments. |
| 429 for (int j = 0; j < block->phis()->length(); j++) { |
| 430 HPhi* phi = block->phis()->at(j); |
| 431 phi->Verify(); |
| 432 } |
| 433 |
| 434 // Check that all join blocks have predecessors that end with an |
| 435 // unconditional goto and agree on their environment node id. |
| 436 if (block->predecessors()->length() >= 2) { |
| 437 int id = block->predecessors()->first()->last_environment()->ast_id(); |
| 438 for (int k = 0; k < block->predecessors()->length(); k++) { |
| 439 HBasicBlock* predecessor = block->predecessors()->at(k); |
| 440 ASSERT(predecessor->end()->IsGoto()); |
| 441 ASSERT(predecessor->last_environment()->ast_id() == id); |
| 442 } |
| 443 } |
| 444 } |
| 445 |
| 446 // Check special property of first block to have no predecessors. |
| 447 ASSERT(blocks_.at(0)->predecessors()->is_empty()); |
| 448 |
| 449 // Check that the graph is fully connected. |
| 450 ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); |
| 451 ASSERT(analyzer.visited_count() == blocks_.length()); |
| 452 |
| 453 // Check that entry block dominator is NULL. |
| 454 ASSERT(entry_block_->dominator() == NULL); |
| 455 |
| 456 // Check dominators. |
| 457 for (int i = 0; i < blocks_.length(); ++i) { |
| 458 HBasicBlock* block = blocks_.at(i); |
| 459 if (block->dominator() == NULL) { |
| 460 // Only start block may have no dominator assigned to. |
| 461 ASSERT(i == 0); |
| 462 } else { |
| 463 // Assert that block is unreachable if dominator must not be visited. |
| 464 ReachabilityAnalyzer dominator_analyzer(entry_block_, |
| 465 blocks_.length(), |
| 466 block->dominator()); |
| 467 ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); |
| 468 } |
| 469 } |
| 470 } |
| 471 |
| 472 #endif |
| 473 |
| 474 |
| 475 HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer, |
| 476 Object* value) { |
| 477 if (!pointer->is_set()) { |
| 478 HConstant* constant = new HConstant(Handle<Object>(value), |
| 479 Representation::Tagged()); |
| 480 constant->InsertAfter(GetConstantUndefined()); |
| 481 pointer->set(constant); |
| 482 } |
| 483 return pointer->get(); |
| 484 } |
| 485 |
| 486 |
| 487 HConstant* HGraph::GetConstant1() { |
| 488 return GetConstant(&constant_1_, Smi::FromInt(1)); |
| 489 } |
| 490 |
| 491 |
| 492 HConstant* HGraph::GetConstantMinus1() { |
| 493 return GetConstant(&constant_minus1_, Smi::FromInt(-1)); |
| 494 } |
| 495 |
| 496 |
| 497 HConstant* HGraph::GetConstantTrue() { |
| 498 return GetConstant(&constant_true_, HEAP->true_value()); |
| 499 } |
| 500 |
| 501 |
| 502 HConstant* HGraph::GetConstantFalse() { |
| 503 return GetConstant(&constant_false_, HEAP->false_value()); |
| 504 } |
| 505 |
| 506 |
| 507 void HSubgraph::AppendOptional(HSubgraph* graph, |
| 508 bool on_true_branch, |
| 509 HValue* boolean_value) { |
| 510 ASSERT(HasExit() && graph->HasExit()); |
| 511 HBasicBlock* other_block = graph_->CreateBasicBlock(); |
| 512 HBasicBlock* join_block = graph_->CreateBasicBlock(); |
| 513 |
| 514 HBasicBlock* true_branch = other_block; |
| 515 HBasicBlock* false_branch = graph->entry_block(); |
| 516 if (on_true_branch) { |
| 517 true_branch = graph->entry_block(); |
| 518 false_branch = other_block; |
| 519 } |
| 520 |
| 521 exit_block_->Finish(new HBranch(true_branch, false_branch, boolean_value)); |
| 522 other_block->Goto(join_block); |
| 523 graph->exit_block()->Goto(join_block); |
| 524 exit_block_ = join_block; |
| 525 } |
| 526 |
| 527 |
| 528 void HSubgraph::AppendJoin(HSubgraph* then_graph, |
| 529 HSubgraph* else_graph, |
| 530 AstNode* node) { |
| 531 if (then_graph->HasExit() && else_graph->HasExit()) { |
| 532 // We need to merge, create new merge block. |
| 533 HBasicBlock* join_block = graph_->CreateBasicBlock(); |
| 534 then_graph->exit_block()->Goto(join_block); |
| 535 else_graph->exit_block()->Goto(join_block); |
| 536 join_block->SetJoinId(node->id()); |
| 537 exit_block_ = join_block; |
| 538 } else if (then_graph->HasExit()) { |
| 539 exit_block_ = then_graph->exit_block_; |
| 540 } else if (else_graph->HasExit()) { |
| 541 exit_block_ = else_graph->exit_block_; |
| 542 } else { |
| 543 exit_block_ = NULL; |
| 544 } |
| 545 } |
| 546 |
| 547 |
| 548 void HSubgraph::ResolveContinue(IterationStatement* statement) { |
| 549 HBasicBlock* continue_block = BundleContinue(statement); |
| 550 if (continue_block != NULL) { |
| 551 exit_block_ = JoinBlocks(exit_block(), |
| 552 continue_block, |
| 553 statement->ContinueId()); |
| 554 } |
| 555 } |
| 556 |
| 557 |
| 558 HBasicBlock* HSubgraph::BundleBreak(BreakableStatement* statement) { |
| 559 return BundleBreakContinue(statement, false, statement->ExitId()); |
| 560 } |
| 561 |
| 562 |
| 563 HBasicBlock* HSubgraph::BundleContinue(IterationStatement* statement) { |
| 564 return BundleBreakContinue(statement, true, statement->ContinueId()); |
| 565 } |
| 566 |
| 567 |
| 568 HBasicBlock* HSubgraph::BundleBreakContinue(BreakableStatement* statement, |
| 569 bool is_continue, |
| 570 int join_id) { |
| 571 HBasicBlock* result = NULL; |
| 572 const ZoneList<BreakContinueInfo*>* infos = break_continue_info(); |
| 573 for (int i = 0; i < infos->length(); ++i) { |
| 574 BreakContinueInfo* info = infos->at(i); |
| 575 if (info->is_continue() == is_continue && |
| 576 info->target() == statement && |
| 577 !info->IsResolved()) { |
| 578 if (result == NULL) { |
| 579 result = graph_->CreateBasicBlock(); |
| 580 } |
| 581 info->block()->Goto(result); |
| 582 info->Resolve(); |
| 583 } |
| 584 } |
| 585 |
| 586 if (result != NULL) result->SetJoinId(join_id); |
| 587 |
| 588 return result; |
| 589 } |
| 590 |
| 591 |
| 592 HBasicBlock* HSubgraph::JoinBlocks(HBasicBlock* a, HBasicBlock* b, int id) { |
| 593 if (a == NULL) return b; |
| 594 if (b == NULL) return a; |
| 595 HBasicBlock* target = graph_->CreateBasicBlock(); |
| 596 a->Goto(target); |
| 597 b->Goto(target); |
| 598 target->SetJoinId(id); |
| 599 return target; |
| 600 } |
| 601 |
| 602 |
| 603 void HSubgraph::AppendEndless(HSubgraph* body, IterationStatement* statement) { |
| 604 ConnectExitTo(body->entry_block()); |
| 605 body->ResolveContinue(statement); |
| 606 body->ConnectExitTo(body->entry_block(), true); |
| 607 exit_block_ = body->BundleBreak(statement); |
| 608 body->entry_block()->PostProcessLoopHeader(statement); |
| 609 } |
| 610 |
| 611 |
| 612 void HSubgraph::AppendDoWhile(HSubgraph* body, |
| 613 IterationStatement* statement, |
| 614 HSubgraph* go_back, |
| 615 HSubgraph* exit) { |
| 616 ConnectExitTo(body->entry_block()); |
| 617 go_back->ConnectExitTo(body->entry_block(), true); |
| 618 |
| 619 HBasicBlock* break_block = body->BundleBreak(statement); |
| 620 exit_block_ = |
| 621 JoinBlocks(exit->exit_block(), break_block, statement->ExitId()); |
| 622 body->entry_block()->PostProcessLoopHeader(statement); |
| 623 } |
| 624 |
| 625 |
| 626 void HSubgraph::AppendWhile(HSubgraph* condition, |
| 627 HSubgraph* body, |
| 628 IterationStatement* statement, |
| 629 HSubgraph* continue_subgraph, |
| 630 HSubgraph* exit) { |
| 631 ConnectExitTo(condition->entry_block()); |
| 632 |
| 633 HBasicBlock* break_block = body->BundleBreak(statement); |
| 634 exit_block_ = |
| 635 JoinBlocks(exit->exit_block(), break_block, statement->ExitId()); |
| 636 |
| 637 if (continue_subgraph != NULL) { |
| 638 body->ConnectExitTo(continue_subgraph->entry_block(), true); |
| 639 continue_subgraph->entry_block()->SetJoinId(statement->EntryId()); |
| 640 exit_block_ = JoinBlocks(exit_block_, |
| 641 continue_subgraph->exit_block(), |
| 642 statement->ExitId()); |
| 643 } else { |
| 644 body->ConnectExitTo(condition->entry_block(), true); |
| 645 } |
| 646 condition->entry_block()->PostProcessLoopHeader(statement); |
| 647 } |
| 648 |
| 649 |
| 650 void HSubgraph::Append(HSubgraph* next, BreakableStatement* stmt) { |
| 651 exit_block_->Goto(next->entry_block()); |
| 652 exit_block_ = next->exit_block_; |
| 653 |
| 654 if (stmt != NULL) { |
| 655 next->entry_block()->SetJoinId(stmt->EntryId()); |
| 656 HBasicBlock* break_block = next->BundleBreak(stmt); |
| 657 exit_block_ = JoinBlocks(exit_block(), break_block, stmt->ExitId()); |
| 658 } |
| 659 } |
| 660 |
| 661 |
| 662 void HSubgraph::FinishExit(HControlInstruction* instruction) { |
| 663 ASSERT(HasExit()); |
| 664 exit_block_->Finish(instruction); |
| 665 exit_block_->ClearEnvironment(); |
| 666 exit_block_ = NULL; |
| 667 } |
| 668 |
| 669 |
| 670 void HSubgraph::FinishBreakContinue(BreakableStatement* target, |
| 671 bool is_continue) { |
| 672 ASSERT(!exit_block_->IsFinished()); |
| 673 BreakContinueInfo* info = new BreakContinueInfo(target, exit_block_, |
| 674 is_continue); |
| 675 break_continue_info_.Add(info); |
| 676 exit_block_ = NULL; |
| 677 } |
| 678 |
| 679 |
| 680 HGraph::HGraph(CompilationInfo* info) |
| 681 : HSubgraph(this), |
| 682 next_block_id_(0), |
| 683 info_(info), |
| 684 blocks_(8), |
| 685 values_(16), |
| 686 phi_list_(NULL) { |
| 687 start_environment_ = new HEnvironment(NULL, info->scope(), info->closure()); |
| 688 start_environment_->set_ast_id(info->function()->id()); |
| 689 } |
| 690 |
| 691 |
| 692 Handle<Code> HGraph::Compile() { |
| 693 int values = GetMaximumValueID(); |
| 694 if (values > LAllocator::max_initial_value_ids()) { |
| 695 if (FLAG_trace_bailout) PrintF("Function is too big\n"); |
| 696 return Handle<Code>::null(); |
| 697 } |
| 698 |
| 699 LAllocator allocator(values, this); |
| 700 LChunkBuilder builder(this, &allocator); |
| 701 LChunk* chunk = builder.Build(); |
| 702 if (chunk == NULL) return Handle<Code>::null(); |
| 703 |
| 704 if (!FLAG_alloc_lithium) return Handle<Code>::null(); |
| 705 |
| 706 allocator.Allocate(chunk); |
| 707 |
| 708 if (!FLAG_use_lithium) return Handle<Code>::null(); |
| 709 |
| 710 MacroAssembler assembler(NULL, 0); |
| 711 LCodeGen generator(chunk, &assembler, info()); |
| 712 |
| 713 if (FLAG_eliminate_empty_blocks) { |
| 714 chunk->MarkEmptyBlocks(); |
| 715 } |
| 716 |
| 717 if (generator.GenerateCode()) { |
| 718 if (FLAG_trace_codegen) { |
| 719 PrintF("Crankshaft Compiler - "); |
| 720 } |
| 721 CodeGenerator::MakeCodePrologue(info()); |
| 722 Code::Flags flags = |
| 723 Code::ComputeFlags(Code::OPTIMIZED_FUNCTION, NOT_IN_LOOP); |
| 724 Handle<Code> code = |
| 725 CodeGenerator::MakeCodeEpilogue(&assembler, flags, info()); |
| 726 generator.FinishCode(code); |
| 727 CodeGenerator::PrintCode(code, info()); |
| 728 return code; |
| 729 } |
| 730 return Handle<Code>::null(); |
| 731 } |
| 732 |
| 733 |
| 734 HBasicBlock* HGraph::CreateBasicBlock() { |
| 735 HBasicBlock* result = new HBasicBlock(this); |
| 736 blocks_.Add(result); |
| 737 return result; |
| 738 } |
| 739 |
| 740 |
| 741 void HGraph::Canonicalize() { |
| 742 HPhase phase("Canonicalize", this); |
| 743 if (FLAG_use_canonicalizing) { |
| 744 for (int i = 0; i < blocks()->length(); ++i) { |
| 745 HBasicBlock* b = blocks()->at(i); |
| 746 for (HInstruction* insn = b->first(); insn != NULL; insn = insn->next()) { |
| 747 HValue* value = insn->Canonicalize(); |
| 748 if (value != insn) { |
| 749 if (value != NULL) { |
| 750 insn->ReplaceAndDelete(value); |
| 751 } else { |
| 752 insn->Delete(); |
| 753 } |
| 754 } |
| 755 } |
| 756 } |
| 757 } |
| 758 } |
| 759 |
| 760 |
| 761 void HGraph::OrderBlocks() { |
| 762 HPhase phase("Block ordering"); |
| 763 BitVector visited(blocks_.length()); |
| 764 |
| 765 ZoneList<HBasicBlock*> reverse_result(8); |
| 766 HBasicBlock* start = blocks_[0]; |
| 767 Postorder(start, &visited, &reverse_result, NULL); |
| 768 |
| 769 blocks_.Clear(); |
| 770 int index = 0; |
| 771 for (int i = reverse_result.length() - 1; i >= 0; --i) { |
| 772 HBasicBlock* b = reverse_result[i]; |
| 773 blocks_.Add(b); |
| 774 b->set_block_id(index++); |
| 775 } |
| 776 } |
| 777 |
| 778 |
| 779 void HGraph::PostorderLoopBlocks(HLoopInformation* loop, |
| 780 BitVector* visited, |
| 781 ZoneList<HBasicBlock*>* order, |
| 782 HBasicBlock* loop_header) { |
| 783 for (int i = 0; i < loop->blocks()->length(); ++i) { |
| 784 HBasicBlock* b = loop->blocks()->at(i); |
| 785 Postorder(b->end()->SecondSuccessor(), visited, order, loop_header); |
| 786 Postorder(b->end()->FirstSuccessor(), visited, order, loop_header); |
| 787 if (b->IsLoopHeader() && b != loop->loop_header()) { |
| 788 PostorderLoopBlocks(b->loop_information(), visited, order, loop_header); |
| 789 } |
| 790 } |
| 791 } |
| 792 |
| 793 |
| 794 void HGraph::Postorder(HBasicBlock* block, |
| 795 BitVector* visited, |
| 796 ZoneList<HBasicBlock*>* order, |
| 797 HBasicBlock* loop_header) { |
| 798 if (block == NULL || visited->Contains(block->block_id())) return; |
| 799 if (block->parent_loop_header() != loop_header) return; |
| 800 visited->Add(block->block_id()); |
| 801 if (block->IsLoopHeader()) { |
| 802 PostorderLoopBlocks(block->loop_information(), visited, order, loop_header); |
| 803 Postorder(block->end()->SecondSuccessor(), visited, order, block); |
| 804 Postorder(block->end()->FirstSuccessor(), visited, order, block); |
| 805 } else { |
| 806 Postorder(block->end()->SecondSuccessor(), visited, order, loop_header); |
| 807 Postorder(block->end()->FirstSuccessor(), visited, order, loop_header); |
| 808 } |
| 809 ASSERT(block->end()->FirstSuccessor() == NULL || |
| 810 order->Contains(block->end()->FirstSuccessor()) || |
| 811 block->end()->FirstSuccessor()->IsLoopHeader()); |
| 812 ASSERT(block->end()->SecondSuccessor() == NULL || |
| 813 order->Contains(block->end()->SecondSuccessor()) || |
| 814 block->end()->SecondSuccessor()->IsLoopHeader()); |
| 815 order->Add(block); |
| 816 } |
| 817 |
| 818 |
| 819 void HGraph::AssignDominators() { |
| 820 HPhase phase("Assign dominators", this); |
| 821 for (int i = 0; i < blocks_.length(); ++i) { |
| 822 if (blocks_[i]->IsLoopHeader()) { |
| 823 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first()); |
| 824 } else { |
| 825 for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) { |
| 826 blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j)); |
| 827 } |
| 828 } |
| 829 } |
| 830 } |
| 831 |
| 832 |
| 833 void HGraph::EliminateRedundantPhis() { |
| 834 HPhase phase("Phi elimination", this); |
| 835 ZoneList<HValue*> uses_to_replace(2); |
| 836 |
| 837 // Worklist of phis that can potentially be eliminated. Initialized |
| 838 // with all phi nodes. When elimination of a phi node modifies |
| 839 // another phi node the modified phi node is added to the worklist. |
| 840 ZoneList<HPhi*> worklist(blocks_.length()); |
| 841 for (int i = 0; i < blocks_.length(); ++i) { |
| 842 worklist.AddAll(*blocks_[i]->phis()); |
| 843 } |
| 844 |
| 845 while (!worklist.is_empty()) { |
| 846 HPhi* phi = worklist.RemoveLast(); |
| 847 HBasicBlock* block = phi->block(); |
| 848 |
| 849 // Skip phi node if it was already replaced. |
| 850 if (block == NULL) continue; |
| 851 |
| 852 // Get replacement value if phi is redundant. |
| 853 HValue* value = phi->GetRedundantReplacement(); |
| 854 |
| 855 if (value != NULL) { |
| 856 // Iterate through uses finding the ones that should be |
| 857 // replaced. |
| 858 const ZoneList<HValue*>* uses = phi->uses(); |
| 859 for (int i = 0; i < uses->length(); ++i) { |
| 860 HValue* use = uses->at(i); |
| 861 if (!use->block()->IsStartBlock()) { |
| 862 uses_to_replace.Add(use); |
| 863 } |
| 864 } |
| 865 // Replace the uses and add phis modified to the work list. |
| 866 for (int i = 0; i < uses_to_replace.length(); ++i) { |
| 867 HValue* use = uses_to_replace[i]; |
| 868 phi->ReplaceAtUse(use, value); |
| 869 if (use->IsPhi()) worklist.Add(HPhi::cast(use)); |
| 870 } |
| 871 uses_to_replace.Rewind(0); |
| 872 block->RemovePhi(phi); |
| 873 } else if (phi->HasNoUses() && |
| 874 !phi->HasReceiverOperand() && |
| 875 FLAG_eliminate_dead_phis) { |
| 876 // We can't eliminate phis that have the receiver as an operand |
| 877 // because in case of throwing an error we need the correct |
| 878 // receiver value in the environment to construct a corrent |
| 879 // stack trace. |
| 880 block->RemovePhi(phi); |
| 881 block->RecordDeletedPhi(phi->merged_index()); |
| 882 } |
| 883 } |
| 884 } |
| 885 |
| 886 |
| 887 bool HGraph::CollectPhis() { |
| 888 const ZoneList<HBasicBlock*>* blocks = graph_->blocks(); |
| 889 phi_list_ = new ZoneList<HPhi*>(blocks->length()); |
| 890 for (int i = 0; i < blocks->length(); ++i) { |
| 891 for (int j = 0; j < blocks->at(i)->phis()->length(); j++) { |
| 892 HPhi* phi = blocks->at(i)->phis()->at(j); |
| 893 phi_list_->Add(phi); |
| 894 // We don't support phi uses of arguments for now. |
| 895 if (phi->CheckFlag(HValue::kIsArguments)) return false; |
| 896 } |
| 897 } |
| 898 return true; |
| 899 } |
| 900 |
| 901 |
| 902 void HGraph::InferTypes(ZoneList<HValue*>* worklist) { |
| 903 BitVector in_worklist(GetMaximumValueID()); |
| 904 for (int i = 0; i < worklist->length(); ++i) { |
| 905 ASSERT(!in_worklist.Contains(worklist->at(i)->id())); |
| 906 in_worklist.Add(worklist->at(i)->id()); |
| 907 } |
| 908 |
| 909 while (!worklist->is_empty()) { |
| 910 HValue* current = worklist->RemoveLast(); |
| 911 in_worklist.Remove(current->id()); |
| 912 if (current->UpdateInferredType()) { |
| 913 for (int j = 0; j < current->uses()->length(); j++) { |
| 914 HValue* use = current->uses()->at(j); |
| 915 if (!in_worklist.Contains(use->id())) { |
| 916 in_worklist.Add(use->id()); |
| 917 worklist->Add(use); |
| 918 } |
| 919 } |
| 920 } |
| 921 } |
| 922 } |
| 923 |
| 924 |
| 925 class HRangeAnalysis BASE_EMBEDDED { |
| 926 public: |
| 927 explicit HRangeAnalysis(HGraph* graph) : graph_(graph), changed_ranges_(16) {} |
| 928 |
| 929 void Analyze(); |
| 930 |
| 931 private: |
| 932 void TraceRange(const char* msg, ...); |
| 933 void Analyze(HBasicBlock* block); |
| 934 void InferControlFlowRange(HBranch* branch, HBasicBlock* dest); |
| 935 void InferControlFlowRange(Token::Value op, HValue* value, HValue* other); |
| 936 void InferPhiRange(HPhi* phi); |
| 937 void InferRange(HValue* value); |
| 938 void RollBackTo(int index); |
| 939 void AddRange(HValue* value, Range* range); |
| 940 |
| 941 HGraph* graph_; |
| 942 ZoneList<HValue*> changed_ranges_; |
| 943 }; |
| 944 |
| 945 |
| 946 void HRangeAnalysis::TraceRange(const char* msg, ...) { |
| 947 if (FLAG_trace_range) { |
| 948 va_list arguments; |
| 949 va_start(arguments, msg); |
| 950 OS::VPrint(msg, arguments); |
| 951 va_end(arguments); |
| 952 } |
| 953 } |
| 954 |
| 955 |
| 956 void HRangeAnalysis::Analyze() { |
| 957 HPhase phase("Range analysis", graph_); |
| 958 Analyze(graph_->blocks()->at(0)); |
| 959 } |
| 960 |
| 961 |
| 962 void HRangeAnalysis::Analyze(HBasicBlock* block) { |
| 963 TraceRange("Analyzing block B%d\n", block->block_id()); |
| 964 |
| 965 int last_changed_range = changed_ranges_.length() - 1; |
| 966 |
| 967 // Infer range based on control flow. |
| 968 if (block->predecessors()->length() == 1) { |
| 969 HBasicBlock* pred = block->predecessors()->first(); |
| 970 if (pred->end()->IsBranch()) { |
| 971 InferControlFlowRange(HBranch::cast(pred->end()), block); |
| 972 } |
| 973 } |
| 974 |
| 975 // Process phi instructions. |
| 976 for (int i = 0; i < block->phis()->length(); ++i) { |
| 977 HPhi* phi = block->phis()->at(i); |
| 978 InferPhiRange(phi); |
| 979 } |
| 980 |
| 981 // Go through all instructions of the current block. |
| 982 HInstruction* instr = block->first(); |
| 983 while (instr != block->end()) { |
| 984 InferRange(instr); |
| 985 instr = instr->next(); |
| 986 } |
| 987 |
| 988 // Continue analysis in all dominated blocks. |
| 989 for (int i = 0; i < block->dominated_blocks()->length(); ++i) { |
| 990 Analyze(block->dominated_blocks()->at(i)); |
| 991 } |
| 992 |
| 993 RollBackTo(last_changed_range); |
| 994 } |
| 995 |
| 996 |
| 997 void HRangeAnalysis::InferControlFlowRange(HBranch* branch, HBasicBlock* dest) { |
| 998 ASSERT(branch->FirstSuccessor() == dest || branch->SecondSuccessor() == dest); |
| 999 ASSERT(branch->FirstSuccessor() != dest || branch->SecondSuccessor() != dest); |
| 1000 |
| 1001 if (branch->value()->IsCompare()) { |
| 1002 HCompare* compare = HCompare::cast(branch->value()); |
| 1003 Token::Value op = compare->token(); |
| 1004 if (branch->SecondSuccessor() == dest) { |
| 1005 op = Token::NegateCompareOp(op); |
| 1006 } |
| 1007 Token::Value inverted_op = Token::InvertCompareOp(op); |
| 1008 InferControlFlowRange(op, compare->left(), compare->right()); |
| 1009 InferControlFlowRange(inverted_op, compare->right(), compare->left()); |
| 1010 } |
| 1011 } |
| 1012 |
| 1013 |
| 1014 // We know that value [op] other. Use this information to update the range on |
| 1015 // value. |
| 1016 void HRangeAnalysis::InferControlFlowRange(Token::Value op, |
| 1017 HValue* value, |
| 1018 HValue* other) { |
| 1019 Range* range = other->range(); |
| 1020 if (range == NULL) range = new Range(); |
| 1021 Range* new_range = NULL; |
| 1022 |
| 1023 TraceRange("Control flow range infer %d %s %d\n", |
| 1024 value->id(), |
| 1025 Token::Name(op), |
| 1026 other->id()); |
| 1027 |
| 1028 if (op == Token::EQ || op == Token::EQ_STRICT) { |
| 1029 // The same range has to apply for value. |
| 1030 new_range = range->Copy(); |
| 1031 } else if (op == Token::LT || op == Token::LTE) { |
| 1032 new_range = range->CopyClearLower(); |
| 1033 if (op == Token::LT) { |
| 1034 new_range->Add(-1); |
| 1035 } |
| 1036 } else if (op == Token::GT || op == Token::GTE) { |
| 1037 new_range = range->CopyClearUpper(); |
| 1038 if (op == Token::GT) { |
| 1039 new_range->Add(1); |
| 1040 } |
| 1041 } |
| 1042 |
| 1043 if (new_range != NULL && !new_range->IsMostGeneric()) { |
| 1044 AddRange(value, new_range); |
| 1045 } |
| 1046 } |
| 1047 |
| 1048 |
| 1049 void HRangeAnalysis::InferPhiRange(HPhi* phi) { |
| 1050 // TODO(twuerthinger): Infer loop phi ranges. |
| 1051 InferRange(phi); |
| 1052 } |
| 1053 |
| 1054 |
| 1055 void HRangeAnalysis::InferRange(HValue* value) { |
| 1056 ASSERT(!value->HasRange()); |
| 1057 if (!value->representation().IsNone()) { |
| 1058 value->ComputeInitialRange(); |
| 1059 Range* range = value->range(); |
| 1060 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n", |
| 1061 value->id(), |
| 1062 value->Mnemonic(), |
| 1063 range->lower(), |
| 1064 range->upper()); |
| 1065 } |
| 1066 } |
| 1067 |
| 1068 |
| 1069 void HRangeAnalysis::RollBackTo(int index) { |
| 1070 for (int i = index + 1; i < changed_ranges_.length(); ++i) { |
| 1071 changed_ranges_[i]->RemoveLastAddedRange(); |
| 1072 } |
| 1073 changed_ranges_.Rewind(index + 1); |
| 1074 } |
| 1075 |
| 1076 |
| 1077 void HRangeAnalysis::AddRange(HValue* value, Range* range) { |
| 1078 Range* original_range = value->range(); |
| 1079 value->AddNewRange(range); |
| 1080 changed_ranges_.Add(value); |
| 1081 Range* new_range = value->range(); |
| 1082 TraceRange("Updated range of %d set to [%d,%d]\n", |
| 1083 value->id(), |
| 1084 new_range->lower(), |
| 1085 new_range->upper()); |
| 1086 if (original_range != NULL) { |
| 1087 TraceRange("Original range was [%d,%d]\n", |
| 1088 original_range->lower(), |
| 1089 original_range->upper()); |
| 1090 } |
| 1091 TraceRange("New information was [%d,%d]\n", |
| 1092 range->lower(), |
| 1093 range->upper()); |
| 1094 } |
| 1095 |
| 1096 |
| 1097 void TraceGVN(const char* msg, ...) { |
| 1098 if (FLAG_trace_gvn) { |
| 1099 va_list arguments; |
| 1100 va_start(arguments, msg); |
| 1101 OS::VPrint(msg, arguments); |
| 1102 va_end(arguments); |
| 1103 } |
| 1104 } |
| 1105 |
| 1106 |
| 1107 HValueMap::HValueMap(const HValueMap* other) |
| 1108 : array_size_(other->array_size_), |
| 1109 lists_size_(other->lists_size_), |
| 1110 count_(other->count_), |
| 1111 present_flags_(other->present_flags_), |
| 1112 array_(ZONE->NewArray<HValueMapListElement>(other->array_size_)), |
| 1113 lists_(ZONE->NewArray<HValueMapListElement>(other->lists_size_)), |
| 1114 free_list_head_(other->free_list_head_) { |
| 1115 memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement)); |
| 1116 memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement)); |
| 1117 } |
| 1118 |
| 1119 |
| 1120 void HValueMap::Kill(int flags) { |
| 1121 int depends_flags = HValue::ConvertChangesToDependsFlags(flags); |
| 1122 if ((present_flags_ & depends_flags) == 0) return; |
| 1123 present_flags_ = 0; |
| 1124 for (int i = 0; i < array_size_; ++i) { |
| 1125 HValue* value = array_[i].value; |
| 1126 if (value != NULL) { |
| 1127 // Clear list of collisions first, so we know if it becomes empty. |
| 1128 int kept = kNil; // List of kept elements. |
| 1129 int next; |
| 1130 for (int current = array_[i].next; current != kNil; current = next) { |
| 1131 next = lists_[current].next; |
| 1132 if ((lists_[current].value->flags() & depends_flags) != 0) { |
| 1133 // Drop it. |
| 1134 count_--; |
| 1135 lists_[current].next = free_list_head_; |
| 1136 free_list_head_ = current; |
| 1137 } else { |
| 1138 // Keep it. |
| 1139 lists_[current].next = kept; |
| 1140 kept = current; |
| 1141 present_flags_ |= lists_[current].value->flags(); |
| 1142 } |
| 1143 } |
| 1144 array_[i].next = kept; |
| 1145 |
| 1146 // Now possibly drop directly indexed element. |
| 1147 if ((array_[i].value->flags() & depends_flags) != 0) { // Drop it. |
| 1148 count_--; |
| 1149 int head = array_[i].next; |
| 1150 if (head == kNil) { |
| 1151 array_[i].value = NULL; |
| 1152 } else { |
| 1153 array_[i].value = lists_[head].value; |
| 1154 array_[i].next = lists_[head].next; |
| 1155 lists_[head].next = free_list_head_; |
| 1156 free_list_head_ = head; |
| 1157 } |
| 1158 } else { |
| 1159 present_flags_ |= array_[i].value->flags(); // Keep it. |
| 1160 } |
| 1161 } |
| 1162 } |
| 1163 } |
| 1164 |
| 1165 |
| 1166 HValue* HValueMap::Lookup(HValue* value) const { |
| 1167 uint32_t hash = value->Hashcode(); |
| 1168 uint32_t pos = Bound(hash); |
| 1169 if (array_[pos].value != NULL) { |
| 1170 if (array_[pos].value->Equals(value)) return array_[pos].value; |
| 1171 int next = array_[pos].next; |
| 1172 while (next != kNil) { |
| 1173 if (lists_[next].value->Equals(value)) return lists_[next].value; |
| 1174 next = lists_[next].next; |
| 1175 } |
| 1176 } |
| 1177 return NULL; |
| 1178 } |
| 1179 |
| 1180 |
| 1181 void HValueMap::Resize(int new_size) { |
| 1182 ASSERT(new_size > count_); |
| 1183 // Hashing the values into the new array has no more collisions than in the |
| 1184 // old hash map, so we can use the existing lists_ array, if we are careful. |
| 1185 |
| 1186 // Make sure we have at least one free element. |
| 1187 if (free_list_head_ == kNil) { |
| 1188 ResizeLists(lists_size_ << 1); |
| 1189 } |
| 1190 |
| 1191 HValueMapListElement* new_array = |
| 1192 ZONE->NewArray<HValueMapListElement>(new_size); |
| 1193 memset(new_array, 0, sizeof(HValueMapListElement) * new_size); |
| 1194 |
| 1195 HValueMapListElement* old_array = array_; |
| 1196 int old_size = array_size_; |
| 1197 |
| 1198 int old_count = count_; |
| 1199 count_ = 0; |
| 1200 // Do not modify present_flags_. It is currently correct. |
| 1201 array_size_ = new_size; |
| 1202 array_ = new_array; |
| 1203 |
| 1204 if (old_array != NULL) { |
| 1205 // Iterate over all the elements in lists, rehashing them. |
| 1206 for (int i = 0; i < old_size; ++i) { |
| 1207 if (old_array[i].value != NULL) { |
| 1208 int current = old_array[i].next; |
| 1209 while (current != kNil) { |
| 1210 Insert(lists_[current].value); |
| 1211 int next = lists_[current].next; |
| 1212 lists_[current].next = free_list_head_; |
| 1213 free_list_head_ = current; |
| 1214 current = next; |
| 1215 } |
| 1216 // Rehash the directly stored value. |
| 1217 Insert(old_array[i].value); |
| 1218 } |
| 1219 } |
| 1220 } |
| 1221 USE(old_count); |
| 1222 ASSERT(count_ == old_count); |
| 1223 } |
| 1224 |
| 1225 |
| 1226 void HValueMap::ResizeLists(int new_size) { |
| 1227 ASSERT(new_size > lists_size_); |
| 1228 |
| 1229 HValueMapListElement* new_lists = |
| 1230 ZONE->NewArray<HValueMapListElement>(new_size); |
| 1231 memset(new_lists, 0, sizeof(HValueMapListElement) * new_size); |
| 1232 |
| 1233 HValueMapListElement* old_lists = lists_; |
| 1234 int old_size = lists_size_; |
| 1235 |
| 1236 lists_size_ = new_size; |
| 1237 lists_ = new_lists; |
| 1238 |
| 1239 if (old_lists != NULL) { |
| 1240 memcpy(lists_, old_lists, old_size * sizeof(HValueMapListElement)); |
| 1241 } |
| 1242 for (int i = old_size; i < lists_size_; ++i) { |
| 1243 lists_[i].next = free_list_head_; |
| 1244 free_list_head_ = i; |
| 1245 } |
| 1246 } |
| 1247 |
| 1248 |
| 1249 void HValueMap::Insert(HValue* value) { |
| 1250 ASSERT(value != NULL); |
| 1251 // Resizing when half of the hashtable is filled up. |
| 1252 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1); |
| 1253 ASSERT(count_ < array_size_); |
| 1254 count_++; |
| 1255 uint32_t pos = Bound(value->Hashcode()); |
| 1256 if (array_[pos].value == NULL) { |
| 1257 array_[pos].value = value; |
| 1258 array_[pos].next = kNil; |
| 1259 } else { |
| 1260 if (free_list_head_ == kNil) { |
| 1261 ResizeLists(lists_size_ << 1); |
| 1262 } |
| 1263 int new_element_pos = free_list_head_; |
| 1264 ASSERT(new_element_pos != kNil); |
| 1265 free_list_head_ = lists_[free_list_head_].next; |
| 1266 lists_[new_element_pos].value = value; |
| 1267 lists_[new_element_pos].next = array_[pos].next; |
| 1268 ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL); |
| 1269 array_[pos].next = new_element_pos; |
| 1270 } |
| 1271 } |
| 1272 |
| 1273 |
| 1274 class HStackCheckEliminator BASE_EMBEDDED { |
| 1275 public: |
| 1276 explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { } |
| 1277 |
| 1278 void Process(); |
| 1279 |
| 1280 private: |
| 1281 void RemoveStackCheck(HBasicBlock* block); |
| 1282 |
| 1283 HGraph* graph_; |
| 1284 }; |
| 1285 |
| 1286 |
| 1287 void HStackCheckEliminator::Process() { |
| 1288 // For each loop block walk the dominator tree from the backwards branch to |
| 1289 // the loop header. If a call instruction is encountered the backwards branch |
| 1290 // is dominated by a call and the stack check in the backwards branch can be |
| 1291 // removed. |
| 1292 for (int i = 0; i < graph_->blocks()->length(); i++) { |
| 1293 HBasicBlock* block = graph_->blocks()->at(i); |
| 1294 if (block->IsLoopHeader()) { |
| 1295 HBasicBlock* backedge = block->loop_information()->GetLastBackEdge(); |
| 1296 HBasicBlock* dominator = backedge; |
| 1297 bool backedge_dominated_by_call = false; |
| 1298 while (dominator != block && !backedge_dominated_by_call) { |
| 1299 HInstruction* instr = dominator->first(); |
| 1300 while (instr != NULL && !backedge_dominated_by_call) { |
| 1301 if (instr->IsCall()) { |
| 1302 RemoveStackCheck(backedge); |
| 1303 backedge_dominated_by_call = true; |
| 1304 } |
| 1305 instr = instr->next(); |
| 1306 } |
| 1307 dominator = dominator->dominator(); |
| 1308 } |
| 1309 } |
| 1310 } |
| 1311 } |
| 1312 |
| 1313 |
| 1314 void HStackCheckEliminator::RemoveStackCheck(HBasicBlock* block) { |
| 1315 HInstruction* instr = block->first(); |
| 1316 while (instr != NULL) { |
| 1317 if (instr->IsGoto()) { |
| 1318 HGoto::cast(instr)->set_include_stack_check(false); |
| 1319 return; |
| 1320 } |
| 1321 instr = instr->next(); |
| 1322 } |
| 1323 } |
| 1324 |
| 1325 |
| 1326 class HGlobalValueNumberer BASE_EMBEDDED { |
| 1327 public: |
| 1328 explicit HGlobalValueNumberer(HGraph* graph) |
| 1329 : graph_(graph), |
| 1330 block_side_effects_(graph_->blocks()->length()), |
| 1331 loop_side_effects_(graph_->blocks()->length()) { |
| 1332 ASSERT(HEAP->allow_allocation(false)); |
| 1333 block_side_effects_.AddBlock(0, graph_->blocks()->length()); |
| 1334 loop_side_effects_.AddBlock(0, graph_->blocks()->length()); |
| 1335 } |
| 1336 ~HGlobalValueNumberer() { |
| 1337 ASSERT(!HEAP->allow_allocation(true)); |
| 1338 } |
| 1339 |
| 1340 void Analyze(); |
| 1341 |
| 1342 private: |
| 1343 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); |
| 1349 bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); |
| 1350 |
| 1351 HGraph* graph_; |
| 1352 |
| 1353 // A map of block IDs to their side effects. |
| 1354 ZoneList<int> block_side_effects_; |
| 1355 |
| 1356 // A map of loop header block IDs to their loop's side effects. |
| 1357 ZoneList<int> loop_side_effects_; |
| 1358 }; |
| 1359 |
| 1360 |
| 1361 void HGlobalValueNumberer::Analyze() { |
| 1362 ComputeBlockSideEffects(); |
| 1363 if (FLAG_loop_invariant_code_motion) { |
| 1364 LoopInvariantCodeMotion(); |
| 1365 } |
| 1366 HValueMap* map = new HValueMap(); |
| 1367 AnalyzeBlock(graph_->blocks()->at(0), map); |
| 1368 } |
| 1369 |
| 1370 |
| 1371 void HGlobalValueNumberer::ComputeBlockSideEffects() { |
| 1372 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { |
| 1373 // Compute side effects for the block. |
| 1374 HBasicBlock* block = graph_->blocks()->at(i); |
| 1375 HInstruction* instr = block->first(); |
| 1376 int id = block->block_id(); |
| 1377 int side_effects = 0; |
| 1378 while (instr != NULL) { |
| 1379 side_effects |= (instr->flags() & HValue::ChangesFlagsMask()); |
| 1380 instr = instr->next(); |
| 1381 } |
| 1382 block_side_effects_[id] |= side_effects; |
| 1383 |
| 1384 // Loop headers are part of their loop. |
| 1385 if (block->IsLoopHeader()) { |
| 1386 loop_side_effects_[id] |= side_effects; |
| 1387 } |
| 1388 |
| 1389 // Propagate loop side effects upwards. |
| 1390 if (block->HasParentLoopHeader()) { |
| 1391 int header_id = block->parent_loop_header()->block_id(); |
| 1392 loop_side_effects_[header_id] |= |
| 1393 block->IsLoopHeader() ? loop_side_effects_[id] : side_effects; |
| 1394 } |
| 1395 } |
| 1396 } |
| 1397 |
| 1398 |
| 1399 void HGlobalValueNumberer::LoopInvariantCodeMotion() { |
| 1400 for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { |
| 1401 HBasicBlock* block = graph_->blocks()->at(i); |
| 1402 if (block->IsLoopHeader()) { |
| 1403 int side_effects = loop_side_effects_[block->block_id()]; |
| 1404 TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n", |
| 1405 block->block_id(), |
| 1406 side_effects); |
| 1407 |
| 1408 HBasicBlock* last = block->loop_information()->GetLastBackEdge(); |
| 1409 for (int j = block->block_id(); j <= last->block_id(); ++j) { |
| 1410 ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects); |
| 1411 } |
| 1412 } |
| 1413 } |
| 1414 } |
| 1415 |
| 1416 |
| 1417 void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block, |
| 1418 HBasicBlock* loop_header, |
| 1419 int loop_kills) { |
| 1420 HBasicBlock* pre_header = loop_header->predecessors()->at(0); |
| 1421 int depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); |
| 1422 TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n", |
| 1423 block->block_id(), |
| 1424 depends_flags); |
| 1425 HInstruction* instr = block->first(); |
| 1426 while (instr != NULL) { |
| 1427 HInstruction* next = instr->next(); |
| 1428 if (instr->CheckFlag(HValue::kUseGVN) && |
| 1429 (instr->flags() & depends_flags) == 0) { |
| 1430 TraceGVN("Checking instruction %d (%s)\n", |
| 1431 instr->id(), |
| 1432 instr->Mnemonic()); |
| 1433 bool inputs_loop_invariant = true; |
| 1434 for (int i = 0; i < instr->OperandCount(); ++i) { |
| 1435 if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { |
| 1436 inputs_loop_invariant = false; |
| 1437 } |
| 1438 } |
| 1439 |
| 1440 if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { |
| 1441 TraceGVN("Found loop invariant instruction %d\n", instr->id()); |
| 1442 // Move the instruction out of the loop. |
| 1443 instr->Unlink(); |
| 1444 instr->InsertBefore(pre_header->end()); |
| 1445 } |
| 1446 } |
| 1447 instr = next; |
| 1448 } |
| 1449 } |
| 1450 |
| 1451 // Only move instructions that postdominate the loop header (i.e. are |
| 1452 // always executed inside the loop). This is to avoid unnecessary |
| 1453 // deoptimizations assuming the loop is executed at least once. |
| 1454 // TODO(fschneider): Better type feedback should give us information |
| 1455 // about code that was never executed. |
| 1456 bool HGlobalValueNumberer::ShouldMove(HInstruction* instr, |
| 1457 HBasicBlock* loop_header) { |
| 1458 if (!instr->IsChange() && |
| 1459 FLAG_aggressive_loop_invariant_motion) return true; |
| 1460 HBasicBlock* block = instr->block(); |
| 1461 bool result = true; |
| 1462 if (block != loop_header) { |
| 1463 for (int i = 1; i < loop_header->predecessors()->length(); ++i) { |
| 1464 bool found = false; |
| 1465 HBasicBlock* pred = loop_header->predecessors()->at(i); |
| 1466 while (pred != loop_header) { |
| 1467 if (pred == block) found = true; |
| 1468 pred = pred->dominator(); |
| 1469 } |
| 1470 if (!found) { |
| 1471 result = false; |
| 1472 break; |
| 1473 } |
| 1474 } |
| 1475 } |
| 1476 return result; |
| 1477 } |
| 1478 |
| 1479 |
| 1480 void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { |
| 1481 TraceGVN("Analyzing block B%d\n", block->block_id()); |
| 1482 |
| 1483 // If this is a loop header kill everything killed by the loop. |
| 1484 if (block->IsLoopHeader()) { |
| 1485 map->Kill(loop_side_effects_[block->block_id()]); |
| 1486 } |
| 1487 |
| 1488 // Go through all instructions of the current block. |
| 1489 HInstruction* instr = block->first(); |
| 1490 while (instr != NULL) { |
| 1491 HInstruction* next = instr->next(); |
| 1492 int flags = (instr->flags() & HValue::ChangesFlagsMask()); |
| 1493 if (flags != 0) { |
| 1494 ASSERT(!instr->CheckFlag(HValue::kUseGVN)); |
| 1495 // Clear all instructions in the map that are affected by side effects. |
| 1496 map->Kill(flags); |
| 1497 TraceGVN("Instruction %d kills\n", instr->id()); |
| 1498 } else if (instr->CheckFlag(HValue::kUseGVN)) { |
| 1499 HValue* other = map->Lookup(instr); |
| 1500 if (other != NULL) { |
| 1501 ASSERT(instr->Equals(other) && other->Equals(instr)); |
| 1502 TraceGVN("Replacing value %d (%s) with value %d (%s)\n", |
| 1503 instr->id(), |
| 1504 instr->Mnemonic(), |
| 1505 other->id(), |
| 1506 other->Mnemonic()); |
| 1507 instr->ReplaceValue(other); |
| 1508 instr->Delete(); |
| 1509 } else { |
| 1510 map->Add(instr); |
| 1511 } |
| 1512 } |
| 1513 instr = next; |
| 1514 } |
| 1515 |
| 1516 // Recursively continue analysis for all immediately dominated blocks. |
| 1517 int length = block->dominated_blocks()->length(); |
| 1518 for (int i = 0; i < length; ++i) { |
| 1519 HBasicBlock* dominated = block->dominated_blocks()->at(i); |
| 1520 // No need to copy the map for the last child in the dominator tree. |
| 1521 HValueMap* successor_map = (i == length - 1) ? map : map->Copy(); |
| 1522 |
| 1523 // If the dominated block is not a successor to this block we have to |
| 1524 // kill everything killed on any path between this block and the |
| 1525 // dominated block. Note we rely on the block ordering. |
| 1526 bool is_successor = false; |
| 1527 int predecessor_count = dominated->predecessors()->length(); |
| 1528 for (int j = 0; !is_successor && j < predecessor_count; ++j) { |
| 1529 is_successor = (dominated->predecessors()->at(j) == block); |
| 1530 } |
| 1531 |
| 1532 if (!is_successor) { |
| 1533 int side_effects = 0; |
| 1534 for (int j = block->block_id() + 1; j < dominated->block_id(); ++j) { |
| 1535 side_effects |= block_side_effects_[j]; |
| 1536 } |
| 1537 successor_map->Kill(side_effects); |
| 1538 } |
| 1539 |
| 1540 AnalyzeBlock(dominated, successor_map); |
| 1541 } |
| 1542 } |
| 1543 |
| 1544 |
| 1545 class HInferRepresentation BASE_EMBEDDED { |
| 1546 public: |
| 1547 explicit HInferRepresentation(HGraph* graph) |
| 1548 : graph_(graph), worklist_(8), in_worklist_(graph->GetMaximumValueID()) {} |
| 1549 |
| 1550 void Analyze(); |
| 1551 |
| 1552 private: |
| 1553 Representation TryChange(HValue* current); |
| 1554 void AddToWorklist(HValue* current); |
| 1555 void InferBasedOnInputs(HValue* current); |
| 1556 void AddDependantsToWorklist(HValue* current); |
| 1557 void InferBasedOnUses(HValue* current); |
| 1558 |
| 1559 HGraph* graph_; |
| 1560 ZoneList<HValue*> worklist_; |
| 1561 BitVector in_worklist_; |
| 1562 }; |
| 1563 |
| 1564 |
| 1565 void HInferRepresentation::AddToWorklist(HValue* current) { |
| 1566 if (current->representation().IsSpecialization()) return; |
| 1567 if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return; |
| 1568 if (in_worklist_.Contains(current->id())) return; |
| 1569 worklist_.Add(current); |
| 1570 in_worklist_.Add(current->id()); |
| 1571 } |
| 1572 |
| 1573 |
| 1574 // This method tries to specialize the representation type of the value |
| 1575 // given as a parameter. The value is asked to infer its representation type |
| 1576 // based on its inputs. If the inferred type is more specialized, then this |
| 1577 // becomes the new representation type of the node. |
| 1578 void HInferRepresentation::InferBasedOnInputs(HValue* current) { |
| 1579 Representation r = current->representation(); |
| 1580 if (r.IsSpecialization()) return; |
| 1581 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation)); |
| 1582 Representation inferred = current->InferredRepresentation(); |
| 1583 if (inferred.IsSpecialization()) { |
| 1584 current->ChangeRepresentation(inferred); |
| 1585 AddDependantsToWorklist(current); |
| 1586 } |
| 1587 } |
| 1588 |
| 1589 |
| 1590 void HInferRepresentation::AddDependantsToWorklist(HValue* current) { |
| 1591 for (int i = 0; i < current->uses()->length(); ++i) { |
| 1592 AddToWorklist(current->uses()->at(i)); |
| 1593 } |
| 1594 for (int i = 0; i < current->OperandCount(); ++i) { |
| 1595 AddToWorklist(current->OperandAt(i)); |
| 1596 } |
| 1597 } |
| 1598 |
| 1599 |
| 1600 // This method calculates whether specializing the representation of the value |
| 1601 // given as the parameter has a benefit in terms of less necessary type |
| 1602 // conversions. If there is a benefit, then the representation of the value is |
| 1603 // specialized. |
| 1604 void HInferRepresentation::InferBasedOnUses(HValue* current) { |
| 1605 Representation r = current->representation(); |
| 1606 if (r.IsSpecialization() || current->HasNoUses()) return; |
| 1607 ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation)); |
| 1608 Representation new_rep = TryChange(current); |
| 1609 if (!new_rep.IsNone()) { |
| 1610 if (!current->representation().Equals(new_rep)) { |
| 1611 current->ChangeRepresentation(new_rep); |
| 1612 AddDependantsToWorklist(current); |
| 1613 } |
| 1614 } |
| 1615 } |
| 1616 |
| 1617 |
| 1618 Representation HInferRepresentation::TryChange(HValue* current) { |
| 1619 // Array of use counts for each representation. |
| 1620 int use_count[Representation::kNumRepresentations]; |
| 1621 for (int i = 0; i < Representation::kNumRepresentations; i++) { |
| 1622 use_count[i] = 0; |
| 1623 } |
| 1624 |
| 1625 for (int i = 0; i < current->uses()->length(); ++i) { |
| 1626 HValue* use = current->uses()->at(i); |
| 1627 int index = use->LookupOperandIndex(0, current); |
| 1628 Representation req_rep = use->RequiredInputRepresentation(index); |
| 1629 if (req_rep.IsNone()) continue; |
| 1630 if (use->IsPhi()) { |
| 1631 HPhi* phi = HPhi::cast(use); |
| 1632 phi->AddIndirectUsesTo(&use_count[0]); |
| 1633 } |
| 1634 use_count[req_rep.kind()]++; |
| 1635 } |
| 1636 int tagged_count = use_count[Representation::kTagged]; |
| 1637 int double_count = use_count[Representation::kDouble]; |
| 1638 int int32_count = use_count[Representation::kInteger32]; |
| 1639 int non_tagged_count = double_count + int32_count; |
| 1640 |
| 1641 // If a non-loop phi has tagged uses, don't convert it to untagged. |
| 1642 if (current->IsPhi() && !current->block()->IsLoopHeader()) { |
| 1643 if (tagged_count > 0) return Representation::None(); |
| 1644 } |
| 1645 |
| 1646 if (non_tagged_count >= tagged_count) { |
| 1647 // More untagged than tagged. |
| 1648 if (double_count > 0) { |
| 1649 // There is at least one usage that is a double => guess that the |
| 1650 // correct representation is double. |
| 1651 return Representation::Double(); |
| 1652 } else if (int32_count > 0) { |
| 1653 return Representation::Integer32(); |
| 1654 } |
| 1655 } |
| 1656 return Representation::None(); |
| 1657 } |
| 1658 |
| 1659 |
| 1660 void HInferRepresentation::Analyze() { |
| 1661 HPhase phase("Infer representations", graph_); |
| 1662 |
| 1663 // (1) Initialize bit vectors and count real uses. Each phi |
| 1664 // gets a bit-vector of length <number of phis>. |
| 1665 const ZoneList<HPhi*>* phi_list = graph_->phi_list(); |
| 1666 int num_phis = phi_list->length(); |
| 1667 ScopedVector<BitVector*> connected_phis(num_phis); |
| 1668 for (int i = 0; i < num_phis; i++) { |
| 1669 phi_list->at(i)->InitRealUses(i); |
| 1670 connected_phis[i] = new BitVector(num_phis); |
| 1671 connected_phis[i]->Add(i); |
| 1672 } |
| 1673 |
| 1674 // (2) Do a fixed point iteration to find the set of connected phis. |
| 1675 // A phi is connected to another phi if its value is used either |
| 1676 // directly or indirectly through a transitive closure of the def-use |
| 1677 // relation. |
| 1678 bool change = true; |
| 1679 while (change) { |
| 1680 change = false; |
| 1681 for (int i = 0; i < num_phis; i++) { |
| 1682 HPhi* phi = phi_list->at(i); |
| 1683 for (int j = 0; j < phi->uses()->length(); j++) { |
| 1684 HValue* use = phi->uses()->at(j); |
| 1685 if (use->IsPhi()) { |
| 1686 int phi_use = HPhi::cast(use)->phi_id(); |
| 1687 if (connected_phis[i]->UnionIsChanged(*connected_phis[phi_use])) { |
| 1688 change = true; |
| 1689 } |
| 1690 } |
| 1691 } |
| 1692 } |
| 1693 } |
| 1694 |
| 1695 // (3) Sum up the non-phi use counts of all connected phis. |
| 1696 // Don't include the non-phi uses of the phi itself. |
| 1697 for (int i = 0; i < num_phis; i++) { |
| 1698 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 |
| 1710 for (int i = 0; i < graph_->blocks()->length(); ++i) { |
| 1711 HBasicBlock* block = graph_->blocks()->at(i); |
| 1712 const ZoneList<HPhi*>* phis = block->phis(); |
| 1713 for (int j = 0; j < phis->length(); ++j) { |
| 1714 AddToWorklist(phis->at(j)); |
| 1715 } |
| 1716 |
| 1717 HInstruction* current = block->first(); |
| 1718 while (current != NULL) { |
| 1719 AddToWorklist(current); |
| 1720 current = current->next(); |
| 1721 } |
| 1722 } |
| 1723 |
| 1724 while (!worklist_.is_empty()) { |
| 1725 HValue* current = worklist_.RemoveLast(); |
| 1726 in_worklist_.Remove(current->id()); |
| 1727 InferBasedOnInputs(current); |
| 1728 InferBasedOnUses(current); |
| 1729 } |
| 1730 } |
| 1731 |
| 1732 |
| 1733 void HGraph::InitializeInferredTypes() { |
| 1734 HPhase phase("Inferring types", this); |
| 1735 InitializeInferredTypes(0, this->blocks_.length() - 1); |
| 1736 } |
| 1737 |
| 1738 |
| 1739 void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) { |
| 1740 for (int i = from_inclusive; i <= to_inclusive; ++i) { |
| 1741 HBasicBlock* block = blocks_[i]; |
| 1742 |
| 1743 const ZoneList<HPhi*>* phis = block->phis(); |
| 1744 for (int j = 0; j < phis->length(); j++) { |
| 1745 phis->at(j)->UpdateInferredType(); |
| 1746 } |
| 1747 |
| 1748 HInstruction* current = block->first(); |
| 1749 while (current != NULL) { |
| 1750 current->UpdateInferredType(); |
| 1751 current = current->next(); |
| 1752 } |
| 1753 |
| 1754 if (block->IsLoopHeader()) { |
| 1755 HBasicBlock* last_back_edge = |
| 1756 block->loop_information()->GetLastBackEdge(); |
| 1757 InitializeInferredTypes(i + 1, last_back_edge->block_id()); |
| 1758 // Skip all blocks already processed by the recursive call. |
| 1759 i = last_back_edge->block_id(); |
| 1760 // Update phis of the loop header now after the whole loop body is |
| 1761 // guaranteed to be processed. |
| 1762 ZoneList<HValue*> worklist(block->phis()->length()); |
| 1763 for (int j = 0; j < block->phis()->length(); ++j) { |
| 1764 worklist.Add(block->phis()->at(j)); |
| 1765 } |
| 1766 InferTypes(&worklist); |
| 1767 } |
| 1768 } |
| 1769 } |
| 1770 |
| 1771 |
| 1772 void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) { |
| 1773 HValue* current = value; |
| 1774 while (current != NULL) { |
| 1775 if (visited->Contains(current->id())) return; |
| 1776 |
| 1777 // For phis, we must propagate the check to all of its inputs. |
| 1778 if (current->IsPhi()) { |
| 1779 visited->Add(current->id()); |
| 1780 HPhi* phi = HPhi::cast(current); |
| 1781 for (int i = 0; i < phi->OperandCount(); ++i) { |
| 1782 PropagateMinusZeroChecks(phi->OperandAt(i), visited); |
| 1783 } |
| 1784 break; |
| 1785 } |
| 1786 |
| 1787 // For multiplication and division, we must propagate to the left and |
| 1788 // the right side. |
| 1789 if (current->IsMul()) { |
| 1790 HMul* mul = HMul::cast(current); |
| 1791 mul->EnsureAndPropagateNotMinusZero(visited); |
| 1792 PropagateMinusZeroChecks(mul->left(), visited); |
| 1793 PropagateMinusZeroChecks(mul->right(), visited); |
| 1794 } else if (current->IsDiv()) { |
| 1795 HDiv* div = HDiv::cast(current); |
| 1796 div->EnsureAndPropagateNotMinusZero(visited); |
| 1797 PropagateMinusZeroChecks(div->left(), visited); |
| 1798 PropagateMinusZeroChecks(div->right(), visited); |
| 1799 } |
| 1800 |
| 1801 current = current->EnsureAndPropagateNotMinusZero(visited); |
| 1802 } |
| 1803 } |
| 1804 |
| 1805 |
| 1806 void HGraph::InsertRepresentationChangeForUse(HValue* value, |
| 1807 HValue* use, |
| 1808 Representation to, |
| 1809 bool is_truncating) { |
| 1810 // Propagate flags for negative zero checks upwards from conversions |
| 1811 // int32-to-tagged and int32-to-double. |
| 1812 Representation from = value->representation(); |
| 1813 if (from.IsInteger32()) { |
| 1814 ASSERT(to.IsTagged() || to.IsDouble()); |
| 1815 BitVector visited(GetMaximumValueID()); |
| 1816 PropagateMinusZeroChecks(value, &visited); |
| 1817 } |
| 1818 |
| 1819 // Insert the representation change right before its use. For phi-uses we |
| 1820 // insert at the end of the corresponding predecessor. |
| 1821 HBasicBlock* insert_block = use->block(); |
| 1822 if (use->IsPhi()) { |
| 1823 int index = 0; |
| 1824 while (use->OperandAt(index) != value) ++index; |
| 1825 insert_block = insert_block->predecessors()->at(index); |
| 1826 } |
| 1827 |
| 1828 HInstruction* next = (insert_block == use->block()) |
| 1829 ? HInstruction::cast(use) |
| 1830 : insert_block->end(); |
| 1831 |
| 1832 // For constants we try to make the representation change at compile |
| 1833 // time. When a representation change is not possible without loss of |
| 1834 // information we treat constants like normal instructions and insert the |
| 1835 // change instructions for them. |
| 1836 HInstruction* new_value = NULL; |
| 1837 if (value->IsConstant()) { |
| 1838 HConstant* constant = HConstant::cast(value); |
| 1839 // Try to create a new copy of the constant with the new representation. |
| 1840 new_value = is_truncating |
| 1841 ? constant->CopyToTruncatedInt32() |
| 1842 : constant->CopyToRepresentation(to); |
| 1843 } |
| 1844 |
| 1845 if (new_value == NULL) { |
| 1846 new_value = new HChange(value, value->representation(), to); |
| 1847 } |
| 1848 |
| 1849 new_value->InsertBefore(next); |
| 1850 value->ReplaceFirstAtUse(use, new_value, to); |
| 1851 } |
| 1852 |
| 1853 |
| 1854 int CompareConversionUses(HValue* a, |
| 1855 HValue* b, |
| 1856 Representation a_rep, |
| 1857 Representation b_rep) { |
| 1858 if (a_rep.kind() > b_rep.kind()) { |
| 1859 // Make sure specializations are separated in the result array. |
| 1860 return 1; |
| 1861 } |
| 1862 // Put truncating conversions before non-truncating conversions. |
| 1863 bool a_truncate = a->CheckFlag(HValue::kTruncatingToInt32); |
| 1864 bool b_truncate = b->CheckFlag(HValue::kTruncatingToInt32); |
| 1865 if (a_truncate != b_truncate) { |
| 1866 return a_truncate ? -1 : 1; |
| 1867 } |
| 1868 // Sort by increasing block ID. |
| 1869 return a->block()->block_id() - b->block()->block_id(); |
| 1870 } |
| 1871 |
| 1872 |
| 1873 void HGraph::InsertRepresentationChanges(HValue* current) { |
| 1874 Representation r = current->representation(); |
| 1875 if (r.IsNone()) return; |
| 1876 if (current->uses()->length() == 0) return; |
| 1877 |
| 1878 // Collect the representation changes in a sorted list. This allows |
| 1879 // us to avoid duplicate changes without searching the list. |
| 1880 ZoneList<HValue*> to_convert(2); |
| 1881 ZoneList<Representation> to_convert_reps(2); |
| 1882 for (int i = 0; i < current->uses()->length(); ++i) { |
| 1883 HValue* use = current->uses()->at(i); |
| 1884 // The occurrences index means the index within the operand array of "use" |
| 1885 // at which "current" is used. While iterating through the use array we |
| 1886 // also have to iterate over the different occurrence indices. |
| 1887 int occurrence_index = 0; |
| 1888 if (use->UsesMultipleTimes(current)) { |
| 1889 occurrence_index = current->uses()->CountOccurrences(use, 0, i - 1); |
| 1890 if (FLAG_trace_representation) { |
| 1891 PrintF("Instruction %d is used multiple times at %d; occurrence=%d\n", |
| 1892 current->id(), |
| 1893 use->id(), |
| 1894 occurrence_index); |
| 1895 } |
| 1896 } |
| 1897 int operand_index = use->LookupOperandIndex(occurrence_index, current); |
| 1898 Representation req = use->RequiredInputRepresentation(operand_index); |
| 1899 if (req.IsNone() || req.Equals(r)) continue; |
| 1900 int index = 0; |
| 1901 while (to_convert.length() > index && |
| 1902 CompareConversionUses(to_convert[index], |
| 1903 use, |
| 1904 to_convert_reps[index], |
| 1905 req) < 0) { |
| 1906 ++index; |
| 1907 } |
| 1908 if (FLAG_trace_representation) { |
| 1909 PrintF("Inserting a representation change to %s of %d for use at %d\n", |
| 1910 req.Mnemonic(), |
| 1911 current->id(), |
| 1912 use->id()); |
| 1913 } |
| 1914 to_convert.InsertAt(index, use); |
| 1915 to_convert_reps.InsertAt(index, req); |
| 1916 } |
| 1917 |
| 1918 for (int i = 0; i < to_convert.length(); ++i) { |
| 1919 HValue* use = to_convert[i]; |
| 1920 Representation r_to = to_convert_reps[i]; |
| 1921 bool is_truncating = use->CheckFlag(HValue::kTruncatingToInt32); |
| 1922 InsertRepresentationChangeForUse(current, use, r_to, is_truncating); |
| 1923 } |
| 1924 |
| 1925 if (current->uses()->is_empty()) { |
| 1926 ASSERT(current->IsConstant()); |
| 1927 current->Delete(); |
| 1928 } |
| 1929 } |
| 1930 |
| 1931 |
| 1932 void HGraph::InsertRepresentationChanges() { |
| 1933 HPhase phase("Insert representation changes", this); |
| 1934 |
| 1935 |
| 1936 // Compute truncation flag for phis: Initially assume that all |
| 1937 // int32-phis allow truncation and iteratively remove the ones that |
| 1938 // are used in an operation that does not allow a truncating |
| 1939 // conversion. |
| 1940 // TODO(fschneider): Replace this with a worklist-based iteration. |
| 1941 for (int i = 0; i < phi_list()->length(); i++) { |
| 1942 HPhi* phi = phi_list()->at(i); |
| 1943 if (phi->representation().IsInteger32()) { |
| 1944 phi->SetFlag(HValue::kTruncatingToInt32); |
| 1945 } |
| 1946 } |
| 1947 bool change = true; |
| 1948 while (change) { |
| 1949 change = false; |
| 1950 for (int i = 0; i < phi_list()->length(); i++) { |
| 1951 HPhi* phi = phi_list()->at(i); |
| 1952 if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue; |
| 1953 for (int j = 0; j < phi->uses()->length(); j++) { |
| 1954 HValue* use = phi->uses()->at(j); |
| 1955 if (!use->CheckFlag(HValue::kTruncatingToInt32)) { |
| 1956 phi->ClearFlag(HValue::kTruncatingToInt32); |
| 1957 change = true; |
| 1958 break; |
| 1959 } |
| 1960 } |
| 1961 } |
| 1962 } |
| 1963 |
| 1964 for (int i = 0; i < blocks_.length(); ++i) { |
| 1965 // Process phi instructions first. |
| 1966 for (int j = 0; j < blocks_[i]->phis()->length(); j++) { |
| 1967 HPhi* phi = blocks_[i]->phis()->at(j); |
| 1968 InsertRepresentationChanges(phi); |
| 1969 } |
| 1970 |
| 1971 // Process normal instructions. |
| 1972 HInstruction* current = blocks_[i]->first(); |
| 1973 while (current != NULL) { |
| 1974 InsertRepresentationChanges(current); |
| 1975 current = current->next(); |
| 1976 } |
| 1977 } |
| 1978 } |
| 1979 |
| 1980 |
| 1981 // Implementation of utility classes to represent an expression's context in |
| 1982 // the AST. |
| 1983 AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) |
| 1984 : owner_(owner), kind_(kind), outer_(owner->ast_context()) { |
| 1985 owner->set_ast_context(this); // Push. |
| 1986 } |
| 1987 |
| 1988 |
| 1989 AstContext::~AstContext() { |
| 1990 owner_->set_ast_context(outer_); // Pop. |
| 1991 } |
| 1992 |
| 1993 |
| 1994 |
| 1995 // HGraphBuilder infrastructure for bailing out and checking bailouts. |
| 1996 #define BAILOUT(reason) \ |
| 1997 do { \ |
| 1998 Bailout(reason); \ |
| 1999 return; \ |
| 2000 } while (false) |
| 2001 |
| 2002 |
| 2003 #define CHECK_BAILOUT \ |
| 2004 do { \ |
| 2005 if (HasStackOverflow()) return; \ |
| 2006 } while (false) |
| 2007 |
| 2008 |
| 2009 #define VISIT_FOR_EFFECT(expr) \ |
| 2010 do { \ |
| 2011 VisitForEffect(expr); \ |
| 2012 if (HasStackOverflow()) return; \ |
| 2013 } while (false) |
| 2014 |
| 2015 |
| 2016 #define VISIT_FOR_VALUE(expr) \ |
| 2017 do { \ |
| 2018 VisitForValue(expr); \ |
| 2019 if (HasStackOverflow()) return; \ |
| 2020 } while (false) |
| 2021 |
| 2022 |
| 2023 // 'thing' could be an expression, statement, or list of statements. |
| 2024 #define ADD_TO_SUBGRAPH(graph, thing) \ |
| 2025 do { \ |
| 2026 AddToSubgraph(graph, thing); \ |
| 2027 if (HasStackOverflow()) return; \ |
| 2028 } while (false) |
| 2029 |
| 2030 |
| 2031 class HGraphBuilder::SubgraphScope BASE_EMBEDDED { |
| 2032 public: |
| 2033 SubgraphScope(HGraphBuilder* builder, HSubgraph* new_subgraph) |
| 2034 : builder_(builder) { |
| 2035 old_subgraph_ = builder_->current_subgraph_; |
| 2036 subgraph_ = new_subgraph; |
| 2037 builder_->current_subgraph_ = subgraph_; |
| 2038 } |
| 2039 |
| 2040 ~SubgraphScope() { |
| 2041 old_subgraph_->AddBreakContinueInfo(subgraph_); |
| 2042 builder_->current_subgraph_ = old_subgraph_; |
| 2043 } |
| 2044 |
| 2045 HSubgraph* subgraph() const { return subgraph_; } |
| 2046 |
| 2047 private: |
| 2048 HGraphBuilder* builder_; |
| 2049 HSubgraph* old_subgraph_; |
| 2050 HSubgraph* subgraph_; |
| 2051 }; |
| 2052 |
| 2053 |
| 2054 void HGraphBuilder::Bailout(const char* reason) { |
| 2055 if (FLAG_trace_bailout) { |
| 2056 SmartPointer<char> debug_name = graph()->debug_name()->ToCString(); |
| 2057 PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *debug_name, reason); |
| 2058 } |
| 2059 SetStackOverflow(); |
| 2060 } |
| 2061 |
| 2062 |
| 2063 void HGraphBuilder::VisitForEffect(Expression* expr) { |
| 2064 #ifdef DEBUG |
| 2065 int original_count = environment()->total_count(); |
| 2066 #endif |
| 2067 BinaryOperation* binary_op = expr->AsBinaryOperation(); |
| 2068 |
| 2069 // We use special casing for expression types not handled properly by our |
| 2070 // usual trick of pretending they're in a value context and cleaning up |
| 2071 // later. |
| 2072 if (binary_op != NULL && binary_op->op() == Token::COMMA) { |
| 2073 VISIT_FOR_EFFECT(binary_op->left()); |
| 2074 VISIT_FOR_EFFECT(binary_op->right()); |
| 2075 } else { |
| 2076 { EffectContext for_effect(this); |
| 2077 Visit(expr); |
| 2078 } |
| 2079 if (HasStackOverflow() || !subgraph()->HasExit()) return; |
| 2080 // Discard return value. |
| 2081 Pop(); |
| 2082 // TODO(kasperl): Try to improve the way we compute the last added |
| 2083 // instruction. The NULL check makes me uncomfortable. |
| 2084 HValue* last = subgraph()->exit_block()->GetLastInstruction(); |
| 2085 // We need to ensure we emit a simulate after inlined functions in an |
| 2086 // effect context, to avoid having a bailout target the fictional |
| 2087 // environment with the return value on top. |
| 2088 if ((last != NULL && last->HasSideEffects()) || |
| 2089 subgraph()->exit_block()->IsInlineReturnTarget()) { |
| 2090 AddSimulate(expr->id()); |
| 2091 } |
| 2092 } |
| 2093 |
| 2094 ASSERT(environment()->total_count() == original_count); |
| 2095 } |
| 2096 |
| 2097 |
| 2098 void HGraphBuilder::VisitForValue(Expression* expr) { |
| 2099 #ifdef DEBUG |
| 2100 int original_height = environment()->values()->length(); |
| 2101 #endif |
| 2102 { ValueContext for_value(this); |
| 2103 Visit(expr); |
| 2104 } |
| 2105 if (HasStackOverflow() || !subgraph()->HasExit()) return; |
| 2106 // TODO(kasperl): Try to improve the way we compute the last added |
| 2107 // instruction. The NULL check makes me uncomfortable. |
| 2108 HValue* last = subgraph()->exit_block()->GetLastInstruction(); |
| 2109 if (last != NULL && last->HasSideEffects()) { |
| 2110 AddSimulate(expr->id()); |
| 2111 } |
| 2112 ASSERT(environment()->values()->length() == original_height + 1); |
| 2113 } |
| 2114 |
| 2115 |
| 2116 HValue* HGraphBuilder::VisitArgument(Expression* expr) { |
| 2117 VisitForValue(expr); |
| 2118 if (HasStackOverflow() || !subgraph()->HasExit()) return NULL; |
| 2119 return environment()->Top(); |
| 2120 } |
| 2121 |
| 2122 |
| 2123 void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) { |
| 2124 for (int i = 0; i < arguments->length(); i++) { |
| 2125 VisitArgument(arguments->at(i)); |
| 2126 if (HasStackOverflow() || !current_subgraph_->HasExit()) return; |
| 2127 } |
| 2128 } |
| 2129 |
| 2130 |
| 2131 HGraph* HGraphBuilder::CreateGraph(CompilationInfo* info) { |
| 2132 ASSERT(current_subgraph_ == NULL); |
| 2133 graph_ = new HGraph(info); |
| 2134 |
| 2135 { |
| 2136 HPhase phase("Block building"); |
| 2137 graph_->Initialize(CreateBasicBlock(graph_->start_environment())); |
| 2138 current_subgraph_ = graph_; |
| 2139 |
| 2140 Scope* scope = info->scope(); |
| 2141 SetupScope(scope); |
| 2142 VisitDeclarations(scope->declarations()); |
| 2143 |
| 2144 AddInstruction(new HStackCheck()); |
| 2145 |
| 2146 ZoneList<Statement*>* stmts = info->function()->body(); |
| 2147 HSubgraph* body = CreateGotoSubgraph(environment()); |
| 2148 AddToSubgraph(body, stmts); |
| 2149 if (HasStackOverflow()) return NULL; |
| 2150 current_subgraph_->Append(body, NULL); |
| 2151 body->entry_block()->SetJoinId(info->function()->id()); |
| 2152 |
| 2153 if (graph_->HasExit()) { |
| 2154 graph_->FinishExit(new HReturn(graph_->GetConstantUndefined())); |
| 2155 } |
| 2156 } |
| 2157 |
| 2158 graph_->OrderBlocks(); |
| 2159 graph_->AssignDominators(); |
| 2160 graph_->EliminateRedundantPhis(); |
| 2161 if (!graph_->CollectPhis()) { |
| 2162 Bailout("Phi-use of arguments object"); |
| 2163 return NULL; |
| 2164 } |
| 2165 |
| 2166 HInferRepresentation rep(graph_); |
| 2167 rep.Analyze(); |
| 2168 |
| 2169 if (FLAG_use_range) { |
| 2170 HRangeAnalysis rangeAnalysis(graph_); |
| 2171 rangeAnalysis.Analyze(); |
| 2172 } |
| 2173 |
| 2174 graph_->InitializeInferredTypes(); |
| 2175 graph_->Canonicalize(); |
| 2176 graph_->InsertRepresentationChanges(); |
| 2177 |
| 2178 // Eliminate redundant stack checks on backwards branches. |
| 2179 HStackCheckEliminator sce(graph_); |
| 2180 sce.Process(); |
| 2181 |
| 2182 // Perform common subexpression elimination and loop-invariant code motion. |
| 2183 if (FLAG_use_gvn) { |
| 2184 HPhase phase("Global value numbering", graph_); |
| 2185 HGlobalValueNumberer gvn(graph_); |
| 2186 gvn.Analyze(); |
| 2187 } |
| 2188 |
| 2189 return graph_; |
| 2190 } |
| 2191 |
| 2192 |
| 2193 void HGraphBuilder::AddToSubgraph(HSubgraph* graph, Statement* stmt) { |
| 2194 SubgraphScope scope(this, graph); |
| 2195 Visit(stmt); |
| 2196 } |
| 2197 |
| 2198 |
| 2199 void HGraphBuilder::AddToSubgraph(HSubgraph* graph, Expression* expr) { |
| 2200 SubgraphScope scope(this, graph); |
| 2201 VisitForValue(expr); |
| 2202 } |
| 2203 |
| 2204 |
| 2205 void HGraphBuilder::VisitCondition(Expression* expr, |
| 2206 HBasicBlock* true_block, |
| 2207 HBasicBlock* false_block, |
| 2208 bool invert_true, |
| 2209 bool invert_false) { |
| 2210 VisitForControl(expr, true_block, false_block, invert_true, invert_false); |
| 2211 CHECK_BAILOUT; |
| 2212 #ifdef DEBUG |
| 2213 HValue* value = true_block->predecessors()->at(0)->last_environment()->Top(); |
| 2214 true_block->set_cond(HConstant::cast(value)->handle()); |
| 2215 |
| 2216 value = false_block->predecessors()->at(0)->last_environment()->Top(); |
| 2217 false_block->set_cond(HConstant::cast(value)->handle()); |
| 2218 #endif |
| 2219 |
| 2220 true_block->SetJoinId(expr->id()); |
| 2221 false_block->SetJoinId(expr->id()); |
| 2222 true_block->last_environment()->Pop(); |
| 2223 false_block->last_environment()->Pop(); |
| 2224 } |
| 2225 |
| 2226 |
| 2227 void HGraphBuilder::AddConditionToSubgraph(HSubgraph* subgraph, |
| 2228 Expression* expr, |
| 2229 HSubgraph* true_graph, |
| 2230 HSubgraph* false_graph) { |
| 2231 SubgraphScope scope(this, subgraph); |
| 2232 VisitCondition(expr, |
| 2233 true_graph->entry_block(), |
| 2234 false_graph->entry_block(), |
| 2235 false, |
| 2236 false); |
| 2237 } |
| 2238 |
| 2239 |
| 2240 void HGraphBuilder::VisitForControl(Expression* expr, |
| 2241 HBasicBlock* true_block, |
| 2242 HBasicBlock* false_block, |
| 2243 bool invert_true, |
| 2244 bool invert_false) { |
| 2245 TestContext for_test(this, true_block, false_block, |
| 2246 invert_true, invert_false); |
| 2247 BinaryOperation* binary_op = expr->AsBinaryOperation(); |
| 2248 UnaryOperation* unary_op = expr->AsUnaryOperation(); |
| 2249 |
| 2250 if (unary_op != NULL && unary_op->op() == Token::NOT) { |
| 2251 VisitForControl(unary_op->expression(), |
| 2252 false_block, |
| 2253 true_block, |
| 2254 !invert_false, |
| 2255 !invert_true); |
| 2256 } else if (binary_op != NULL && binary_op->op() == Token::AND) { |
| 2257 // Translate left subexpression. |
| 2258 HBasicBlock* eval_right = graph()->CreateBasicBlock(); |
| 2259 VisitForControl(binary_op->left(), |
| 2260 eval_right, |
| 2261 false_block, |
| 2262 false, |
| 2263 invert_false); |
| 2264 if (HasStackOverflow()) return; |
| 2265 eval_right->SetJoinId(binary_op->left()->id()); |
| 2266 |
| 2267 // Translate right subexpression. |
| 2268 eval_right->last_environment()->Pop(); |
| 2269 subgraph()->set_exit_block(eval_right); |
| 2270 VisitForControl(binary_op->right(), |
| 2271 true_block, |
| 2272 false_block, |
| 2273 invert_true, |
| 2274 invert_false); |
| 2275 } else if (binary_op != NULL && binary_op->op() == Token::OR) { |
| 2276 // Translate left subexpression. |
| 2277 HBasicBlock* eval_right = graph()->CreateBasicBlock(); |
| 2278 VisitForControl(binary_op->left(), |
| 2279 true_block, |
| 2280 eval_right, |
| 2281 invert_true, |
| 2282 false); |
| 2283 if (HasStackOverflow()) return; |
| 2284 eval_right->SetJoinId(binary_op->left()->id()); |
| 2285 |
| 2286 // Translate right subexpression |
| 2287 eval_right->last_environment()->Pop(); |
| 2288 subgraph()->set_exit_block(eval_right); |
| 2289 VisitForControl(binary_op->right(), |
| 2290 true_block, |
| 2291 false_block, |
| 2292 invert_true, |
| 2293 invert_false); |
| 2294 } else { |
| 2295 #ifdef DEBUG |
| 2296 int original_length = environment()->values()->length(); |
| 2297 #endif |
| 2298 // TODO(kmillikin): Refactor to avoid. This code is duplicated from |
| 2299 // VisitForValue, except without pushing a value context on the |
| 2300 // expression context stack. |
| 2301 Visit(expr); |
| 2302 if (HasStackOverflow() || !subgraph()->HasExit()) return; |
| 2303 HValue* last = subgraph()->exit_block()->GetLastInstruction(); |
| 2304 if (last != NULL && last->HasSideEffects()) { |
| 2305 AddSimulate(expr->id()); |
| 2306 } |
| 2307 ASSERT(environment()->values()->length() == original_length + 1); |
| 2308 HValue* value = Pop(); |
| 2309 HBasicBlock* materialize_true = graph()->CreateBasicBlock(); |
| 2310 HBasicBlock* materialize_false = graph()->CreateBasicBlock(); |
| 2311 CurrentBlock()->Finish(new HBranch(materialize_true, |
| 2312 materialize_false, |
| 2313 value)); |
| 2314 HValue* true_value = invert_true |
| 2315 ? graph()->GetConstantFalse() |
| 2316 : graph()->GetConstantTrue(); |
| 2317 materialize_true->set_inverted(invert_true); |
| 2318 true_block->set_deopt_predecessor(materialize_true); |
| 2319 |
| 2320 if (true_block->IsInlineReturnTarget()) { |
| 2321 materialize_true->AddLeaveInlined(true_value, true_block); |
| 2322 } else { |
| 2323 materialize_true->last_environment()->Push(true_value); |
| 2324 materialize_true->Goto(true_block); |
| 2325 } |
| 2326 HValue* false_value = invert_false |
| 2327 ? graph()->GetConstantTrue() |
| 2328 : graph()->GetConstantFalse(); |
| 2329 materialize_false->set_inverted(invert_false); |
| 2330 false_block->set_deopt_predecessor(materialize_false); |
| 2331 |
| 2332 if (false_block->IsInlineReturnTarget()) { |
| 2333 materialize_false->AddLeaveInlined(false_value, false_block); |
| 2334 } else { |
| 2335 materialize_false->last_environment()->Push(false_value); |
| 2336 materialize_false->Goto(false_block); |
| 2337 } |
| 2338 subgraph()->set_exit_block(NULL); |
| 2339 } |
| 2340 } |
| 2341 |
| 2342 |
| 2343 void HGraphBuilder::AddToSubgraph(HSubgraph* graph, |
| 2344 ZoneList<Statement*>* stmts) { |
| 2345 SubgraphScope scope(this, graph); |
| 2346 VisitStatements(stmts); |
| 2347 } |
| 2348 |
| 2349 |
| 2350 HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { |
| 2351 ASSERT(current_subgraph_->HasExit()); |
| 2352 current_subgraph_->exit_block()->AddInstruction(instr); |
| 2353 return instr; |
| 2354 } |
| 2355 |
| 2356 |
| 2357 void HGraphBuilder::AddSimulate(int id) { |
| 2358 ASSERT(current_subgraph_->HasExit()); |
| 2359 current_subgraph_->exit_block()->AddSimulate(id); |
| 2360 } |
| 2361 |
| 2362 |
| 2363 void HGraphBuilder::AddPhi(HPhi* instr) { |
| 2364 ASSERT(current_subgraph_->HasExit()); |
| 2365 current_subgraph_->exit_block()->AddPhi(instr); |
| 2366 } |
| 2367 |
| 2368 |
| 2369 void HGraphBuilder::PushAndAdd(HInstruction* instr) { |
| 2370 Push(instr); |
| 2371 AddInstruction(instr); |
| 2372 } |
| 2373 |
| 2374 |
| 2375 void HGraphBuilder::PushAndAdd(HInstruction* instr, int position) { |
| 2376 instr->set_position(position); |
| 2377 PushAndAdd(instr); |
| 2378 } |
| 2379 |
| 2380 |
| 2381 void HGraphBuilder::PushArgumentsForStubCall(int argument_count) { |
| 2382 const int kMaxStubArguments = 4; |
| 2383 ASSERT_GE(kMaxStubArguments, argument_count); |
| 2384 // Push the arguments on the stack. |
| 2385 HValue* arguments[kMaxStubArguments]; |
| 2386 for (int i = argument_count - 1; i >= 0; i--) { |
| 2387 arguments[i] = Pop(); |
| 2388 } |
| 2389 for (int i = 0; i < argument_count; i++) { |
| 2390 AddInstruction(new HPushArgument(arguments[i])); |
| 2391 } |
| 2392 } |
| 2393 |
| 2394 |
| 2395 void HGraphBuilder::ProcessCall(HCall* call, int source_position) { |
| 2396 for (int i = call->argument_count() - 1; i >= 0; --i) { |
| 2397 HValue* value = Pop(); |
| 2398 HPushArgument* push = new HPushArgument(value); |
| 2399 call->SetArgumentAt(i, push); |
| 2400 } |
| 2401 |
| 2402 for (int i = 0; i < call->argument_count(); ++i) { |
| 2403 AddInstruction(call->PushArgumentAt(i)); |
| 2404 } |
| 2405 |
| 2406 PushAndAdd(call, source_position); |
| 2407 } |
| 2408 |
| 2409 |
| 2410 void HGraphBuilder::SetupScope(Scope* scope) { |
| 2411 // We don't yet handle the function name for named function expressions. |
| 2412 if (scope->function() != NULL) BAILOUT("named function expression"); |
| 2413 |
| 2414 // We can't handle heap-allocated locals. |
| 2415 if (scope->num_heap_slots() > 0) BAILOUT("heap allocated locals"); |
| 2416 |
| 2417 HConstant* undefined_constant = |
| 2418 new HConstant(FACTORY->undefined_value(), Representation::Tagged()); |
| 2419 AddInstruction(undefined_constant); |
| 2420 graph_->set_undefined_constant(undefined_constant); |
| 2421 |
| 2422 // Set the initial values of parameters including "this". "This" has |
| 2423 // parameter index 0. |
| 2424 int count = scope->num_parameters() + 1; |
| 2425 for (int i = 0; i < count; ++i) { |
| 2426 HInstruction* parameter = AddInstruction(new HParameter(i)); |
| 2427 environment()->Bind(i, parameter); |
| 2428 } |
| 2429 |
| 2430 // Set the initial values of stack-allocated locals. |
| 2431 for (int i = count; i < environment()->values()->length(); ++i) { |
| 2432 environment()->Bind(i, undefined_constant); |
| 2433 } |
| 2434 |
| 2435 // Handle the arguments and arguments shadow variables specially (they do |
| 2436 // not have declarations). |
| 2437 if (scope->arguments() != NULL) { |
| 2438 HArgumentsObject* object = new HArgumentsObject; |
| 2439 AddInstruction(object); |
| 2440 graph()->SetArgumentsObject(object); |
| 2441 environment()->Bind(scope->arguments(), object); |
| 2442 environment()->Bind(scope->arguments_shadow(), object); |
| 2443 } |
| 2444 } |
| 2445 |
| 2446 |
| 2447 void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) { |
| 2448 for (int i = 0; i < statements->length(); i++) { |
| 2449 Visit(statements->at(i)); |
| 2450 if (HasStackOverflow() || !current_subgraph_->HasExit()) break; |
| 2451 } |
| 2452 } |
| 2453 |
| 2454 |
| 2455 HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) { |
| 2456 HBasicBlock* b = graph()->CreateBasicBlock(); |
| 2457 b->SetInitialEnvironment(env); |
| 2458 return b; |
| 2459 } |
| 2460 |
| 2461 |
| 2462 HSubgraph* HGraphBuilder::CreateInlinedSubgraph(HEnvironment* outer, |
| 2463 Handle<JSFunction> target, |
| 2464 FunctionLiteral* function) { |
| 2465 HConstant* undefined = graph()->GetConstantUndefined(); |
| 2466 HEnvironment* inner = |
| 2467 outer->CopyForInlining(target, function, true, undefined); |
| 2468 HSubgraph* subgraph = new HSubgraph(graph()); |
| 2469 subgraph->Initialize(CreateBasicBlock(inner)); |
| 2470 return subgraph; |
| 2471 } |
| 2472 |
| 2473 |
| 2474 HSubgraph* HGraphBuilder::CreateGotoSubgraph(HEnvironment* env) { |
| 2475 HSubgraph* subgraph = new HSubgraph(graph()); |
| 2476 HEnvironment* new_env = env->CopyWithoutHistory(); |
| 2477 subgraph->Initialize(CreateBasicBlock(new_env)); |
| 2478 return subgraph; |
| 2479 } |
| 2480 |
| 2481 |
| 2482 HSubgraph* HGraphBuilder::CreateEmptySubgraph() { |
| 2483 HSubgraph* subgraph = new HSubgraph(graph()); |
| 2484 subgraph->Initialize(graph()->CreateBasicBlock()); |
| 2485 return subgraph; |
| 2486 } |
| 2487 |
| 2488 |
| 2489 HSubgraph* HGraphBuilder::CreateBranchSubgraph(HEnvironment* env) { |
| 2490 HSubgraph* subgraph = new HSubgraph(graph()); |
| 2491 HEnvironment* new_env = env->Copy(); |
| 2492 subgraph->Initialize(CreateBasicBlock(new_env)); |
| 2493 return subgraph; |
| 2494 } |
| 2495 |
| 2496 |
| 2497 HSubgraph* HGraphBuilder::CreateLoopHeaderSubgraph(HEnvironment* env) { |
| 2498 HSubgraph* subgraph = new HSubgraph(graph()); |
| 2499 HBasicBlock* block = graph()->CreateBasicBlock(); |
| 2500 HEnvironment* new_env = env->CopyAsLoopHeader(block); |
| 2501 block->SetInitialEnvironment(new_env); |
| 2502 subgraph->Initialize(block); |
| 2503 subgraph->entry_block()->AttachLoopInformation(); |
| 2504 return subgraph; |
| 2505 } |
| 2506 |
| 2507 |
| 2508 void HGraphBuilder::VisitBlock(Block* stmt) { |
| 2509 if (stmt->labels() != NULL) { |
| 2510 HSubgraph* block_graph = CreateGotoSubgraph(environment()); |
| 2511 ADD_TO_SUBGRAPH(block_graph, stmt->statements()); |
| 2512 current_subgraph_->Append(block_graph, stmt); |
| 2513 } else { |
| 2514 VisitStatements(stmt->statements()); |
| 2515 } |
| 2516 } |
| 2517 |
| 2518 |
| 2519 void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
| 2520 VisitForEffect(stmt->expression()); |
| 2521 } |
| 2522 |
| 2523 |
| 2524 void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
| 2525 } |
| 2526 |
| 2527 |
| 2528 void HGraphBuilder::VisitIfStatement(IfStatement* stmt) { |
| 2529 if (stmt->condition()->ToBooleanIsTrue()) { |
| 2530 Visit(stmt->then_statement()); |
| 2531 } else if (stmt->condition()->ToBooleanIsFalse()) { |
| 2532 Visit(stmt->else_statement()); |
| 2533 } else { |
| 2534 HSubgraph* then_graph = CreateEmptySubgraph(); |
| 2535 HSubgraph* else_graph = CreateEmptySubgraph(); |
| 2536 VisitCondition(stmt->condition(), |
| 2537 then_graph->entry_block(), |
| 2538 else_graph->entry_block(), |
| 2539 false, false); |
| 2540 if (HasStackOverflow()) return; |
| 2541 ADD_TO_SUBGRAPH(then_graph, stmt->then_statement()); |
| 2542 ADD_TO_SUBGRAPH(else_graph, stmt->else_statement()); |
| 2543 current_subgraph_->AppendJoin(then_graph, else_graph, stmt); |
| 2544 } |
| 2545 } |
| 2546 |
| 2547 |
| 2548 void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
| 2549 current_subgraph_->FinishBreakContinue(stmt->target(), true); |
| 2550 } |
| 2551 |
| 2552 |
| 2553 void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) { |
| 2554 current_subgraph_->FinishBreakContinue(stmt->target(), false); |
| 2555 } |
| 2556 |
| 2557 |
| 2558 void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
| 2559 AstContext* context = call_context(); |
| 2560 if (context == NULL) { |
| 2561 // Not an inlined return, so an actual one. |
| 2562 VISIT_FOR_VALUE(stmt->expression()); |
| 2563 HValue* result = environment()->Pop(); |
| 2564 subgraph()->FinishExit(new HReturn(result)); |
| 2565 } else { |
| 2566 // Return from an inlined function, visit the subexpression in the |
| 2567 // expression context of the call. |
| 2568 if (context->IsTest()) { |
| 2569 TestContext* test = TestContext::cast(context); |
| 2570 VisitForControl(stmt->expression(), |
| 2571 test->if_true(), |
| 2572 test->if_false(), |
| 2573 false, |
| 2574 false); |
| 2575 } else { |
| 2576 HValue* return_value = NULL; |
| 2577 if (context->IsEffect()) { |
| 2578 VISIT_FOR_EFFECT(stmt->expression()); |
| 2579 return_value = graph()->GetConstantUndefined(); |
| 2580 } else { |
| 2581 ASSERT(context->IsValue()); |
| 2582 VISIT_FOR_VALUE(stmt->expression()); |
| 2583 return_value = environment()->Pop(); |
| 2584 } |
| 2585 subgraph()->exit_block()->AddLeaveInlined(return_value, |
| 2586 function_return_); |
| 2587 subgraph()->set_exit_block(NULL); |
| 2588 } |
| 2589 } |
| 2590 } |
| 2591 |
| 2592 |
| 2593 void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
| 2594 BAILOUT("WithEnterStatement"); |
| 2595 } |
| 2596 |
| 2597 |
| 2598 void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
| 2599 BAILOUT("WithExitStatement"); |
| 2600 } |
| 2601 |
| 2602 |
| 2603 HCompare* HGraphBuilder::BuildSwitchCompare(HSubgraph* subgraph, |
| 2604 HValue* switch_value, |
| 2605 CaseClause* clause) { |
| 2606 AddToSubgraph(subgraph, clause->label()); |
| 2607 if (HasStackOverflow()) return NULL; |
| 2608 HValue* clause_value = subgraph->environment()->Pop(); |
| 2609 HCompare* compare = new HCompare(switch_value, |
| 2610 clause_value, |
| 2611 Token::EQ_STRICT); |
| 2612 compare->SetInputRepresentation(Representation::Integer32()); |
| 2613 subgraph->exit_block()->AddInstruction(compare); |
| 2614 return compare; |
| 2615 } |
| 2616 |
| 2617 |
| 2618 void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
| 2619 VISIT_FOR_VALUE(stmt->tag()); |
| 2620 HValue* switch_value = Pop(); |
| 2621 |
| 2622 ZoneList<CaseClause*>* clauses = stmt->cases(); |
| 2623 int num_clauses = clauses->length(); |
| 2624 if (num_clauses == 0) return; |
| 2625 if (num_clauses > 128) BAILOUT("SwitchStatement: too many clauses"); |
| 2626 |
| 2627 for (int i = 0; i < num_clauses; i++) { |
| 2628 CaseClause* clause = clauses->at(i); |
| 2629 if (clause->is_default()) continue; |
| 2630 clause->RecordTypeFeedback(oracle()); |
| 2631 if (!clause->IsSmiCompare()) BAILOUT("SwitchStatement: non-smi compare"); |
| 2632 if (!clause->label()->IsSmiLiteral()) { |
| 2633 BAILOUT("SwitchStatement: non-literal switch label"); |
| 2634 } |
| 2635 } |
| 2636 |
| 2637 // The single exit block of the whole switch statement. |
| 2638 HBasicBlock* single_exit_block = graph_->CreateBasicBlock(); |
| 2639 |
| 2640 // Build a series of empty subgraphs for the comparisons. |
| 2641 // The default clause does not have a comparison subgraph. |
| 2642 ZoneList<HSubgraph*> compare_graphs(num_clauses); |
| 2643 for (int i = 0; i < num_clauses; i++) { |
| 2644 HSubgraph* subgraph = !clauses->at(i)->is_default() |
| 2645 ? CreateEmptySubgraph() |
| 2646 : NULL; |
| 2647 compare_graphs.Add(subgraph); |
| 2648 } |
| 2649 |
| 2650 HSubgraph* prev_graph = current_subgraph_; |
| 2651 HCompare* prev_compare_inst = NULL; |
| 2652 for (int i = 0; i < num_clauses; i++) { |
| 2653 CaseClause* clause = clauses->at(i); |
| 2654 if (clause->is_default()) continue; |
| 2655 |
| 2656 // Finish the previous graph by connecting it to the current. |
| 2657 HSubgraph* subgraph = compare_graphs.at(i); |
| 2658 if (prev_compare_inst == NULL) { |
| 2659 ASSERT(prev_graph == current_subgraph_); |
| 2660 prev_graph->exit_block()->Finish(new HGoto(subgraph->entry_block())); |
| 2661 } else { |
| 2662 HBasicBlock* empty = graph()->CreateBasicBlock(); |
| 2663 prev_graph->exit_block()->Finish(new HBranch(empty, |
| 2664 subgraph->entry_block(), |
| 2665 prev_compare_inst)); |
| 2666 } |
| 2667 |
| 2668 // Build instructions for current subgraph. |
| 2669 prev_compare_inst = BuildSwitchCompare(subgraph, switch_value, clause); |
| 2670 if (HasStackOverflow()) return; |
| 2671 |
| 2672 prev_graph = subgraph; |
| 2673 } |
| 2674 |
| 2675 // Finish last comparison if there was at least one comparison. |
| 2676 // last_false_block is the (empty) false-block of the last comparison. If |
| 2677 // there are no comparisons at all (a single default clause), it is just |
| 2678 // the last block of the current subgraph. |
| 2679 HBasicBlock* last_false_block = current_subgraph_->exit_block(); |
| 2680 if (prev_graph != current_subgraph_) { |
| 2681 last_false_block = graph()->CreateBasicBlock(); |
| 2682 HBasicBlock* empty = graph()->CreateBasicBlock(); |
| 2683 prev_graph->exit_block()->Finish(new HBranch(empty, |
| 2684 last_false_block, |
| 2685 prev_compare_inst)); |
| 2686 } |
| 2687 |
| 2688 // Build statement blocks, connect them to their comparison block and |
| 2689 // to the previous statement block, if there is a fall-through. |
| 2690 HSubgraph* previous_subgraph = NULL; |
| 2691 for (int i = 0; i < num_clauses; i++) { |
| 2692 CaseClause* clause = clauses->at(i); |
| 2693 HSubgraph* subgraph = CreateEmptySubgraph(); |
| 2694 |
| 2695 if (clause->is_default()) { |
| 2696 // Default clause: Connect it to the last false block. |
| 2697 last_false_block->Finish(new HGoto(subgraph->entry_block())); |
| 2698 } else { |
| 2699 // Connect with the corresponding comparison. |
| 2700 HBasicBlock* empty = |
| 2701 compare_graphs.at(i)->exit_block()->end()->FirstSuccessor(); |
| 2702 empty->Finish(new HGoto(subgraph->entry_block())); |
| 2703 } |
| 2704 |
| 2705 // Check for fall-through from previous statement block. |
| 2706 if (previous_subgraph != NULL && previous_subgraph->HasExit()) { |
| 2707 previous_subgraph->exit_block()-> |
| 2708 Finish(new HGoto(subgraph->entry_block())); |
| 2709 } |
| 2710 |
| 2711 ADD_TO_SUBGRAPH(subgraph, clause->statements()); |
| 2712 HBasicBlock* break_block = subgraph->BundleBreak(stmt); |
| 2713 if (break_block != NULL) { |
| 2714 break_block->Finish(new HGoto(single_exit_block)); |
| 2715 } |
| 2716 |
| 2717 previous_subgraph = subgraph; |
| 2718 } |
| 2719 |
| 2720 // If the last statement block has a fall-through, connect it to the |
| 2721 // single exit block. |
| 2722 if (previous_subgraph->HasExit()) { |
| 2723 previous_subgraph->exit_block()->Finish(new HGoto(single_exit_block)); |
| 2724 } |
| 2725 |
| 2726 // If there is no default clause finish the last comparison's false target. |
| 2727 if (!last_false_block->IsFinished()) { |
| 2728 last_false_block->Finish(new HGoto(single_exit_block)); |
| 2729 } |
| 2730 |
| 2731 if (single_exit_block->HasPredecessor()) { |
| 2732 current_subgraph_->set_exit_block(single_exit_block); |
| 2733 } else { |
| 2734 current_subgraph_->set_exit_block(NULL); |
| 2735 } |
| 2736 } |
| 2737 |
| 2738 bool HGraph::HasOsrEntryAt(IterationStatement* statement) { |
| 2739 return statement->OsrEntryId() == info()->osr_ast_id(); |
| 2740 } |
| 2741 |
| 2742 |
| 2743 void HSubgraph::PreProcessOsrEntry(IterationStatement* statement) { |
| 2744 if (!graph()->HasOsrEntryAt(statement)) return; |
| 2745 |
| 2746 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock(); |
| 2747 HBasicBlock* osr_entry = graph()->CreateBasicBlock(); |
| 2748 HValue* true_value = graph()->GetConstantTrue(); |
| 2749 HBranch* branch = new HBranch(non_osr_entry, osr_entry, true_value); |
| 2750 exit_block()->Finish(branch); |
| 2751 |
| 2752 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock(); |
| 2753 non_osr_entry->Goto(loop_predecessor); |
| 2754 |
| 2755 int osr_entry_id = statement->OsrEntryId(); |
| 2756 // We want the correct environment at the OsrEntry instruction. Build |
| 2757 // it explicitly. The expression stack should be empty. |
| 2758 int count = osr_entry->last_environment()->total_count(); |
| 2759 ASSERT(count == (osr_entry->last_environment()->parameter_count() + |
| 2760 osr_entry->last_environment()->local_count())); |
| 2761 for (int i = 0; i < count; ++i) { |
| 2762 HUnknownOSRValue* unknown = new HUnknownOSRValue; |
| 2763 osr_entry->AddInstruction(unknown); |
| 2764 osr_entry->last_environment()->Bind(i, unknown); |
| 2765 } |
| 2766 |
| 2767 osr_entry->AddSimulate(osr_entry_id); |
| 2768 osr_entry->AddInstruction(new HOsrEntry(osr_entry_id)); |
| 2769 osr_entry->Goto(loop_predecessor); |
| 2770 loop_predecessor->SetJoinId(statement->EntryId()); |
| 2771 set_exit_block(loop_predecessor); |
| 2772 } |
| 2773 |
| 2774 |
| 2775 void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { |
| 2776 ASSERT(subgraph()->HasExit()); |
| 2777 subgraph()->PreProcessOsrEntry(stmt); |
| 2778 |
| 2779 HSubgraph* body_graph = CreateLoopHeaderSubgraph(environment()); |
| 2780 ADD_TO_SUBGRAPH(body_graph, stmt->body()); |
| 2781 body_graph->ResolveContinue(stmt); |
| 2782 |
| 2783 if (!body_graph->HasExit() || stmt->cond()->ToBooleanIsTrue()) { |
| 2784 current_subgraph_->AppendEndless(body_graph, stmt); |
| 2785 } else { |
| 2786 HSubgraph* go_back = CreateEmptySubgraph(); |
| 2787 HSubgraph* exit = CreateEmptySubgraph(); |
| 2788 AddConditionToSubgraph(body_graph, stmt->cond(), go_back, exit); |
| 2789 if (HasStackOverflow()) return; |
| 2790 current_subgraph_->AppendDoWhile(body_graph, stmt, go_back, exit); |
| 2791 } |
| 2792 } |
| 2793 |
| 2794 |
| 2795 bool HGraphBuilder::ShouldPeel(HSubgraph* cond, HSubgraph* body) { |
| 2796 return FLAG_use_peeling; |
| 2797 } |
| 2798 |
| 2799 |
| 2800 void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { |
| 2801 ASSERT(subgraph()->HasExit()); |
| 2802 subgraph()->PreProcessOsrEntry(stmt); |
| 2803 |
| 2804 HSubgraph* cond_graph = NULL; |
| 2805 HSubgraph* body_graph = NULL; |
| 2806 HSubgraph* exit_graph = NULL; |
| 2807 |
| 2808 // If the condition is constant true, do not generate a condition subgraph. |
| 2809 if (stmt->cond()->ToBooleanIsTrue()) { |
| 2810 body_graph = CreateLoopHeaderSubgraph(environment()); |
| 2811 ADD_TO_SUBGRAPH(body_graph, stmt->body()); |
| 2812 } else { |
| 2813 cond_graph = CreateLoopHeaderSubgraph(environment()); |
| 2814 body_graph = CreateEmptySubgraph(); |
| 2815 exit_graph = CreateEmptySubgraph(); |
| 2816 AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph); |
| 2817 if (HasStackOverflow()) return; |
| 2818 ADD_TO_SUBGRAPH(body_graph, stmt->body()); |
| 2819 } |
| 2820 |
| 2821 body_graph->ResolveContinue(stmt); |
| 2822 |
| 2823 if (cond_graph != NULL) { |
| 2824 AppendPeeledWhile(stmt, cond_graph, body_graph, exit_graph); |
| 2825 } else { |
| 2826 // TODO(fschneider): Implement peeling for endless loops as well. |
| 2827 current_subgraph_->AppendEndless(body_graph, stmt); |
| 2828 } |
| 2829 } |
| 2830 |
| 2831 |
| 2832 void HGraphBuilder::AppendPeeledWhile(IterationStatement* stmt, |
| 2833 HSubgraph* cond_graph, |
| 2834 HSubgraph* body_graph, |
| 2835 HSubgraph* exit_graph) { |
| 2836 HSubgraph* loop = NULL; |
| 2837 if (body_graph->HasExit() && stmt != peeled_statement_ && |
| 2838 ShouldPeel(cond_graph, body_graph)) { |
| 2839 // Save the last peeled iteration statement to prevent infinite recursion. |
| 2840 IterationStatement* outer_peeled_statement = peeled_statement_; |
| 2841 peeled_statement_ = stmt; |
| 2842 loop = CreateGotoSubgraph(body_graph->environment()); |
| 2843 ADD_TO_SUBGRAPH(loop, stmt); |
| 2844 peeled_statement_ = outer_peeled_statement; |
| 2845 } |
| 2846 current_subgraph_->AppendWhile(cond_graph, body_graph, stmt, loop, |
| 2847 exit_graph); |
| 2848 } |
| 2849 |
| 2850 |
| 2851 void HGraphBuilder::VisitForStatement(ForStatement* stmt) { |
| 2852 // Only visit the init statement in the peeled part of the loop. |
| 2853 if (stmt->init() != NULL && peeled_statement_ != stmt) { |
| 2854 Visit(stmt->init()); |
| 2855 CHECK_BAILOUT; |
| 2856 } |
| 2857 ASSERT(subgraph()->HasExit()); |
| 2858 subgraph()->PreProcessOsrEntry(stmt); |
| 2859 |
| 2860 HSubgraph* cond_graph = NULL; |
| 2861 HSubgraph* body_graph = NULL; |
| 2862 HSubgraph* exit_graph = NULL; |
| 2863 if (stmt->cond() != NULL) { |
| 2864 cond_graph = CreateLoopHeaderSubgraph(environment()); |
| 2865 body_graph = CreateEmptySubgraph(); |
| 2866 exit_graph = CreateEmptySubgraph(); |
| 2867 AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph); |
| 2868 if (HasStackOverflow()) return; |
| 2869 ADD_TO_SUBGRAPH(body_graph, stmt->body()); |
| 2870 } else { |
| 2871 body_graph = CreateLoopHeaderSubgraph(environment()); |
| 2872 ADD_TO_SUBGRAPH(body_graph, stmt->body()); |
| 2873 } |
| 2874 |
| 2875 HSubgraph* next_graph = NULL; |
| 2876 body_graph->ResolveContinue(stmt); |
| 2877 |
| 2878 if (stmt->next() != NULL && body_graph->HasExit()) { |
| 2879 next_graph = CreateGotoSubgraph(body_graph->environment()); |
| 2880 ADD_TO_SUBGRAPH(next_graph, stmt->next()); |
| 2881 body_graph->Append(next_graph, NULL); |
| 2882 next_graph->entry_block()->SetJoinId(stmt->ContinueId()); |
| 2883 } |
| 2884 |
| 2885 if (cond_graph != NULL) { |
| 2886 AppendPeeledWhile(stmt, cond_graph, body_graph, exit_graph); |
| 2887 } else { |
| 2888 current_subgraph_->AppendEndless(body_graph, stmt); |
| 2889 } |
| 2890 } |
| 2891 |
| 2892 |
| 2893 void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) { |
| 2894 BAILOUT("ForInStatement"); |
| 2895 } |
| 2896 |
| 2897 |
| 2898 void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) { |
| 2899 BAILOUT("TryCatchStatement"); |
| 2900 } |
| 2901 |
| 2902 |
| 2903 void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
| 2904 BAILOUT("TryFinallyStatement"); |
| 2905 } |
| 2906 |
| 2907 |
| 2908 void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
| 2909 BAILOUT("DebuggerStatement"); |
| 2910 } |
| 2911 |
| 2912 |
| 2913 void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
| 2914 Handle<SharedFunctionInfo> shared_info = |
| 2915 Compiler::BuildFunctionInfo(expr, graph_->info()->script()); |
| 2916 CHECK_BAILOUT; |
| 2917 PushAndAdd(new HFunctionLiteral(shared_info, expr->pretenure())); |
| 2918 } |
| 2919 |
| 2920 |
| 2921 void HGraphBuilder::VisitSharedFunctionInfoLiteral( |
| 2922 SharedFunctionInfoLiteral* expr) { |
| 2923 BAILOUT("SharedFunctionInfoLiteral"); |
| 2924 } |
| 2925 |
| 2926 |
| 2927 void HGraphBuilder::VisitConditional(Conditional* expr) { |
| 2928 HSubgraph* then_graph = CreateEmptySubgraph(); |
| 2929 HSubgraph* else_graph = CreateEmptySubgraph(); |
| 2930 VisitCondition(expr->condition(), |
| 2931 then_graph->entry_block(), |
| 2932 else_graph->entry_block(), |
| 2933 false, false); |
| 2934 if (HasStackOverflow()) return; |
| 2935 ADD_TO_SUBGRAPH(then_graph, expr->then_expression()); |
| 2936 ADD_TO_SUBGRAPH(else_graph, expr->else_expression()); |
| 2937 current_subgraph_->AppendJoin(then_graph, else_graph, expr); |
| 2938 } |
| 2939 |
| 2940 |
| 2941 void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr, |
| 2942 LookupResult* lookup, |
| 2943 bool is_store) { |
| 2944 if (expr->is_this()) { |
| 2945 BAILOUT("global this reference"); |
| 2946 } |
| 2947 if (!graph()->info()->has_global_object()) { |
| 2948 BAILOUT("no global object to optimize VariableProxy"); |
| 2949 } |
| 2950 Handle<GlobalObject> global(graph()->info()->global_object()); |
| 2951 global->Lookup(*expr->name(), lookup); |
| 2952 if (!lookup->IsProperty()) { |
| 2953 BAILOUT("global variable cell not yet introduced"); |
| 2954 } |
| 2955 if (lookup->type() != NORMAL) { |
| 2956 BAILOUT("global variable has accessors"); |
| 2957 } |
| 2958 if (is_store && lookup->IsReadOnly()) { |
| 2959 BAILOUT("read-only global variable"); |
| 2960 } |
| 2961 } |
| 2962 |
| 2963 |
| 2964 void HGraphBuilder::HandleGlobalVariableLoad(VariableProxy* expr) { |
| 2965 LookupResult lookup; |
| 2966 LookupGlobalPropertyCell(expr, &lookup, false); |
| 2967 CHECK_BAILOUT; |
| 2968 |
| 2969 Handle<GlobalObject> global(graph()->info()->global_object()); |
| 2970 // TODO(3039103): Handle global property load through an IC call when access |
| 2971 // checks are enabled. |
| 2972 if (global->IsAccessCheckNeeded()) { |
| 2973 BAILOUT("global object requires access check"); |
| 2974 } |
| 2975 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); |
| 2976 bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); |
| 2977 PushAndAdd(new HLoadGlobal(cell, check_hole)); |
| 2978 } |
| 2979 |
| 2980 |
| 2981 void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { |
| 2982 Variable* variable = expr->AsVariable(); |
| 2983 if (variable == NULL) { |
| 2984 BAILOUT("reference to rewritten variable"); |
| 2985 } else if (variable->IsStackAllocated()) { |
| 2986 if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) { |
| 2987 BAILOUT("unsupported context for arguments object"); |
| 2988 } |
| 2989 Push(environment()->Lookup(variable)); |
| 2990 } else if (variable->is_global()) { |
| 2991 HandleGlobalVariableLoad(expr); |
| 2992 } else { |
| 2993 BAILOUT("reference to non-stack-allocated/non-global variable"); |
| 2994 } |
| 2995 } |
| 2996 |
| 2997 |
| 2998 void HGraphBuilder::VisitLiteral(Literal* expr) { |
| 2999 PushAndAdd(new HConstant(expr->handle(), Representation::Tagged())); |
| 3000 } |
| 3001 |
| 3002 |
| 3003 void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
| 3004 PushAndAdd(new HRegExpLiteral(expr->pattern(), |
| 3005 expr->flags(), |
| 3006 expr->literal_index())); |
| 3007 } |
| 3008 |
| 3009 |
| 3010 void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
| 3011 HObjectLiteral* literal = (new HObjectLiteral(expr->constant_properties(), |
| 3012 expr->fast_elements(), |
| 3013 expr->literal_index(), |
| 3014 expr->depth())); |
| 3015 PushAndAdd(literal); |
| 3016 |
| 3017 expr->CalculateEmitStore(); |
| 3018 |
| 3019 for (int i = 0; i < expr->properties()->length(); i++) { |
| 3020 ObjectLiteral::Property* property = expr->properties()->at(i); |
| 3021 if (property->IsCompileTimeValue()) continue; |
| 3022 |
| 3023 Literal* key = property->key(); |
| 3024 Expression* value = property->value(); |
| 3025 |
| 3026 switch (property->kind()) { |
| 3027 case ObjectLiteral::Property::MATERIALIZED_LITERAL: |
| 3028 ASSERT(!CompileTimeValue::IsCompileTimeValue(value)); |
| 3029 // Fall through. |
| 3030 case ObjectLiteral::Property::COMPUTED: |
| 3031 if (key->handle()->IsSymbol()) { |
| 3032 if (property->emit_store()) { |
| 3033 VISIT_FOR_VALUE(value); |
| 3034 HValue* value = Pop(); |
| 3035 Handle<String> name = Handle<String>::cast(key->handle()); |
| 3036 AddInstruction(new HStoreNamedGeneric(literal, name, value)); |
| 3037 AddSimulate(key->id()); |
| 3038 } else { |
| 3039 VISIT_FOR_EFFECT(value); |
| 3040 } |
| 3041 break; |
| 3042 } |
| 3043 // Fall through. |
| 3044 case ObjectLiteral::Property::PROTOTYPE: |
| 3045 case ObjectLiteral::Property::SETTER: |
| 3046 case ObjectLiteral::Property::GETTER: |
| 3047 BAILOUT("Object literal with complex property"); |
| 3048 default: UNREACHABLE(); |
| 3049 } |
| 3050 } |
| 3051 } |
| 3052 |
| 3053 |
| 3054 void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
| 3055 ZoneList<Expression*>* subexprs = expr->values(); |
| 3056 int length = subexprs->length(); |
| 3057 |
| 3058 HArrayLiteral* literal = new HArrayLiteral(expr->constant_elements(), |
| 3059 length, |
| 3060 expr->literal_index(), |
| 3061 expr->depth()); |
| 3062 PushAndAdd(literal); |
| 3063 HValue* elements = AddInstruction(new HLoadElements(literal)); |
| 3064 |
| 3065 for (int i = 0; i < length; i++) { |
| 3066 Expression* subexpr = subexprs->at(i); |
| 3067 // If the subexpression is a literal or a simple materialized literal it |
| 3068 // is already set in the cloned array. |
| 3069 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; |
| 3070 |
| 3071 VISIT_FOR_VALUE(subexpr); |
| 3072 HValue* value = Pop(); |
| 3073 if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal"); |
| 3074 HValue* key = AddInstruction(new HConstant(Handle<Object>(Smi::FromInt(i)), |
| 3075 Representation::Integer32())); |
| 3076 AddInstruction(new HStoreKeyedFastElement(elements, key, value)); |
| 3077 AddSimulate(expr->GetIdForElement(i)); |
| 3078 } |
| 3079 } |
| 3080 |
| 3081 |
| 3082 void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
| 3083 BAILOUT("CatchExtensionObject"); |
| 3084 } |
| 3085 |
| 3086 |
| 3087 HBasicBlock* HGraphBuilder::BuildTypeSwitch(ZoneMapList* maps, |
| 3088 ZoneList<HSubgraph*>* subgraphs, |
| 3089 HValue* receiver, |
| 3090 int join_id) { |
| 3091 ASSERT(subgraphs->length() == (maps->length() + 1)); |
| 3092 |
| 3093 // Build map compare subgraphs for all but the first map. |
| 3094 ZoneList<HSubgraph*> map_compare_subgraphs(maps->length() - 1); |
| 3095 for (int i = maps->length() - 1; i > 0; --i) { |
| 3096 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3097 SubgraphScope scope(this, subgraph); |
| 3098 HSubgraph* else_subgraph = |
| 3099 (i == (maps->length() - 1)) |
| 3100 ? subgraphs->last() |
| 3101 : map_compare_subgraphs.last(); |
| 3102 current_subgraph_->exit_block()->Finish( |
| 3103 new HCompareMapAndBranch(receiver, |
| 3104 maps->at(i), |
| 3105 subgraphs->at(i)->entry_block(), |
| 3106 else_subgraph->entry_block())); |
| 3107 map_compare_subgraphs.Add(subgraph); |
| 3108 } |
| 3109 |
| 3110 // Generate first map check to end the current block. |
| 3111 AddInstruction(new HCheckNonSmi(receiver)); |
| 3112 HSubgraph* else_subgraph = |
| 3113 (maps->length() == 1) ? subgraphs->at(1) : map_compare_subgraphs.last(); |
| 3114 current_subgraph_->exit_block()->Finish( |
| 3115 new HCompareMapAndBranch(receiver, |
| 3116 Handle<Map>(maps->first()), |
| 3117 subgraphs->first()->entry_block(), |
| 3118 else_subgraph->entry_block())); |
| 3119 |
| 3120 // Join all the call subgraphs in a new basic block and make |
| 3121 // this basic block the current basic block. |
| 3122 HBasicBlock* join_block = graph_->CreateBasicBlock(); |
| 3123 for (int i = 0; i < subgraphs->length(); ++i) { |
| 3124 if (subgraphs->at(i)->HasExit()) { |
| 3125 subgraphs->at(i)->exit_block()->Goto(join_block); |
| 3126 } |
| 3127 } |
| 3128 |
| 3129 if (join_block->predecessors()->is_empty()) return NULL; |
| 3130 join_block->SetJoinId(join_id); |
| 3131 return join_block; |
| 3132 } |
| 3133 |
| 3134 |
| 3135 // Sets the lookup result and returns true if the store can be inlined. |
| 3136 static bool ComputeStoredField(Handle<Map> type, |
| 3137 Handle<String> name, |
| 3138 LookupResult* lookup) { |
| 3139 type->LookupInDescriptors(NULL, *name, lookup); |
| 3140 if (!lookup->IsPropertyOrTransition()) return false; |
| 3141 if (lookup->type() == FIELD) return true; |
| 3142 return (lookup->type() == MAP_TRANSITION) && |
| 3143 (type->unused_property_fields() > 0); |
| 3144 } |
| 3145 |
| 3146 |
| 3147 static int ComputeStoredFieldIndex(Handle<Map> type, |
| 3148 Handle<String> name, |
| 3149 LookupResult* lookup) { |
| 3150 ASSERT(lookup->type() == FIELD || lookup->type() == MAP_TRANSITION); |
| 3151 if (lookup->type() == FIELD) { |
| 3152 return lookup->GetLocalFieldIndexFromMap(*type); |
| 3153 } else { |
| 3154 Map* transition = lookup->GetTransitionMapFromMap(*type); |
| 3155 return transition->PropertyIndexFor(*name) - type->inobject_properties(); |
| 3156 } |
| 3157 } |
| 3158 |
| 3159 |
| 3160 HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object, |
| 3161 Handle<String> name, |
| 3162 HValue* value, |
| 3163 Handle<Map> type, |
| 3164 LookupResult* lookup, |
| 3165 bool smi_and_map_check) { |
| 3166 if (smi_and_map_check) { |
| 3167 AddInstruction(new HCheckNonSmi(object)); |
| 3168 AddInstruction(new HCheckMap(object, type)); |
| 3169 } |
| 3170 |
| 3171 int index = ComputeStoredFieldIndex(type, name, lookup); |
| 3172 bool is_in_object = index < 0; |
| 3173 int offset = index * kPointerSize; |
| 3174 if (index < 0) { |
| 3175 // Negative property indices are in-object properties, indexed |
| 3176 // from the end of the fixed part of the object. |
| 3177 offset += type->instance_size(); |
| 3178 } else { |
| 3179 offset += FixedArray::kHeaderSize; |
| 3180 } |
| 3181 HStoreNamedField* instr = |
| 3182 new HStoreNamedField(object, name, value, is_in_object, offset); |
| 3183 if (lookup->type() == MAP_TRANSITION) { |
| 3184 Handle<Map> transition(lookup->GetTransitionMapFromMap(*type)); |
| 3185 instr->set_transition(transition); |
| 3186 } |
| 3187 return instr; |
| 3188 } |
| 3189 |
| 3190 |
| 3191 HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object, |
| 3192 Handle<String> name, |
| 3193 HValue* value) { |
| 3194 return new HStoreNamedGeneric(object, name, value); |
| 3195 } |
| 3196 |
| 3197 |
| 3198 HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object, |
| 3199 HValue* value, |
| 3200 Expression* expr) { |
| 3201 Property* prop = (expr->AsProperty() != NULL) |
| 3202 ? expr->AsProperty() |
| 3203 : expr->AsAssignment()->target()->AsProperty(); |
| 3204 Literal* key = prop->key()->AsLiteral(); |
| 3205 Handle<String> name = Handle<String>::cast(key->handle()); |
| 3206 ASSERT(!name.is_null()); |
| 3207 |
| 3208 LookupResult lookup; |
| 3209 ZoneMapList* types = expr->GetReceiverTypes(); |
| 3210 bool is_monomorphic = expr->IsMonomorphic() && |
| 3211 ComputeStoredField(types->first(), name, &lookup); |
| 3212 |
| 3213 return is_monomorphic |
| 3214 ? BuildStoreNamedField(object, name, value, types->first(), &lookup, |
| 3215 true) // Needs smi and map check. |
| 3216 : BuildStoreNamedGeneric(object, name, value); |
| 3217 } |
| 3218 |
| 3219 |
| 3220 void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, |
| 3221 HValue* object, |
| 3222 HValue* value, |
| 3223 ZoneMapList* types, |
| 3224 Handle<String> name) { |
| 3225 int number_of_types = Min(types->length(), kMaxStorePolymorphism); |
| 3226 ZoneMapList maps(number_of_types); |
| 3227 ZoneList<HSubgraph*> subgraphs(number_of_types + 1); |
| 3228 bool needs_generic = (types->length() > kMaxStorePolymorphism); |
| 3229 |
| 3230 // Build subgraphs for each of the specific maps. |
| 3231 // |
| 3232 // TODO(ager): We should recognize when the prototype chains for |
| 3233 // different maps are identical. In that case we can avoid |
| 3234 // repeatedly generating the same prototype map checks. |
| 3235 for (int i = 0; i < number_of_types; ++i) { |
| 3236 Handle<Map> map = types->at(i); |
| 3237 LookupResult lookup; |
| 3238 if (ComputeStoredField(map, name, &lookup)) { |
| 3239 maps.Add(map); |
| 3240 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3241 SubgraphScope scope(this, subgraph); |
| 3242 HInstruction* instr = |
| 3243 BuildStoreNamedField(object, name, value, map, &lookup, false); |
| 3244 Push(value); |
| 3245 instr->set_position(expr->position()); |
| 3246 AddInstruction(instr); |
| 3247 subgraphs.Add(subgraph); |
| 3248 } else { |
| 3249 needs_generic = true; |
| 3250 } |
| 3251 } |
| 3252 |
| 3253 // If none of the properties were named fields we generate a |
| 3254 // generic store. |
| 3255 if (maps.length() == 0) { |
| 3256 HInstruction* instr = new HStoreNamedGeneric(object, name, value); |
| 3257 Push(value); |
| 3258 instr->set_position(expr->position()); |
| 3259 AddInstruction(instr); |
| 3260 return; |
| 3261 } |
| 3262 |
| 3263 // Build subgraph for generic store through IC. |
| 3264 { |
| 3265 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3266 SubgraphScope scope(this, subgraph); |
| 3267 if (!needs_generic && FLAG_deoptimize_uncommon_cases) { |
| 3268 subgraph->FinishExit(new HDeoptimize()); |
| 3269 } else { |
| 3270 HInstruction* instr = new HStoreNamedGeneric(object, name, value); |
| 3271 Push(value); |
| 3272 instr->set_position(expr->position()); |
| 3273 AddInstruction(instr); |
| 3274 } |
| 3275 subgraphs.Add(subgraph); |
| 3276 } |
| 3277 |
| 3278 HBasicBlock* new_exit_block = |
| 3279 BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); |
| 3280 current_subgraph_->set_exit_block(new_exit_block); |
| 3281 } |
| 3282 |
| 3283 |
| 3284 void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { |
| 3285 Property* prop = expr->target()->AsProperty(); |
| 3286 ASSERT(prop != NULL); |
| 3287 expr->RecordTypeFeedback(oracle()); |
| 3288 VISIT_FOR_VALUE(prop->obj()); |
| 3289 |
| 3290 HValue* value = NULL; |
| 3291 HInstruction* instr = NULL; |
| 3292 |
| 3293 if (prop->key()->IsPropertyName()) { |
| 3294 // Named store. |
| 3295 VISIT_FOR_VALUE(expr->value()); |
| 3296 value = Pop(); |
| 3297 HValue* object = Pop(); |
| 3298 |
| 3299 Literal* key = prop->key()->AsLiteral(); |
| 3300 Handle<String> name = Handle<String>::cast(key->handle()); |
| 3301 ASSERT(!name.is_null()); |
| 3302 |
| 3303 ZoneMapList* types = expr->GetReceiverTypes(); |
| 3304 LookupResult lookup; |
| 3305 |
| 3306 if (expr->IsMonomorphic()) { |
| 3307 instr = BuildStoreNamed(object, value, expr); |
| 3308 |
| 3309 } else if (types != NULL && types->length() > 1) { |
| 3310 HandlePolymorphicStoreNamedField(expr, object, value, types, name); |
| 3311 return; |
| 3312 |
| 3313 } else { |
| 3314 instr = new HStoreNamedGeneric(object, name, value); |
| 3315 } |
| 3316 |
| 3317 } else { |
| 3318 // Keyed store. |
| 3319 VISIT_FOR_VALUE(prop->key()); |
| 3320 VISIT_FOR_VALUE(expr->value()); |
| 3321 value = Pop(); |
| 3322 HValue* key = Pop(); |
| 3323 HValue* object = Pop(); |
| 3324 |
| 3325 bool is_fast_elements = expr->IsMonomorphic() && |
| 3326 expr->GetMonomorphicReceiverType()->has_fast_elements(); |
| 3327 |
| 3328 instr = is_fast_elements |
| 3329 ? BuildStoreKeyedFastElement(object, key, value, expr) |
| 3330 : BuildStoreKeyedGeneric(object, key, value); |
| 3331 } |
| 3332 |
| 3333 Push(value); |
| 3334 instr->set_position(expr->position()); |
| 3335 AddInstruction(instr); |
| 3336 } |
| 3337 |
| 3338 |
| 3339 void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy, |
| 3340 HValue* value, |
| 3341 int position) { |
| 3342 LookupResult lookup; |
| 3343 LookupGlobalPropertyCell(proxy, &lookup, true); |
| 3344 CHECK_BAILOUT; |
| 3345 |
| 3346 Handle<GlobalObject> global(graph()->info()->global_object()); |
| 3347 Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); |
| 3348 HInstruction* instr = new HStoreGlobal(value, cell); |
| 3349 instr->set_position(position); |
| 3350 AddInstruction(instr); |
| 3351 } |
| 3352 |
| 3353 |
| 3354 void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { |
| 3355 Expression* target = expr->target(); |
| 3356 VariableProxy* proxy = target->AsVariableProxy(); |
| 3357 Variable* var = proxy->AsVariable(); |
| 3358 Property* prop = target->AsProperty(); |
| 3359 ASSERT(var == NULL || prop == NULL); |
| 3360 |
| 3361 // We have a second position recorded in the FullCodeGenerator to have |
| 3362 // type feedback for the binary operation. |
| 3363 BinaryOperation* operation = expr->binary_operation(); |
| 3364 operation->RecordTypeFeedback(oracle()); |
| 3365 |
| 3366 if (var != NULL) { |
| 3367 if (!var->is_global() && !var->IsStackAllocated()) { |
| 3368 BAILOUT("non-stack/non-global in compound assignment"); |
| 3369 } |
| 3370 |
| 3371 VISIT_FOR_VALUE(operation); |
| 3372 |
| 3373 if (var->is_global()) { |
| 3374 HandleGlobalVariableAssignment(proxy, Top(), expr->position()); |
| 3375 } else { |
| 3376 Bind(var, Top()); |
| 3377 } |
| 3378 } else if (prop != NULL) { |
| 3379 prop->RecordTypeFeedback(oracle()); |
| 3380 |
| 3381 if (prop->key()->IsPropertyName()) { |
| 3382 // Named property. |
| 3383 VISIT_FOR_VALUE(prop->obj()); |
| 3384 HValue* obj = Top(); |
| 3385 |
| 3386 HInstruction* load = NULL; |
| 3387 if (prop->IsMonomorphic()) { |
| 3388 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); |
| 3389 Handle<Map> map = prop->GetReceiverTypes()->first(); |
| 3390 load = BuildLoadNamed(obj, prop, map, name); |
| 3391 } else { |
| 3392 load = BuildLoadNamedGeneric(obj, prop); |
| 3393 } |
| 3394 PushAndAdd(load); |
| 3395 if (load->HasSideEffects()) { |
| 3396 AddSimulate(expr->compound_bailout_id()); |
| 3397 } |
| 3398 |
| 3399 VISIT_FOR_VALUE(expr->value()); |
| 3400 HValue* right = Pop(); |
| 3401 HValue* left = Pop(); |
| 3402 |
| 3403 HInstruction* instr = BuildBinaryOperation(operation, left, right); |
| 3404 PushAndAdd(instr); |
| 3405 if (instr->HasSideEffects()) AddSimulate(operation->id()); |
| 3406 |
| 3407 HInstruction* store = BuildStoreNamed(obj, instr, prop); |
| 3408 AddInstruction(store); |
| 3409 |
| 3410 // Drop the simulated receiver and value and put back the value. |
| 3411 Drop(2); |
| 3412 Push(instr); |
| 3413 |
| 3414 } else { |
| 3415 // Keyed property. |
| 3416 VISIT_FOR_VALUE(prop->obj()); |
| 3417 VISIT_FOR_VALUE(prop->key()); |
| 3418 HValue* obj = environment()->ExpressionStackAt(1); |
| 3419 HValue* key = environment()->ExpressionStackAt(0); |
| 3420 |
| 3421 bool is_fast_elements = prop->IsMonomorphic() && |
| 3422 prop->GetMonomorphicReceiverType()->has_fast_elements(); |
| 3423 |
| 3424 HInstruction* load = is_fast_elements |
| 3425 ? BuildLoadKeyedFastElement(obj, key, prop) |
| 3426 : BuildLoadKeyedGeneric(obj, key); |
| 3427 PushAndAdd(load); |
| 3428 if (load->HasSideEffects()) { |
| 3429 AddSimulate(expr->compound_bailout_id()); |
| 3430 } |
| 3431 |
| 3432 VISIT_FOR_VALUE(expr->value()); |
| 3433 HValue* right = Pop(); |
| 3434 HValue* left = Pop(); |
| 3435 |
| 3436 HInstruction* instr = BuildBinaryOperation(operation, left, right); |
| 3437 PushAndAdd(instr); |
| 3438 if (instr->HasSideEffects()) AddSimulate(operation->id()); |
| 3439 |
| 3440 HInstruction* store = is_fast_elements |
| 3441 ? BuildStoreKeyedFastElement(obj, key, instr, prop) |
| 3442 : BuildStoreKeyedGeneric(obj, key, instr); |
| 3443 AddInstruction(store); |
| 3444 |
| 3445 // Drop the simulated receiver, key and value and put back the value. |
| 3446 Drop(3); |
| 3447 Push(instr); |
| 3448 } |
| 3449 } else { |
| 3450 BAILOUT("invalid lhs in compound assignment"); |
| 3451 } |
| 3452 } |
| 3453 |
| 3454 |
| 3455 void HGraphBuilder::VisitAssignment(Assignment* expr) { |
| 3456 VariableProxy* proxy = expr->target()->AsVariableProxy(); |
| 3457 Variable* var = proxy->AsVariable(); |
| 3458 Property* prop = expr->target()->AsProperty(); |
| 3459 ASSERT(var == NULL || prop == NULL); |
| 3460 |
| 3461 if (expr->is_compound()) { |
| 3462 HandleCompoundAssignment(expr); |
| 3463 return; |
| 3464 } |
| 3465 |
| 3466 if (var != NULL) { |
| 3467 if (proxy->IsArguments()) BAILOUT("assignment to arguments"); |
| 3468 if (var->is_global()) { |
| 3469 VISIT_FOR_VALUE(expr->value()); |
| 3470 HandleGlobalVariableAssignment(proxy, Top(), expr->position()); |
| 3471 } else { |
| 3472 // We allow reference to the arguments object only in assignemtns |
| 3473 // to local variables to make sure that the arguments object does |
| 3474 // not escape and is not modified. |
| 3475 VariableProxy* rhs = expr->value()->AsVariableProxy(); |
| 3476 if (rhs != NULL && |
| 3477 rhs->var()->IsStackAllocated() && |
| 3478 environment()->Lookup(rhs->var())->CheckFlag(HValue::kIsArguments)) { |
| 3479 Push(environment()->Lookup(rhs->var())); |
| 3480 } else { |
| 3481 VISIT_FOR_VALUE(expr->value()); |
| 3482 } |
| 3483 |
| 3484 Bind(proxy->var(), Top()); |
| 3485 } |
| 3486 } else if (prop != NULL) { |
| 3487 HandlePropertyAssignment(expr); |
| 3488 } else { |
| 3489 BAILOUT("unsupported invalid lhs"); |
| 3490 } |
| 3491 } |
| 3492 |
| 3493 |
| 3494 void HGraphBuilder::VisitThrow(Throw* expr) { |
| 3495 VISIT_FOR_VALUE(expr->exception()); |
| 3496 |
| 3497 HValue* value = environment()->Pop(); |
| 3498 HControlInstruction* instr = new HThrow(value); |
| 3499 instr->set_position(expr->position()); |
| 3500 current_subgraph_->FinishExit(instr); |
| 3501 } |
| 3502 |
| 3503 |
| 3504 void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, |
| 3505 HValue* object, |
| 3506 ZoneMapList* types, |
| 3507 Handle<String> name) { |
| 3508 int number_of_types = Min(types->length(), kMaxLoadPolymorphism); |
| 3509 ZoneMapList maps(number_of_types); |
| 3510 ZoneList<HSubgraph*> subgraphs(number_of_types + 1); |
| 3511 bool needs_generic = (types->length() > kMaxLoadPolymorphism); |
| 3512 |
| 3513 // Build subgraphs for each of the specific maps. |
| 3514 // |
| 3515 // TODO(ager): We should recognize when the prototype chains for |
| 3516 // different maps are identical. In that case we can avoid |
| 3517 // repeatedly generating the same prototype map checks. |
| 3518 for (int i = 0; i < number_of_types; ++i) { |
| 3519 Handle<Map> map = types->at(i); |
| 3520 LookupResult lookup; |
| 3521 map->LookupInDescriptors(NULL, *name, &lookup); |
| 3522 if (lookup.IsProperty() && lookup.type() == FIELD) { |
| 3523 maps.Add(map); |
| 3524 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3525 SubgraphScope scope(this, subgraph); |
| 3526 HInstruction* instr = |
| 3527 BuildLoadNamedField(object, expr, map, &lookup, false); |
| 3528 PushAndAdd(instr, expr->position()); |
| 3529 subgraphs.Add(subgraph); |
| 3530 } else { |
| 3531 needs_generic = true; |
| 3532 } |
| 3533 } |
| 3534 |
| 3535 // If none of the properties were named fields we generate a |
| 3536 // generic load. |
| 3537 if (maps.length() == 0) { |
| 3538 HInstruction* instr = BuildLoadNamedGeneric(object, expr); |
| 3539 PushAndAdd(instr, expr->position()); |
| 3540 return; |
| 3541 } |
| 3542 |
| 3543 // Build subgraph for generic load through IC. |
| 3544 { |
| 3545 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3546 SubgraphScope scope(this, subgraph); |
| 3547 if (!needs_generic && FLAG_deoptimize_uncommon_cases) { |
| 3548 subgraph->FinishExit(new HDeoptimize()); |
| 3549 } else { |
| 3550 HInstruction* instr = BuildLoadNamedGeneric(object, expr); |
| 3551 PushAndAdd(instr, expr->position()); |
| 3552 } |
| 3553 subgraphs.Add(subgraph); |
| 3554 } |
| 3555 |
| 3556 HBasicBlock* new_exit_block = |
| 3557 BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); |
| 3558 current_subgraph_->set_exit_block(new_exit_block); |
| 3559 } |
| 3560 |
| 3561 |
| 3562 HInstruction* HGraphBuilder::BuildLoadNamedField(HValue* object, |
| 3563 Property* expr, |
| 3564 Handle<Map> type, |
| 3565 LookupResult* lookup, |
| 3566 bool smi_and_map_check) { |
| 3567 if (smi_and_map_check) { |
| 3568 AddInstruction(new HCheckNonSmi(object)); |
| 3569 AddInstruction(new HCheckMap(object, type)); |
| 3570 } |
| 3571 |
| 3572 int index = lookup->GetLocalFieldIndexFromMap(*type); |
| 3573 if (index < 0) { |
| 3574 // Negative property indices are in-object properties, indexed |
| 3575 // from the end of the fixed part of the object. |
| 3576 int offset = (index * kPointerSize) + type->instance_size(); |
| 3577 return new HLoadNamedField(object, true, offset); |
| 3578 } else { |
| 3579 // Non-negative property indices are in the properties array. |
| 3580 int offset = (index * kPointerSize) + FixedArray::kHeaderSize; |
| 3581 return new HLoadNamedField(object, false, offset); |
| 3582 } |
| 3583 } |
| 3584 |
| 3585 |
| 3586 HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj, |
| 3587 Property* expr) { |
| 3588 ASSERT(expr->key()->IsPropertyName()); |
| 3589 Handle<Object> name = expr->key()->AsLiteral()->handle(); |
| 3590 return new HLoadNamedGeneric(obj, name); |
| 3591 } |
| 3592 |
| 3593 |
| 3594 HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj, |
| 3595 Property* expr, |
| 3596 Handle<Map> map, |
| 3597 Handle<String> name) { |
| 3598 LookupResult lookup; |
| 3599 map->LookupInDescriptors(NULL, *name, &lookup); |
| 3600 if (lookup.IsProperty() && lookup.type() == FIELD) { |
| 3601 return BuildLoadNamedField(obj, |
| 3602 expr, |
| 3603 map, |
| 3604 &lookup, |
| 3605 true); |
| 3606 } else { |
| 3607 return BuildLoadNamedGeneric(obj, expr); |
| 3608 } |
| 3609 } |
| 3610 |
| 3611 |
| 3612 HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object, |
| 3613 HValue* key) { |
| 3614 return new HLoadKeyedGeneric(object, key); |
| 3615 } |
| 3616 |
| 3617 |
| 3618 HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object, |
| 3619 HValue* key, |
| 3620 Property* expr) { |
| 3621 ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic()); |
| 3622 AddInstruction(new HCheckNonSmi(object)); |
| 3623 Handle<Map> map = expr->GetMonomorphicReceiverType(); |
| 3624 ASSERT(map->has_fast_elements()); |
| 3625 AddInstruction(new HCheckMap(object, map)); |
| 3626 HInstruction* elements = AddInstruction(new HLoadElements(object)); |
| 3627 HInstruction* length = AddInstruction(new HArrayLength(elements)); |
| 3628 AddInstruction(new HBoundsCheck(key, length)); |
| 3629 return new HLoadKeyedFastElement(elements, key); |
| 3630 } |
| 3631 |
| 3632 |
| 3633 HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object, |
| 3634 HValue* key, |
| 3635 HValue* value) { |
| 3636 return new HStoreKeyedGeneric(object, key, value); |
| 3637 } |
| 3638 |
| 3639 |
| 3640 HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object, |
| 3641 HValue* key, |
| 3642 HValue* val, |
| 3643 Expression* expr) { |
| 3644 ASSERT(expr->IsMonomorphic()); |
| 3645 AddInstruction(new HCheckNonSmi(object)); |
| 3646 Handle<Map> map = expr->GetMonomorphicReceiverType(); |
| 3647 ASSERT(map->has_fast_elements()); |
| 3648 AddInstruction(new HCheckMap(object, map)); |
| 3649 HInstruction* elements = AddInstruction(new HLoadElements(object)); |
| 3650 AddInstruction(new HCheckMap(elements, FACTORY->fixed_array_map())); |
| 3651 bool is_array = (map->instance_type() == JS_ARRAY_TYPE); |
| 3652 HInstruction* length = NULL; |
| 3653 if (is_array) { |
| 3654 length = AddInstruction(new HArrayLength(object)); |
| 3655 } else { |
| 3656 length = AddInstruction(new HArrayLength(elements)); |
| 3657 } |
| 3658 AddInstruction(new HBoundsCheck(key, length)); |
| 3659 return new HStoreKeyedFastElement(elements, key, val); |
| 3660 } |
| 3661 |
| 3662 |
| 3663 bool HGraphBuilder::TryArgumentsAccess(Property* expr) { |
| 3664 VariableProxy* proxy = expr->obj()->AsVariableProxy(); |
| 3665 if (proxy == NULL) return false; |
| 3666 if (!proxy->var()->IsStackAllocated()) return false; |
| 3667 if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) { |
| 3668 return false; |
| 3669 } |
| 3670 |
| 3671 if (expr->key()->IsPropertyName()) { |
| 3672 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName(); |
| 3673 if (!name->IsEqualTo(CStrVector("length"))) return false; |
| 3674 HInstruction* elements = AddInstruction(new HArgumentsElements); |
| 3675 PushAndAdd(new HArgumentsLength(elements)); |
| 3676 } else { |
| 3677 VisitForValue(expr->key()); |
| 3678 if (HasStackOverflow()) return false; |
| 3679 HValue* key = Pop(); |
| 3680 HInstruction* elements = AddInstruction(new HArgumentsElements); |
| 3681 HInstruction* length = AddInstruction(new HArgumentsLength(elements)); |
| 3682 AddInstruction(new HBoundsCheck(key, length)); |
| 3683 PushAndAdd(new HAccessArgumentsAt(elements, length, key)); |
| 3684 } |
| 3685 return true; |
| 3686 } |
| 3687 |
| 3688 |
| 3689 void HGraphBuilder::VisitProperty(Property* expr) { |
| 3690 expr->RecordTypeFeedback(oracle()); |
| 3691 |
| 3692 if (TryArgumentsAccess(expr)) return; |
| 3693 CHECK_BAILOUT; |
| 3694 |
| 3695 VISIT_FOR_VALUE(expr->obj()); |
| 3696 |
| 3697 HInstruction* instr = NULL; |
| 3698 if (expr->IsArrayLength()) { |
| 3699 HValue* array = Pop(); |
| 3700 AddInstruction(new HCheckNonSmi(array)); |
| 3701 instr = new HArrayLength(array); |
| 3702 |
| 3703 } else if (expr->key()->IsPropertyName()) { |
| 3704 Handle<String> name = expr->key()->AsLiteral()->AsPropertyName(); |
| 3705 ZoneMapList* types = expr->GetReceiverTypes(); |
| 3706 |
| 3707 HValue* obj = Pop(); |
| 3708 if (expr->IsMonomorphic()) { |
| 3709 instr = BuildLoadNamed(obj, expr, types->first(), name); |
| 3710 } else if (types != NULL && types->length() > 1) { |
| 3711 HandlePolymorphicLoadNamedField(expr, obj, types, name); |
| 3712 return; |
| 3713 |
| 3714 } else { |
| 3715 instr = BuildLoadNamedGeneric(obj, expr); |
| 3716 } |
| 3717 |
| 3718 } else { |
| 3719 VISIT_FOR_VALUE(expr->key()); |
| 3720 |
| 3721 HValue* key = Pop(); |
| 3722 HValue* obj = Pop(); |
| 3723 |
| 3724 bool is_fast_elements = expr->IsMonomorphic() && |
| 3725 expr->GetMonomorphicReceiverType()->has_fast_elements(); |
| 3726 |
| 3727 instr = is_fast_elements |
| 3728 ? BuildLoadKeyedFastElement(obj, key, expr) |
| 3729 : BuildLoadKeyedGeneric(obj, key); |
| 3730 } |
| 3731 PushAndAdd(instr, expr->position()); |
| 3732 } |
| 3733 |
| 3734 |
| 3735 void HGraphBuilder::AddCheckConstantFunction(Call* expr, |
| 3736 HValue* receiver, |
| 3737 Handle<Map> receiver_map, |
| 3738 bool smi_and_map_check) { |
| 3739 // Constant functions have the nice property that the map will change if they |
| 3740 // are overwritten. Therefore it is enough to check the map of the holder and |
| 3741 // its prototypes. |
| 3742 if (smi_and_map_check) { |
| 3743 AddInstruction(new HCheckNonSmi(receiver)); |
| 3744 AddInstruction(new HCheckMap(receiver, receiver_map)); |
| 3745 } |
| 3746 if (!expr->holder().is_null()) { |
| 3747 AddInstruction(new HCheckPrototypeMaps(receiver, |
| 3748 expr->holder(), |
| 3749 receiver_map)); |
| 3750 } |
| 3751 } |
| 3752 |
| 3753 |
| 3754 void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
| 3755 HValue* receiver, |
| 3756 ZoneMapList* types, |
| 3757 Handle<String> name) { |
| 3758 int argument_count = expr->arguments()->length() + 1; // Plus receiver. |
| 3759 int number_of_types = Min(types->length(), kMaxCallPolymorphism); |
| 3760 ZoneMapList maps(number_of_types); |
| 3761 ZoneList<HSubgraph*> subgraphs(number_of_types + 1); |
| 3762 bool needs_generic = (types->length() > kMaxCallPolymorphism); |
| 3763 |
| 3764 // Build subgraphs for each of the specific maps. |
| 3765 // |
| 3766 // TODO(ager): We should recognize when the prototype chains for |
| 3767 // different maps are identical. In that case we can avoid |
| 3768 // repeatedly generating the same prototype map checks. |
| 3769 for (int i = 0; i < number_of_types; ++i) { |
| 3770 Handle<Map> map = types->at(i); |
| 3771 if (expr->ComputeTarget(map, name)) { |
| 3772 maps.Add(map); |
| 3773 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3774 SubgraphScope scope(this, subgraph); |
| 3775 AddCheckConstantFunction(expr, receiver, map, false); |
| 3776 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { |
| 3777 PrintF("Trying to inline the polymorphic call to %s\n", |
| 3778 *name->ToCString()); |
| 3779 } |
| 3780 if (!FLAG_polymorphic_inlining || !TryInline(expr)) { |
| 3781 // Check for bailout, as trying to inline might fail due to bailout |
| 3782 // during hydrogen processing. |
| 3783 CHECK_BAILOUT; |
| 3784 HCall* call = new HCallConstantFunction(expr->target(), argument_count); |
| 3785 ProcessCall(call, expr->position()); |
| 3786 } |
| 3787 subgraphs.Add(subgraph); |
| 3788 } else { |
| 3789 needs_generic = true; |
| 3790 } |
| 3791 } |
| 3792 |
| 3793 // If we couldn't compute the target for any of the maps just |
| 3794 // perform an IC call. |
| 3795 if (maps.length() == 0) { |
| 3796 HCall* call = new HCallNamed(name, argument_count); |
| 3797 ProcessCall(call, expr->position()); |
| 3798 return; |
| 3799 } |
| 3800 |
| 3801 // Build subgraph for generic call through IC. |
| 3802 { |
| 3803 HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
| 3804 SubgraphScope scope(this, subgraph); |
| 3805 if (!needs_generic && FLAG_deoptimize_uncommon_cases) { |
| 3806 subgraph->FinishExit(new HDeoptimize()); |
| 3807 } else { |
| 3808 HCall* call = new HCallNamed(name, argument_count); |
| 3809 ProcessCall(call, expr->position()); |
| 3810 } |
| 3811 subgraphs.Add(subgraph); |
| 3812 } |
| 3813 |
| 3814 HBasicBlock* new_exit_block = |
| 3815 BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id()); |
| 3816 current_subgraph_->set_exit_block(new_exit_block); |
| 3817 } |
| 3818 |
| 3819 |
| 3820 void HGraphBuilder::TraceInline(Handle<JSFunction> target, bool result) { |
| 3821 SmartPointer<char> callee = target->shared()->DebugName()->ToCString(); |
| 3822 SmartPointer<char> caller = |
| 3823 graph()->info()->function()->debug_name()->ToCString(); |
| 3824 if (result) { |
| 3825 PrintF("Inlined %s called from %s.\n", *callee, *caller); |
| 3826 } else { |
| 3827 PrintF("Do not inline %s called from %s.\n", *callee, *caller); |
| 3828 } |
| 3829 } |
| 3830 |
| 3831 |
| 3832 bool HGraphBuilder::TryInline(Call* expr) { |
| 3833 if (!FLAG_use_inlining) return false; |
| 3834 |
| 3835 // Precondition: call is monomorphic and we have found a target with the |
| 3836 // appropriate arity. |
| 3837 Handle<JSFunction> target = expr->target(); |
| 3838 |
| 3839 // Do a quick check on source code length to avoid parsing large |
| 3840 // inlining candidates. |
| 3841 if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) { |
| 3842 if (FLAG_trace_inlining) TraceInline(target, false); |
| 3843 return false; |
| 3844 } |
| 3845 |
| 3846 // Target must be inlineable. |
| 3847 if (!target->IsInlineable()) return false; |
| 3848 |
| 3849 // No context change required. |
| 3850 CompilationInfo* outer_info = graph()->info(); |
| 3851 if (target->context() != outer_info->closure()->context() || |
| 3852 outer_info->scope()->contains_with() || |
| 3853 outer_info->scope()->num_heap_slots() > 0) { |
| 3854 return false; |
| 3855 } |
| 3856 |
| 3857 // Don't inline deeper than two calls. |
| 3858 HEnvironment* env = environment(); |
| 3859 if (env->outer() != NULL && env->outer()->outer() != NULL) return false; |
| 3860 |
| 3861 // Don't inline recursive functions. |
| 3862 if (target->shared() == outer_info->closure()->shared()) return false; |
| 3863 |
| 3864 // We don't want to add more than a certain number of nodes from inlining. |
| 3865 if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) { |
| 3866 if (FLAG_trace_inlining) TraceInline(target, false); |
| 3867 return false; |
| 3868 } |
| 3869 |
| 3870 int count_before = AstNode::Count(); |
| 3871 |
| 3872 // Parse and allocate variables. |
| 3873 Handle<SharedFunctionInfo> shared(target->shared()); |
| 3874 CompilationInfo inner_info(shared); |
| 3875 if (!ParserApi::Parse(&inner_info) || |
| 3876 !Scope::Analyze(&inner_info)) { |
| 3877 return false; |
| 3878 } |
| 3879 FunctionLiteral* function = inner_info.function(); |
| 3880 |
| 3881 // Count the number of AST nodes added by inlining this call. |
| 3882 int nodes_added = AstNode::Count() - count_before; |
| 3883 if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) { |
| 3884 if (FLAG_trace_inlining) TraceInline(target, false); |
| 3885 return false; |
| 3886 } |
| 3887 |
| 3888 // Check if we can handle all declarations in the inlined functions. |
| 3889 VisitDeclarations(inner_info.scope()->declarations()); |
| 3890 if (HasStackOverflow()) { |
| 3891 ClearStackOverflow(); |
| 3892 return false; |
| 3893 } |
| 3894 |
| 3895 // Don't inline functions that uses the arguments object or that |
| 3896 // have a mismatching number of parameters. |
| 3897 int arity = expr->arguments()->length(); |
| 3898 if (function->scope()->arguments() != NULL || |
| 3899 arity != target->shared()->formal_parameter_count()) { |
| 3900 return false; |
| 3901 } |
| 3902 |
| 3903 // All statements in the body must be inlineable. |
| 3904 for (int i = 0, count = function->body()->length(); i < count; ++i) { |
| 3905 if (!function->body()->at(i)->IsInlineable()) return false; |
| 3906 } |
| 3907 |
| 3908 // Generate the deoptimization data for the unoptimized version of |
| 3909 // the target function if we don't already have it. |
| 3910 if (!shared->has_deoptimization_support()) { |
| 3911 // Note that we compile here using the same AST that we will use for |
| 3912 // generating the optimized inline code. |
| 3913 inner_info.EnableDeoptimizationSupport(); |
| 3914 if (!FullCodeGenerator::MakeCode(&inner_info)) return false; |
| 3915 shared->EnableDeoptimizationSupport(*inner_info.code()); |
| 3916 Compiler::RecordFunctionCompilation( |
| 3917 Logger::FUNCTION_TAG, |
| 3918 Handle<String>(shared->DebugName()), |
| 3919 shared->start_position(), |
| 3920 &inner_info); |
| 3921 } |
| 3922 |
| 3923 // Save the pending call context and type feedback oracle. Set up new ones |
| 3924 // for the inlined function. |
| 3925 ASSERT(shared->has_deoptimization_support()); |
| 3926 AstContext* saved_call_context = call_context(); |
| 3927 HBasicBlock* saved_function_return = function_return(); |
| 3928 TypeFeedbackOracle* saved_oracle = oracle(); |
| 3929 // On-stack replacement cannot target inlined functions. Since we don't |
| 3930 // use a separate CompilationInfo structure for the inlined function, we |
| 3931 // save and restore the AST ID in the original compilation info. |
| 3932 int saved_osr_ast_id = graph()->info()->osr_ast_id(); |
| 3933 |
| 3934 TestContext* test_context = NULL; |
| 3935 if (ast_context()->IsTest()) { |
| 3936 // Inlined body is treated as if it occurs in an 'inlined' call context |
| 3937 // with true and false blocks that will forward to the real ones. |
| 3938 HBasicBlock* if_true = graph()->CreateBasicBlock(); |
| 3939 HBasicBlock* if_false = graph()->CreateBasicBlock(); |
| 3940 if_true->MarkAsInlineReturnTarget(); |
| 3941 if_false->MarkAsInlineReturnTarget(); |
| 3942 // AstContext constructor pushes on the context stack. |
| 3943 bool invert_true = TestContext::cast(ast_context())->invert_true(); |
| 3944 bool invert_false = TestContext::cast(ast_context())->invert_false(); |
| 3945 test_context = new TestContext(this, if_true, if_false, |
| 3946 invert_true, invert_false); |
| 3947 function_return_ = NULL; |
| 3948 } else { |
| 3949 // Inlined body is treated as if it occurs in the original call context. |
| 3950 function_return_ = graph()->CreateBasicBlock(); |
| 3951 function_return_->MarkAsInlineReturnTarget(); |
| 3952 } |
| 3953 call_context_ = ast_context(); |
| 3954 TypeFeedbackOracle new_oracle(Handle<Code>(shared->code())); |
| 3955 oracle_ = &new_oracle; |
| 3956 graph()->info()->SetOsrAstId(AstNode::kNoNumber); |
| 3957 |
| 3958 HSubgraph* body = CreateInlinedSubgraph(env, target, function); |
| 3959 body->exit_block()->AddInstruction(new HEnterInlined(target, function)); |
| 3960 AddToSubgraph(body, function->body()); |
| 3961 if (HasStackOverflow()) { |
| 3962 // Bail out if the inline function did, as we cannot residualize a call |
| 3963 // instead. |
| 3964 delete test_context; |
| 3965 call_context_ = saved_call_context; |
| 3966 function_return_ = saved_function_return; |
| 3967 oracle_ = saved_oracle; |
| 3968 graph()->info()->SetOsrAstId(saved_osr_ast_id); |
| 3969 return false; |
| 3970 } |
| 3971 |
| 3972 // Update inlined nodes count. |
| 3973 inlined_count_ += nodes_added; |
| 3974 |
| 3975 if (FLAG_trace_inlining) TraceInline(target, true); |
| 3976 |
| 3977 if (body->HasExit()) { |
| 3978 // Add a return of undefined if control can fall off the body. In a |
| 3979 // test context, undefined is false. |
| 3980 HValue* return_value = NULL; |
| 3981 HBasicBlock* target = NULL; |
| 3982 if (test_context == NULL) { |
| 3983 ASSERT(function_return_ != NULL); |
| 3984 return_value = graph()->GetConstantUndefined(); |
| 3985 target = function_return_; |
| 3986 } else { |
| 3987 return_value = graph()->GetConstantFalse(); |
| 3988 target = test_context->if_false(); |
| 3989 } |
| 3990 body->exit_block()->AddLeaveInlined(return_value, target); |
| 3991 body->set_exit_block(NULL); |
| 3992 } |
| 3993 |
| 3994 // Record the environment at the inlined function call. |
| 3995 AddSimulate(expr->ReturnId()); |
| 3996 |
| 3997 // Jump to the function entry (without re-recording the environment). |
| 3998 subgraph()->exit_block()->Finish(new HGoto(body->entry_block())); |
| 3999 |
| 4000 // Fix up the function exits. |
| 4001 if (test_context != NULL) { |
| 4002 HBasicBlock* if_true = test_context->if_true(); |
| 4003 HBasicBlock* if_false = test_context->if_false(); |
| 4004 if_true->SetJoinId(expr->id()); |
| 4005 if_false->SetJoinId(expr->id()); |
| 4006 ASSERT(ast_context() == test_context); |
| 4007 delete test_context; // Destructor pops from expression context stack. |
| 4008 // Forward to the real test context. |
| 4009 |
| 4010 // Discard the lingering branch value (which may be true or false, |
| 4011 // depending on whether the final condition was negated) and jump to the |
| 4012 // true target with a true branch value. |
| 4013 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); |
| 4014 bool invert_true = TestContext::cast(ast_context())->invert_true(); |
| 4015 HValue* true_value = invert_true |
| 4016 ? graph()->GetConstantFalse() |
| 4017 : graph()->GetConstantTrue(); |
| 4018 if_true->last_environment()->Pop(); |
| 4019 if (true_target->IsInlineReturnTarget()) { |
| 4020 if_true->AddLeaveInlined(true_value, true_target); |
| 4021 } else { |
| 4022 if_true->last_environment()->Push(true_value); |
| 4023 if_true->Goto(true_target); |
| 4024 } |
| 4025 |
| 4026 // Do the same for the false target. |
| 4027 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); |
| 4028 bool invert_false = TestContext::cast(ast_context())->invert_false(); |
| 4029 HValue* false_value = invert_false |
| 4030 ? graph()->GetConstantTrue() |
| 4031 : graph()->GetConstantFalse(); |
| 4032 if_false->last_environment()->Pop(); |
| 4033 if (false_target->IsInlineReturnTarget()) { |
| 4034 if_false->AddLeaveInlined(false_value, false_target); |
| 4035 } else { |
| 4036 if_false->last_environment()->Push(false_value); |
| 4037 if_false->Goto(false_target); |
| 4038 } |
| 4039 |
| 4040 // TODO(kmillikin): Come up with a better way to handle this. It is too |
| 4041 // subtle. NULL here indicates that the enclosing context has no control |
| 4042 // flow to handle. |
| 4043 subgraph()->set_exit_block(NULL); |
| 4044 |
| 4045 } else { |
| 4046 function_return_->SetJoinId(expr->id()); |
| 4047 subgraph()->set_exit_block(function_return_); |
| 4048 } |
| 4049 |
| 4050 call_context_ = saved_call_context; |
| 4051 function_return_ = saved_function_return; |
| 4052 oracle_ = saved_oracle; |
| 4053 graph()->info()->SetOsrAstId(saved_osr_ast_id); |
| 4054 return true; |
| 4055 } |
| 4056 |
| 4057 |
| 4058 void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { |
| 4059 ASSERT(target->IsInlineReturnTarget()); |
| 4060 AddInstruction(new HLeaveInlined); |
| 4061 HEnvironment* outer = last_environment()->outer(); |
| 4062 outer->Push(return_value); |
| 4063 UpdateEnvironment(outer); |
| 4064 Goto(target); |
| 4065 } |
| 4066 |
| 4067 |
| 4068 bool HGraphBuilder::TryMathFunctionInline(Call* expr) { |
| 4069 // Try to inline calls like Math.* as operations in the calling function. |
| 4070 MathFunctionId id = expr->target()->shared()->math_function_id(); |
| 4071 int argument_count = expr->arguments()->length() + 1; // Plus receiver. |
| 4072 switch (id) { |
| 4073 case kMathRound: |
| 4074 case kMathFloor: |
| 4075 case kMathAbs: |
| 4076 case kMathSqrt: |
| 4077 if (argument_count == 2) { |
| 4078 HValue* argument = Pop(); |
| 4079 // Pop receiver. |
| 4080 Pop(); |
| 4081 HUnaryMathOperation* op = new HUnaryMathOperation(argument, id); |
| 4082 PushAndAdd(op, expr->position()); |
| 4083 return true; |
| 4084 } |
| 4085 break; |
| 4086 default: |
| 4087 // Either not a special math function or not yet supported for inlining. |
| 4088 break; |
| 4089 } |
| 4090 return false; |
| 4091 } |
| 4092 |
| 4093 |
| 4094 bool HGraphBuilder::TryCallApply(Call* expr) { |
| 4095 Expression* callee = expr->expression(); |
| 4096 Property* prop = callee->AsProperty(); |
| 4097 ASSERT(prop != NULL); |
| 4098 |
| 4099 if (graph()->info()->scope()->arguments() == NULL) return false; |
| 4100 |
| 4101 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); |
| 4102 if (!name->IsEqualTo(CStrVector("apply"))) return false; |
| 4103 |
| 4104 ZoneList<Expression*>* args = expr->arguments(); |
| 4105 if (args->length() != 2) return false; |
| 4106 |
| 4107 VariableProxy* arg_two = args->at(1)->AsVariableProxy(); |
| 4108 if (arg_two == NULL) return false; |
| 4109 HValue* arg_two_value = environment()->Lookup(arg_two->var()); |
| 4110 if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false; |
| 4111 |
| 4112 if (!expr->IsMonomorphic()) return false; |
| 4113 |
| 4114 // Found pattern f.apply(receiver, arguments). |
| 4115 VisitForValue(prop->obj()); |
| 4116 if (HasStackOverflow()) return false; |
| 4117 HValue* function = Pop(); |
| 4118 VisitForValue(args->at(0)); |
| 4119 if (HasStackOverflow()) return false; |
| 4120 HValue* receiver = Pop(); |
| 4121 HInstruction* elements = AddInstruction(new HArgumentsElements); |
| 4122 HInstruction* length = AddInstruction(new HArgumentsLength(elements)); |
| 4123 AddCheckConstantFunction(expr, |
| 4124 function, |
| 4125 expr->GetReceiverTypes()->first(), |
| 4126 true); |
| 4127 PushAndAdd(new HApplyArguments(function, receiver, length, elements), |
| 4128 expr->position()); |
| 4129 return true; |
| 4130 } |
| 4131 |
| 4132 |
| 4133 void HGraphBuilder::VisitCall(Call* expr) { |
| 4134 Expression* callee = expr->expression(); |
| 4135 int argument_count = expr->arguments()->length() + 1; // Plus receiver. |
| 4136 HCall* call = NULL; |
| 4137 |
| 4138 Property* prop = callee->AsProperty(); |
| 4139 if (prop != NULL) { |
| 4140 if (!prop->key()->IsPropertyName()) { |
| 4141 // Keyed function call. |
| 4142 VisitArgument(prop->obj()); |
| 4143 CHECK_BAILOUT; |
| 4144 |
| 4145 VISIT_FOR_VALUE(prop->key()); |
| 4146 // Push receiver and key like the non-optimized code generator expects it. |
| 4147 HValue* key = Pop(); |
| 4148 HValue* receiver = Pop(); |
| 4149 Push(key); |
| 4150 Push(receiver); |
| 4151 |
| 4152 VisitArgumentList(expr->arguments()); |
| 4153 CHECK_BAILOUT; |
| 4154 |
| 4155 call = new HCallKeyed(key, argument_count); |
| 4156 ProcessCall(call, expr->position()); |
| 4157 HValue* result = Pop(); |
| 4158 // Drop the receiver from the environment and put back the result of |
| 4159 // the call. |
| 4160 Drop(1); |
| 4161 Push(result); |
| 4162 return; |
| 4163 } |
| 4164 |
| 4165 // Named function call. |
| 4166 expr->RecordTypeFeedback(oracle()); |
| 4167 |
| 4168 if (TryCallApply(expr)) return; |
| 4169 CHECK_BAILOUT; |
| 4170 |
| 4171 HValue* receiver = VisitArgument(prop->obj()); |
| 4172 CHECK_BAILOUT; |
| 4173 VisitArgumentList(expr->arguments()); |
| 4174 CHECK_BAILOUT; |
| 4175 |
| 4176 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); |
| 4177 |
| 4178 expr->RecordTypeFeedback(oracle()); |
| 4179 ZoneMapList* types = expr->GetReceiverTypes(); |
| 4180 |
| 4181 if (expr->IsMonomorphic()) { |
| 4182 AddCheckConstantFunction(expr, receiver, types->first(), true); |
| 4183 |
| 4184 if (TryMathFunctionInline(expr) || TryInline(expr)) { |
| 4185 return; |
| 4186 } else { |
| 4187 // Check for bailout, as the TryInline call in the if condition above |
| 4188 // might return false due to bailout during hydrogen processing. |
| 4189 CHECK_BAILOUT; |
| 4190 call = new HCallConstantFunction(expr->target(), argument_count); |
| 4191 } |
| 4192 } else if (types != NULL && types->length() > 1) { |
| 4193 HandlePolymorphicCallNamed(expr, receiver, types, name); |
| 4194 return; |
| 4195 |
| 4196 } else { |
| 4197 call = new HCallNamed(name, argument_count); |
| 4198 } |
| 4199 |
| 4200 } else { |
| 4201 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
| 4202 bool global_call = (var != NULL) && var->is_global() && !var->is_this(); |
| 4203 |
| 4204 if (!global_call) { |
| 4205 ++argument_count; |
| 4206 VisitArgument(expr->expression()); |
| 4207 CHECK_BAILOUT; |
| 4208 } |
| 4209 |
| 4210 if (global_call) { |
| 4211 // If there is a global property cell for the name at compile time and |
| 4212 // access check is not enabled we assume that the function will not change |
| 4213 // and generate optimized code for calling the function. |
| 4214 CompilationInfo* info = graph()->info(); |
| 4215 bool known_global_function = info->has_global_object() && |
| 4216 !info->global_object()->IsAccessCheckNeeded() && |
| 4217 expr->ComputeGlobalTarget(Handle<GlobalObject>(info->global_object()), |
| 4218 var->name()); |
| 4219 if (known_global_function) { |
| 4220 // Push the global object instead of the global receiver because |
| 4221 // code generated by the full code generator expects it. |
| 4222 PushAndAdd(new HGlobalObject); |
| 4223 VisitArgumentList(expr->arguments()); |
| 4224 CHECK_BAILOUT; |
| 4225 |
| 4226 VISIT_FOR_VALUE(expr->expression()); |
| 4227 HValue* function = Pop(); |
| 4228 AddInstruction(new HCheckFunction(function, expr->target())); |
| 4229 |
| 4230 // Replace the global object with the global receiver. |
| 4231 HGlobalReceiver* global_receiver = new HGlobalReceiver; |
| 4232 // Index of the receiver from the top of the expression stack. |
| 4233 const int receiver_index = argument_count - 1; |
| 4234 AddInstruction(global_receiver); |
| 4235 ASSERT(environment()->ExpressionStackAt(receiver_index)-> |
| 4236 IsGlobalObject()); |
| 4237 environment()->SetExpressionStackAt(receiver_index, global_receiver); |
| 4238 |
| 4239 if (TryInline(expr)) return; |
| 4240 // Check for bailout, as trying to inline might fail due to bailout |
| 4241 // during hydrogen processing. |
| 4242 CHECK_BAILOUT; |
| 4243 |
| 4244 call = new HCallKnownGlobal(expr->target(), argument_count); |
| 4245 } else { |
| 4246 PushAndAdd(new HGlobalObject); |
| 4247 VisitArgumentList(expr->arguments()); |
| 4248 CHECK_BAILOUT; |
| 4249 |
| 4250 call = new HCallGlobal(var->name(), argument_count); |
| 4251 } |
| 4252 |
| 4253 } else { |
| 4254 PushAndAdd(new HGlobalReceiver); |
| 4255 VisitArgumentList(expr->arguments()); |
| 4256 CHECK_BAILOUT; |
| 4257 |
| 4258 call = new HCallFunction(argument_count); |
| 4259 } |
| 4260 } |
| 4261 |
| 4262 ProcessCall(call, expr->position()); |
| 4263 } |
| 4264 |
| 4265 |
| 4266 void HGraphBuilder::VisitCallNew(CallNew* expr) { |
| 4267 // The constructor function is also used as the receiver argument to the |
| 4268 // JS construct call builtin. |
| 4269 VisitArgument(expr->expression()); |
| 4270 CHECK_BAILOUT; |
| 4271 VisitArgumentList(expr->arguments()); |
| 4272 CHECK_BAILOUT; |
| 4273 |
| 4274 int argument_count = expr->arguments()->length() + 1; // Plus constructor. |
| 4275 HCall* call = new HCallNew(argument_count); |
| 4276 |
| 4277 ProcessCall(call, expr->position()); |
| 4278 } |
| 4279 |
| 4280 |
| 4281 // Support for generating inlined runtime functions. |
| 4282 |
| 4283 // Lookup table for generators for runtime calls that are generated inline. |
| 4284 // Elements of the table are member pointers to functions of HGraphBuilder. |
| 4285 #define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \ |
| 4286 &HGraphBuilder::Generate##Name, |
| 4287 |
| 4288 const HGraphBuilder::InlineFunctionGenerator |
| 4289 HGraphBuilder::kInlineFunctionGenerators[] = { |
| 4290 INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS) |
| 4291 INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS) |
| 4292 }; |
| 4293 #undef INLINE_FUNCTION_GENERATOR_ADDRESS |
| 4294 |
| 4295 |
| 4296 void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { |
| 4297 Handle<String> name = expr->name(); |
| 4298 if (name->IsEqualTo(CStrVector("_Log"))) { |
| 4299 Push(graph()->GetConstantUndefined()); |
| 4300 return; |
| 4301 } |
| 4302 |
| 4303 const Runtime::Function* function = expr->function(); |
| 4304 if (expr->is_jsruntime()) { |
| 4305 BAILOUT("call to a JavaScript runtime function"); |
| 4306 } |
| 4307 ASSERT(function != NULL); |
| 4308 |
| 4309 VisitArgumentList(expr->arguments()); |
| 4310 CHECK_BAILOUT; |
| 4311 |
| 4312 int argument_count = expr->arguments()->length(); |
| 4313 if (function->intrinsic_type == Runtime::INLINE) { |
| 4314 ASSERT(name->length() > 0); |
| 4315 ASSERT(name->Get(0) == '_'); |
| 4316 // Call to an inline function. |
| 4317 int lookup_index = static_cast<int>(function->function_id) - |
| 4318 static_cast<int>(Runtime::kFirstInlineFunction); |
| 4319 ASSERT(lookup_index >= 0); |
| 4320 ASSERT(static_cast<size_t>(lookup_index) < |
| 4321 ARRAY_SIZE(kInlineFunctionGenerators)); |
| 4322 InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index]; |
| 4323 |
| 4324 // Call the inline code generator using the pointer-to-member. |
| 4325 (this->*generator)(argument_count); |
| 4326 } else { |
| 4327 ASSERT(function->intrinsic_type == Runtime::RUNTIME); |
| 4328 HCall* call = new HCallRuntime(name, expr->function(), argument_count); |
| 4329 ProcessCall(call, RelocInfo::kNoPosition); |
| 4330 } |
| 4331 } |
| 4332 |
| 4333 |
| 4334 void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
| 4335 Token::Value op = expr->op(); |
| 4336 if (op == Token::VOID) { |
| 4337 VISIT_FOR_EFFECT(expr->expression()); |
| 4338 Push(graph()->GetConstantUndefined()); |
| 4339 } else if (op == Token::DELETE) { |
| 4340 Property* prop = expr->expression()->AsProperty(); |
| 4341 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
| 4342 if (prop == NULL && var == NULL) { |
| 4343 // Result of deleting non-property, non-variable reference is true. |
| 4344 // Evaluate the subexpression for side effects. |
| 4345 VISIT_FOR_EFFECT(expr->expression()); |
| 4346 Push(graph_->GetConstantTrue()); |
| 4347 } else if (var != NULL && |
| 4348 !var->is_global() && |
| 4349 var->AsSlot() != NULL && |
| 4350 var->AsSlot()->type() != Slot::LOOKUP) { |
| 4351 // Result of deleting non-global, non-dynamic variables is false. |
| 4352 // The subexpression does not have side effects. |
| 4353 Push(graph_->GetConstantFalse()); |
| 4354 } else if (prop != NULL) { |
| 4355 VISIT_FOR_VALUE(prop->obj()); |
| 4356 VISIT_FOR_VALUE(prop->key()); |
| 4357 HValue* key = Pop(); |
| 4358 HValue* obj = Pop(); |
| 4359 PushAndAdd(new HDeleteProperty(obj, key)); |
| 4360 } else if (var->is_global()) { |
| 4361 BAILOUT("delete with global variable"); |
| 4362 } else { |
| 4363 BAILOUT("delete with non-global variable"); |
| 4364 } |
| 4365 } else if (op == Token::NOT) { |
| 4366 HSubgraph* true_graph = CreateEmptySubgraph(); |
| 4367 HSubgraph* false_graph = CreateEmptySubgraph(); |
| 4368 VisitCondition(expr->expression(), |
| 4369 false_graph->entry_block(), |
| 4370 true_graph->entry_block(), |
| 4371 true, true); |
| 4372 if (HasStackOverflow()) return; |
| 4373 true_graph->environment()->Push(graph_->GetConstantTrue()); |
| 4374 false_graph->environment()->Push(graph_->GetConstantFalse()); |
| 4375 current_subgraph_->AppendJoin(true_graph, false_graph, expr); |
| 4376 } else if (op == Token::BIT_NOT || op == Token::SUB) { |
| 4377 VISIT_FOR_VALUE(expr->expression()); |
| 4378 HValue* value = Pop(); |
| 4379 HInstruction* instr = NULL; |
| 4380 switch (op) { |
| 4381 case Token::BIT_NOT: |
| 4382 instr = new HBitNot(value); |
| 4383 break; |
| 4384 case Token::SUB: |
| 4385 instr = new HMul(graph_->GetConstantMinus1(), value); |
| 4386 break; |
| 4387 default: |
| 4388 UNREACHABLE(); |
| 4389 break; |
| 4390 } |
| 4391 PushAndAdd(instr); |
| 4392 } else if (op == Token::TYPEOF) { |
| 4393 VISIT_FOR_VALUE(expr->expression()); |
| 4394 HValue* value = Pop(); |
| 4395 PushAndAdd(new HTypeof(value)); |
| 4396 } else { |
| 4397 BAILOUT("Value: unsupported unary operation"); |
| 4398 } |
| 4399 } |
| 4400 |
| 4401 |
| 4402 void HGraphBuilder::VisitIncrementOperation(IncrementOperation* expr) { |
| 4403 // IncrementOperation is never visited by the visitor. It only |
| 4404 // occurs as a subexpression of CountOperation. |
| 4405 UNREACHABLE(); |
| 4406 } |
| 4407 |
| 4408 |
| 4409 HInstruction* HGraphBuilder::BuildIncrement(HValue* value, bool increment) { |
| 4410 HConstant* delta = increment |
| 4411 ? graph_->GetConstant1() |
| 4412 : graph_->GetConstantMinus1(); |
| 4413 HInstruction* instr = new HAdd(value, delta); |
| 4414 AssumeRepresentation(instr, Representation::Integer32()); |
| 4415 return instr; |
| 4416 } |
| 4417 |
| 4418 |
| 4419 void HGraphBuilder::VisitCountOperation(CountOperation* expr) { |
| 4420 IncrementOperation* increment = expr->increment(); |
| 4421 Expression* target = increment->expression(); |
| 4422 VariableProxy* proxy = target->AsVariableProxy(); |
| 4423 Variable* var = proxy->AsVariable(); |
| 4424 Property* prop = target->AsProperty(); |
| 4425 ASSERT(var == NULL || prop == NULL); |
| 4426 bool inc = expr->op() == Token::INC; |
| 4427 |
| 4428 if (var != NULL) { |
| 4429 if (!var->is_global() && !var->IsStackAllocated()) { |
| 4430 BAILOUT("non-stack/non-global variable in count operation"); |
| 4431 } |
| 4432 |
| 4433 VISIT_FOR_VALUE(target); |
| 4434 |
| 4435 HValue* value = Pop(); |
| 4436 HInstruction* instr = BuildIncrement(value, inc); |
| 4437 AddInstruction(instr); |
| 4438 |
| 4439 if (expr->is_prefix()) { |
| 4440 Push(instr); |
| 4441 } else { |
| 4442 Push(value); |
| 4443 } |
| 4444 |
| 4445 if (var->is_global()) { |
| 4446 HandleGlobalVariableAssignment(proxy, instr, expr->position()); |
| 4447 } else { |
| 4448 ASSERT(var->IsStackAllocated()); |
| 4449 Bind(var, instr); |
| 4450 } |
| 4451 |
| 4452 } else if (prop != NULL) { |
| 4453 prop->RecordTypeFeedback(oracle()); |
| 4454 |
| 4455 if (prop->key()->IsPropertyName()) { |
| 4456 // Named property. |
| 4457 |
| 4458 // Match the full code generator stack by simulate an extra stack element |
| 4459 // for postfix operations in a value context. |
| 4460 if (expr->is_postfix() && !ast_context()->IsEffect()) { |
| 4461 Push(graph_->GetConstantUndefined()); |
| 4462 } |
| 4463 |
| 4464 VISIT_FOR_VALUE(prop->obj()); |
| 4465 HValue* obj = Top(); |
| 4466 |
| 4467 HInstruction* load = NULL; |
| 4468 if (prop->IsMonomorphic()) { |
| 4469 Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); |
| 4470 Handle<Map> map = prop->GetReceiverTypes()->first(); |
| 4471 load = BuildLoadNamed(obj, prop, map, name); |
| 4472 } else { |
| 4473 load = BuildLoadNamedGeneric(obj, prop); |
| 4474 } |
| 4475 PushAndAdd(load); |
| 4476 if (load->HasSideEffects()) AddSimulate(increment->id()); |
| 4477 |
| 4478 HValue* value = Pop(); |
| 4479 |
| 4480 HInstruction* instr = BuildIncrement(value, inc); |
| 4481 AddInstruction(instr); |
| 4482 |
| 4483 HInstruction* store = BuildStoreNamed(obj, instr, prop); |
| 4484 AddInstruction(store); |
| 4485 |
| 4486 // Drop simulated receiver and push the result. |
| 4487 // There is no deoptimization to after the increment, so we can simulate |
| 4488 // the expression stack here. |
| 4489 Drop(1); |
| 4490 if (expr->is_prefix()) { |
| 4491 Push(instr); |
| 4492 } else { |
| 4493 if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero. |
| 4494 Push(value); |
| 4495 } |
| 4496 |
| 4497 } else { |
| 4498 // Keyed property. |
| 4499 |
| 4500 // Match the full code generator stack by simulate an extra stack element |
| 4501 // for postfix operations in a value context. |
| 4502 if (expr->is_postfix() && !ast_context()->IsEffect()) { |
| 4503 Push(graph_->GetConstantUndefined()); |
| 4504 } |
| 4505 |
| 4506 VISIT_FOR_VALUE(prop->obj()); |
| 4507 VISIT_FOR_VALUE(prop->key()); |
| 4508 |
| 4509 HValue* obj = environment()->ExpressionStackAt(1); |
| 4510 HValue* key = environment()->ExpressionStackAt(0); |
| 4511 |
| 4512 bool is_fast_elements = prop->IsMonomorphic() && |
| 4513 prop->GetMonomorphicReceiverType()->has_fast_elements(); |
| 4514 |
| 4515 HInstruction* load = is_fast_elements |
| 4516 ? BuildLoadKeyedFastElement(obj, key, prop) |
| 4517 : BuildLoadKeyedGeneric(obj, key); |
| 4518 PushAndAdd(load); |
| 4519 if (load->HasSideEffects()) AddSimulate(increment->id()); |
| 4520 |
| 4521 HValue* value = Pop(); |
| 4522 |
| 4523 HInstruction* instr = BuildIncrement(value, inc); |
| 4524 AddInstruction(instr); |
| 4525 |
| 4526 HInstruction* store = is_fast_elements |
| 4527 ? BuildStoreKeyedFastElement(obj, key, instr, prop) |
| 4528 : new HStoreKeyedGeneric(obj, key, instr); |
| 4529 AddInstruction(store); |
| 4530 |
| 4531 // Drop simulated receiver and key and push the result. |
| 4532 // There is no deoptimization to after the increment, so we can simulate |
| 4533 // the expression stack here. |
| 4534 Drop(2); |
| 4535 if (expr->is_prefix()) { |
| 4536 Push(instr); |
| 4537 } else { |
| 4538 if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero. |
| 4539 Push(value); |
| 4540 } |
| 4541 } |
| 4542 } else { |
| 4543 BAILOUT("invalid lhs in count operation"); |
| 4544 } |
| 4545 } |
| 4546 |
| 4547 |
| 4548 HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, |
| 4549 HValue* left, |
| 4550 HValue* right) { |
| 4551 HInstruction* instr = NULL; |
| 4552 switch (expr->op()) { |
| 4553 case Token::ADD: |
| 4554 instr = new HAdd(left, right); |
| 4555 break; |
| 4556 case Token::SUB: |
| 4557 instr = new HSub(left, right); |
| 4558 break; |
| 4559 case Token::MUL: |
| 4560 instr = new HMul(left, right); |
| 4561 break; |
| 4562 case Token::MOD: |
| 4563 instr = new HMod(left, right); |
| 4564 break; |
| 4565 case Token::DIV: |
| 4566 instr = new HDiv(left, right); |
| 4567 break; |
| 4568 case Token::BIT_XOR: |
| 4569 instr = new HBitXor(left, right); |
| 4570 break; |
| 4571 case Token::BIT_AND: |
| 4572 instr = new HBitAnd(left, right); |
| 4573 break; |
| 4574 case Token::BIT_OR: |
| 4575 instr = new HBitOr(left, right); |
| 4576 break; |
| 4577 case Token::SAR: |
| 4578 instr = new HSar(left, right); |
| 4579 break; |
| 4580 case Token::SHR: |
| 4581 instr = new HShr(left, right); |
| 4582 break; |
| 4583 case Token::SHL: |
| 4584 instr = new HShl(left, right); |
| 4585 break; |
| 4586 default: |
| 4587 UNREACHABLE(); |
| 4588 } |
| 4589 TypeInfo info = oracle()->BinaryType(expr, TypeFeedbackOracle::RESULT); |
| 4590 // If we hit an uninitialized binary op stub we will get type info |
| 4591 // for a smi operation. If one of the operands is a constant string |
| 4592 // do not generate code assuming it is a smi operation. |
| 4593 if (info.IsSmi() && |
| 4594 ((left->IsConstant() && HConstant::cast(left)->HasStringValue()) || |
| 4595 (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) { |
| 4596 return instr; |
| 4597 } |
| 4598 if (FLAG_trace_representation) { |
| 4599 PrintF("Info: %s/%s\n", info.ToString(), ToRepresentation(info).Mnemonic()); |
| 4600 } |
| 4601 AssumeRepresentation(instr, ToRepresentation(info)); |
| 4602 return instr; |
| 4603 } |
| 4604 |
| 4605 |
| 4606 // Check for the form (%_ClassOf(foo) === 'BarClass'). |
| 4607 static bool IsClassOfTest(CompareOperation* expr) { |
| 4608 if (expr->op() != Token::EQ_STRICT) return false; |
| 4609 CallRuntime* call = expr->left()->AsCallRuntime(); |
| 4610 if (call == NULL) return false; |
| 4611 Literal* literal = expr->right()->AsLiteral(); |
| 4612 if (literal == NULL) return false; |
| 4613 if (!literal->handle()->IsString()) return false; |
| 4614 if (!call->name()->IsEqualTo(CStrVector("_ClassOf"))) return false; |
| 4615 ASSERT(call->arguments()->length() == 1); |
| 4616 return true; |
| 4617 } |
| 4618 |
| 4619 |
| 4620 void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
| 4621 if (expr->op() == Token::COMMA) { |
| 4622 VISIT_FOR_EFFECT(expr->left()); |
| 4623 VISIT_FOR_VALUE(expr->right()); |
| 4624 } else if (expr->op() == Token::AND || expr->op() == Token::OR) { |
| 4625 VISIT_FOR_VALUE(expr->left()); |
| 4626 ASSERT(current_subgraph_->HasExit()); |
| 4627 |
| 4628 HValue* left = Top(); |
| 4629 bool is_logical_and = (expr->op() == Token::AND); |
| 4630 |
| 4631 HEnvironment* environment_copy = environment()->Copy(); |
| 4632 environment_copy->Pop(); |
| 4633 HSubgraph* right_subgraph; |
| 4634 right_subgraph = CreateBranchSubgraph(environment_copy); |
| 4635 ADD_TO_SUBGRAPH(right_subgraph, expr->right()); |
| 4636 current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left); |
| 4637 current_subgraph_->exit_block()->SetJoinId(expr->id()); |
| 4638 } else { |
| 4639 VISIT_FOR_VALUE(expr->left()); |
| 4640 VISIT_FOR_VALUE(expr->right()); |
| 4641 |
| 4642 HValue* right = Pop(); |
| 4643 HValue* left = Pop(); |
| 4644 HInstruction* instr = BuildBinaryOperation(expr, left, right); |
| 4645 PushAndAdd(instr, expr->position()); |
| 4646 } |
| 4647 } |
| 4648 |
| 4649 |
| 4650 void HGraphBuilder::AssumeRepresentation(HValue* value, Representation r) { |
| 4651 if (value->CheckFlag(HValue::kFlexibleRepresentation)) { |
| 4652 if (FLAG_trace_representation) { |
| 4653 PrintF("Assume representation for %s to be %s (%d)\n", |
| 4654 value->Mnemonic(), |
| 4655 r.Mnemonic(), |
| 4656 graph_->GetMaximumValueID()); |
| 4657 } |
| 4658 value->ChangeRepresentation(r); |
| 4659 // The representation of the value is dictated by type feedback. |
| 4660 value->ClearFlag(HValue::kFlexibleRepresentation); |
| 4661 } else if (FLAG_trace_representation) { |
| 4662 PrintF("No representation assumed\n"); |
| 4663 } |
| 4664 } |
| 4665 |
| 4666 |
| 4667 Representation HGraphBuilder::ToRepresentation(TypeInfo info) { |
| 4668 if (info.IsSmi()) return Representation::Integer32(); |
| 4669 if (info.IsInteger32()) return Representation::Integer32(); |
| 4670 if (info.IsDouble()) return Representation::Double(); |
| 4671 if (info.IsNumber()) return Representation::Double(); |
| 4672 return Representation::Tagged(); |
| 4673 } |
| 4674 |
| 4675 |
| 4676 void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
| 4677 if (IsClassOfTest(expr)) { |
| 4678 CallRuntime* call = expr->left()->AsCallRuntime(); |
| 4679 VISIT_FOR_VALUE(call->arguments()->at(0)); |
| 4680 HValue* value = Pop(); |
| 4681 Literal* literal = expr->right()->AsLiteral(); |
| 4682 Handle<String> rhs = Handle<String>::cast(literal->handle()); |
| 4683 HInstruction* instr = new HClassOfTest(value, rhs); |
| 4684 PushAndAdd(instr, expr->position()); |
| 4685 return; |
| 4686 } |
| 4687 |
| 4688 // Check for the pattern: typeof <expression> == <string literal>. |
| 4689 UnaryOperation* left_unary = expr->left()->AsUnaryOperation(); |
| 4690 Literal* right_literal = expr->right()->AsLiteral(); |
| 4691 if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) && |
| 4692 left_unary != NULL && left_unary->op() == Token::TYPEOF && |
| 4693 right_literal != NULL && right_literal->handle()->IsString()) { |
| 4694 VISIT_FOR_VALUE(left_unary->expression()); |
| 4695 HValue* left = Pop(); |
| 4696 HInstruction* instr = new HTypeofIs(left, |
| 4697 Handle<String>::cast(right_literal->handle())); |
| 4698 PushAndAdd(instr, expr->position()); |
| 4699 return; |
| 4700 } |
| 4701 |
| 4702 VISIT_FOR_VALUE(expr->left()); |
| 4703 VISIT_FOR_VALUE(expr->right()); |
| 4704 |
| 4705 HValue* right = Pop(); |
| 4706 HValue* left = Pop(); |
| 4707 Token::Value op = expr->op(); |
| 4708 |
| 4709 TypeInfo info = oracle()->CompareType(expr, TypeFeedbackOracle::RESULT); |
| 4710 HInstruction* instr = NULL; |
| 4711 if (op == Token::INSTANCEOF) { |
| 4712 instr = new HInstanceOf(left, right); |
| 4713 } else if (op == Token::IN) { |
| 4714 BAILOUT("Unsupported comparison: in"); |
| 4715 } else if (info.IsNonPrimitive()) { |
| 4716 switch (op) { |
| 4717 case Token::EQ: |
| 4718 case Token::EQ_STRICT: { |
| 4719 AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left)); |
| 4720 AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right)); |
| 4721 instr = new HCompareJSObjectEq(left, right); |
| 4722 break; |
| 4723 } |
| 4724 default: |
| 4725 BAILOUT("Unsupported non-primitive compare"); |
| 4726 break; |
| 4727 } |
| 4728 } else { |
| 4729 HCompare* compare = new HCompare(left, right, op); |
| 4730 Representation r = ToRepresentation(info); |
| 4731 compare->SetInputRepresentation(r); |
| 4732 instr = compare; |
| 4733 } |
| 4734 PushAndAdd(instr, expr->position()); |
| 4735 } |
| 4736 |
| 4737 |
| 4738 void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { |
| 4739 VISIT_FOR_VALUE(expr->expression()); |
| 4740 |
| 4741 HValue* value = Pop(); |
| 4742 HIsNull* compare = new HIsNull(value, expr->is_strict()); |
| 4743 |
| 4744 PushAndAdd(compare); |
| 4745 } |
| 4746 |
| 4747 |
| 4748 void HGraphBuilder::VisitThisFunction(ThisFunction* expr) { |
| 4749 BAILOUT("ThisFunction"); |
| 4750 } |
| 4751 |
| 4752 |
| 4753 void HGraphBuilder::VisitDeclaration(Declaration* decl) { |
| 4754 // We allow only declarations that do not require code generation. |
| 4755 // The following all require code generation: global variables and |
| 4756 // functions, variables with slot type LOOKUP, declarations with |
| 4757 // mode CONST, and functions. |
| 4758 Variable* var = decl->proxy()->var(); |
| 4759 Slot* slot = var->AsSlot(); |
| 4760 if (var->is_global() || |
| 4761 (slot != NULL && slot->type() == Slot::LOOKUP) || |
| 4762 decl->mode() == Variable::CONST || |
| 4763 decl->fun() != NULL) { |
| 4764 BAILOUT("unsupported declaration"); |
| 4765 } |
| 4766 } |
| 4767 |
| 4768 |
| 4769 // Generators for inline runtime functions. |
| 4770 // Support for types. |
| 4771 void HGraphBuilder::GenerateIsSmi(int argument_count) { |
| 4772 ASSERT(argument_count == 1); |
| 4773 |
| 4774 HValue* value = Pop(); |
| 4775 PushAndAdd(new HIsSmi(value)); |
| 4776 } |
| 4777 |
| 4778 |
| 4779 void HGraphBuilder::GenerateIsSpecObject(int argument_count) { |
| 4780 ASSERT(argument_count == 1); |
| 4781 |
| 4782 HValue* value = Pop(); |
| 4783 HHasInstanceType* test = |
| 4784 new HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE); |
| 4785 PushAndAdd(test); |
| 4786 } |
| 4787 |
| 4788 |
| 4789 void HGraphBuilder::GenerateIsFunction(int argument_count) { |
| 4790 ASSERT(argument_count == 1); |
| 4791 |
| 4792 HValue* value = Pop(); |
| 4793 HHasInstanceType* test = |
| 4794 new HHasInstanceType(value, JS_FUNCTION_TYPE); |
| 4795 PushAndAdd(test); |
| 4796 } |
| 4797 |
| 4798 |
| 4799 void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count) { |
| 4800 ASSERT(argument_count == 1); |
| 4801 |
| 4802 HValue* value = Pop(); |
| 4803 HHasCachedArrayIndex* spec_test = new HHasCachedArrayIndex(value); |
| 4804 PushAndAdd(spec_test); |
| 4805 } |
| 4806 |
| 4807 |
| 4808 void HGraphBuilder::GenerateIsArray(int argument_count) { |
| 4809 ASSERT(argument_count == 1); |
| 4810 |
| 4811 HValue* value = Pop(); |
| 4812 HHasInstanceType* test = |
| 4813 new HHasInstanceType(value, JS_ARRAY_TYPE); |
| 4814 PushAndAdd(test); |
| 4815 } |
| 4816 |
| 4817 |
| 4818 void HGraphBuilder::GenerateIsRegExp(int argument_count) { |
| 4819 ASSERT(argument_count == 1); |
| 4820 |
| 4821 HValue* value = Pop(); |
| 4822 HHasInstanceType* test = |
| 4823 new HHasInstanceType(value, JS_REGEXP_TYPE); |
| 4824 PushAndAdd(test); |
| 4825 } |
| 4826 |
| 4827 |
| 4828 void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count) { |
| 4829 BAILOUT("inlined runtime function: IsNonNegativeSmi"); |
| 4830 } |
| 4831 |
| 4832 |
| 4833 void HGraphBuilder::GenerateIsObject(int argument_count) { |
| 4834 BAILOUT("inlined runtime function: IsObject"); |
| 4835 } |
| 4836 |
| 4837 |
| 4838 void HGraphBuilder::GenerateIsUndetectableObject(int argument_count) { |
| 4839 BAILOUT("inlined runtime function: IsUndetectableObject"); |
| 4840 } |
| 4841 |
| 4842 |
| 4843 void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf( |
| 4844 int argument_count) { |
| 4845 BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf"); |
| 4846 } |
| 4847 |
| 4848 |
| 4849 // Support for construct call checks. |
| 4850 void HGraphBuilder::GenerateIsConstructCall(int argument_count) { |
| 4851 BAILOUT("inlined runtime function: IsConstructCall"); |
| 4852 } |
| 4853 |
| 4854 |
| 4855 // Support for arguments.length and arguments[?]. |
| 4856 void HGraphBuilder::GenerateArgumentsLength(int argument_count) { |
| 4857 ASSERT(argument_count == 0); |
| 4858 HInstruction* elements = AddInstruction(new HArgumentsElements); |
| 4859 PushAndAdd(new HArgumentsLength(elements)); |
| 4860 } |
| 4861 |
| 4862 |
| 4863 void HGraphBuilder::GenerateArguments(int argument_count) { |
| 4864 ASSERT(argument_count == 1); |
| 4865 HValue* index = Pop(); |
| 4866 HInstruction* elements = AddInstruction(new HArgumentsElements); |
| 4867 HInstruction* length = AddInstruction(new HArgumentsLength(elements)); |
| 4868 PushAndAdd(new HAccessArgumentsAt(elements, length, index)); |
| 4869 } |
| 4870 |
| 4871 |
| 4872 // Support for accessing the class and value fields of an object. |
| 4873 void HGraphBuilder::GenerateClassOf(int argument_count) { |
| 4874 // The special form detected by IsClassOfTest is detected before we get here |
| 4875 // and does not cause a bailout. |
| 4876 BAILOUT("inlined runtime function: ClassOf"); |
| 4877 } |
| 4878 |
| 4879 |
| 4880 void HGraphBuilder::GenerateValueOf(int argument_count) { |
| 4881 ASSERT(argument_count == 1); |
| 4882 |
| 4883 HValue* value = Pop(); |
| 4884 HValueOf* op = new HValueOf(value); |
| 4885 PushAndAdd(op); |
| 4886 } |
| 4887 |
| 4888 |
| 4889 void HGraphBuilder::GenerateSetValueOf(int argument_count) { |
| 4890 BAILOUT("inlined runtime function: SetValueOf"); |
| 4891 } |
| 4892 |
| 4893 |
| 4894 // Fast support for charCodeAt(n). |
| 4895 void HGraphBuilder::GenerateStringCharCodeAt(int argument_count) { |
| 4896 BAILOUT("inlined runtime function: StringCharCodeAt"); |
| 4897 } |
| 4898 |
| 4899 |
| 4900 // Fast support for string.charAt(n) and string[n]. |
| 4901 void HGraphBuilder::GenerateStringCharFromCode(int argument_count) { |
| 4902 BAILOUT("inlined runtime function: StringCharFromCode"); |
| 4903 } |
| 4904 |
| 4905 |
| 4906 // Fast support for string.charAt(n) and string[n]. |
| 4907 void HGraphBuilder::GenerateStringCharAt(int argument_count) { |
| 4908 ASSERT_EQ(2, argument_count); |
| 4909 PushArgumentsForStubCall(argument_count); |
| 4910 PushAndAdd(new HCallStub(CodeStub::StringCharAt, argument_count), |
| 4911 RelocInfo::kNoPosition); |
| 4912 } |
| 4913 |
| 4914 |
| 4915 // Fast support for object equality testing. |
| 4916 void HGraphBuilder::GenerateObjectEquals(int argument_count) { |
| 4917 ASSERT(argument_count == 2); |
| 4918 |
| 4919 HValue* right = Pop(); |
| 4920 HValue* left = Pop(); |
| 4921 PushAndAdd(new HCompareJSObjectEq(left, right)); |
| 4922 } |
| 4923 |
| 4924 |
| 4925 void HGraphBuilder::GenerateLog(int argument_count) { |
| 4926 UNREACHABLE(); // We caught this in VisitCallRuntime. |
| 4927 } |
| 4928 |
| 4929 |
| 4930 // Fast support for Math.random(). |
| 4931 void HGraphBuilder::GenerateRandomHeapNumber(int argument_count) { |
| 4932 BAILOUT("inlined runtime function: RandomHeapNumber"); |
| 4933 } |
| 4934 |
| 4935 |
| 4936 // Fast support for StringAdd. |
| 4937 void HGraphBuilder::GenerateStringAdd(int argument_count) { |
| 4938 ASSERT_EQ(2, argument_count); |
| 4939 PushArgumentsForStubCall(argument_count); |
| 4940 PushAndAdd(new HCallStub(CodeStub::StringAdd, argument_count), |
| 4941 RelocInfo::kNoPosition); |
| 4942 } |
| 4943 |
| 4944 |
| 4945 // Fast support for SubString. |
| 4946 void HGraphBuilder::GenerateSubString(int argument_count) { |
| 4947 ASSERT_EQ(3, argument_count); |
| 4948 PushArgumentsForStubCall(argument_count); |
| 4949 PushAndAdd(new HCallStub(CodeStub::SubString, argument_count), |
| 4950 RelocInfo::kNoPosition); |
| 4951 } |
| 4952 |
| 4953 |
| 4954 // Fast support for StringCompare. |
| 4955 void HGraphBuilder::GenerateStringCompare(int argument_count) { |
| 4956 ASSERT_EQ(2, argument_count); |
| 4957 PushArgumentsForStubCall(argument_count); |
| 4958 PushAndAdd(new HCallStub(CodeStub::StringCompare, argument_count), |
| 4959 RelocInfo::kNoPosition); |
| 4960 } |
| 4961 |
| 4962 |
| 4963 // Support for direct calls from JavaScript to native RegExp code. |
| 4964 void HGraphBuilder::GenerateRegExpExec(int argument_count) { |
| 4965 ASSERT_EQ(4, argument_count); |
| 4966 PushArgumentsForStubCall(argument_count); |
| 4967 PushAndAdd(new HCallStub(CodeStub::RegExpExec, argument_count), |
| 4968 RelocInfo::kNoPosition); |
| 4969 } |
| 4970 |
| 4971 |
| 4972 // Construct a RegExp exec result with two in-object properties. |
| 4973 void HGraphBuilder::GenerateRegExpConstructResult(int argument_count) { |
| 4974 ASSERT_EQ(3, argument_count); |
| 4975 PushArgumentsForStubCall(argument_count); |
| 4976 PushAndAdd(new HCallStub(CodeStub::RegExpConstructResult, argument_count), |
| 4977 RelocInfo::kNoPosition); |
| 4978 } |
| 4979 |
| 4980 |
| 4981 // Support for fast native caches. |
| 4982 void HGraphBuilder::GenerateGetFromCache(int argument_count) { |
| 4983 BAILOUT("inlined runtime function: GetFromCache"); |
| 4984 } |
| 4985 |
| 4986 |
| 4987 // Fast support for number to string. |
| 4988 void HGraphBuilder::GenerateNumberToString(int argument_count) { |
| 4989 ASSERT_EQ(1, argument_count); |
| 4990 PushArgumentsForStubCall(argument_count); |
| 4991 PushAndAdd(new HCallStub(CodeStub::NumberToString, argument_count), |
| 4992 RelocInfo::kNoPosition); |
| 4993 } |
| 4994 |
| 4995 |
| 4996 // Fast swapping of elements. Takes three expressions, the object and two |
| 4997 // indices. This should only be used if the indices are known to be |
| 4998 // non-negative and within bounds of the elements array at the call site. |
| 4999 void HGraphBuilder::GenerateSwapElements(int argument_count) { |
| 5000 BAILOUT("inlined runtime function: SwapElements"); |
| 5001 } |
| 5002 |
| 5003 |
| 5004 // Fast call for custom callbacks. |
| 5005 void HGraphBuilder::GenerateCallFunction(int argument_count) { |
| 5006 BAILOUT("inlined runtime function: CallFunction"); |
| 5007 } |
| 5008 |
| 5009 |
| 5010 // Fast call to math functions. |
| 5011 void HGraphBuilder::GenerateMathPow(int argument_count) { |
| 5012 ASSERT_EQ(2, argument_count); |
| 5013 PushArgumentsForStubCall(argument_count); |
| 5014 PushAndAdd(new HCallStub(CodeStub::MathPow, argument_count), |
| 5015 RelocInfo::kNoPosition); |
| 5016 } |
| 5017 |
| 5018 |
| 5019 void HGraphBuilder::GenerateMathSin(int argument_count) { |
| 5020 ASSERT_EQ(1, argument_count); |
| 5021 PushArgumentsForStubCall(argument_count); |
| 5022 HCallStub* instr = |
| 5023 new HCallStub(CodeStub::TranscendentalCache, argument_count); |
| 5024 instr->set_transcendental_type(TranscendentalCache::SIN); |
| 5025 PushAndAdd(instr, RelocInfo::kNoPosition); |
| 5026 } |
| 5027 |
| 5028 |
| 5029 void HGraphBuilder::GenerateMathCos(int argument_count) { |
| 5030 ASSERT_EQ(1, argument_count); |
| 5031 PushArgumentsForStubCall(argument_count); |
| 5032 HCallStub* instr = |
| 5033 new HCallStub(CodeStub::TranscendentalCache, argument_count); |
| 5034 instr->set_transcendental_type(TranscendentalCache::COS); |
| 5035 PushAndAdd(instr, RelocInfo::kNoPosition); |
| 5036 } |
| 5037 |
| 5038 |
| 5039 void HGraphBuilder::GenerateMathLog(int argument_count) { |
| 5040 ASSERT_EQ(1, argument_count); |
| 5041 PushArgumentsForStubCall(argument_count); |
| 5042 HCallStub* instr = |
| 5043 new HCallStub(CodeStub::TranscendentalCache, argument_count); |
| 5044 instr->set_transcendental_type(TranscendentalCache::LOG); |
| 5045 PushAndAdd(instr, RelocInfo::kNoPosition); |
| 5046 } |
| 5047 |
| 5048 |
| 5049 void HGraphBuilder::GenerateMathSqrt(int argument_count) { |
| 5050 BAILOUT("inlined runtime function: MathSqrt"); |
| 5051 } |
| 5052 |
| 5053 |
| 5054 // Check whether two RegExps are equivalent |
| 5055 void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count) { |
| 5056 BAILOUT("inlined runtime function: IsRegExpEquivalent"); |
| 5057 } |
| 5058 |
| 5059 |
| 5060 void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count) { |
| 5061 BAILOUT("inlined runtime function: GetCachedArrayIndex"); |
| 5062 } |
| 5063 |
| 5064 |
| 5065 void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count) { |
| 5066 BAILOUT("inlined runtime function: FastAsciiArrayJoin"); |
| 5067 } |
| 5068 |
| 5069 |
| 5070 #undef BAILOUT |
| 5071 #undef CHECK_BAILOUT |
| 5072 #undef VISIT_FOR_EFFECT |
| 5073 #undef VISIT_FOR_VALUE |
| 5074 #undef ADD_TO_SUBGRAPH |
| 5075 |
| 5076 |
| 5077 HEnvironment::HEnvironment(HEnvironment* outer, |
| 5078 Scope* scope, |
| 5079 Handle<JSFunction> closure) |
| 5080 : closure_(closure), |
| 5081 values_(0), |
| 5082 assigned_variables_(4), |
| 5083 parameter_count_(0), |
| 5084 local_count_(0), |
| 5085 outer_(outer), |
| 5086 pop_count_(0), |
| 5087 push_count_(0), |
| 5088 ast_id_(AstNode::kNoNumber) { |
| 5089 Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0); |
| 5090 } |
| 5091 |
| 5092 |
| 5093 HEnvironment::HEnvironment(const HEnvironment* other) |
| 5094 : values_(0), |
| 5095 assigned_variables_(0), |
| 5096 parameter_count_(0), |
| 5097 local_count_(0), |
| 5098 outer_(NULL), |
| 5099 pop_count_(0), |
| 5100 push_count_(0), |
| 5101 ast_id_(other->ast_id()) { |
| 5102 Initialize(other); |
| 5103 } |
| 5104 |
| 5105 |
| 5106 void HEnvironment::Initialize(int parameter_count, |
| 5107 int local_count, |
| 5108 int stack_height) { |
| 5109 parameter_count_ = parameter_count; |
| 5110 local_count_ = local_count; |
| 5111 |
| 5112 // Avoid reallocating the temporaries' backing store on the first Push. |
| 5113 int total = parameter_count + local_count + stack_height; |
| 5114 values_.Initialize(total + 4); |
| 5115 for (int i = 0; i < total; ++i) values_.Add(NULL); |
| 5116 } |
| 5117 |
| 5118 |
| 5119 void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) { |
| 5120 ASSERT(!block->IsLoopHeader()); |
| 5121 ASSERT(values_.length() == other->values_.length()); |
| 5122 |
| 5123 int length = values_.length(); |
| 5124 for (int i = 0; i < length; ++i) { |
| 5125 HValue* value = values_[i]; |
| 5126 if (value != NULL && value->IsPhi() && value->block() == block) { |
| 5127 // There is already a phi for the i'th value. |
| 5128 HPhi* phi = HPhi::cast(value); |
| 5129 // Assert index is correct and that we haven't missed an incoming edge. |
| 5130 ASSERT(phi->merged_index() == i); |
| 5131 ASSERT(phi->OperandCount() == block->predecessors()->length()); |
| 5132 phi->AddInput(other->values_[i]); |
| 5133 } else if (values_[i] != other->values_[i]) { |
| 5134 // There is a fresh value on the incoming edge, a phi is needed. |
| 5135 ASSERT(values_[i] != NULL && other->values_[i] != NULL); |
| 5136 HPhi* phi = new HPhi(i); |
| 5137 HValue* old_value = values_[i]; |
| 5138 for (int j = 0; j < block->predecessors()->length(); j++) { |
| 5139 phi->AddInput(old_value); |
| 5140 } |
| 5141 phi->AddInput(other->values_[i]); |
| 5142 this->values_[i] = phi; |
| 5143 block->AddPhi(phi); |
| 5144 } |
| 5145 } |
| 5146 } |
| 5147 |
| 5148 |
| 5149 void HEnvironment::Initialize(const HEnvironment* other) { |
| 5150 closure_ = other->closure(); |
| 5151 values_.AddAll(other->values_); |
| 5152 assigned_variables_.AddAll(other->assigned_variables_); |
| 5153 parameter_count_ = other->parameter_count_; |
| 5154 local_count_ = other->local_count_; |
| 5155 if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy. |
| 5156 pop_count_ = other->pop_count_; |
| 5157 push_count_ = other->push_count_; |
| 5158 ast_id_ = other->ast_id_; |
| 5159 } |
| 5160 |
| 5161 |
| 5162 int HEnvironment::IndexFor(Variable* variable) const { |
| 5163 Slot* slot = variable->AsSlot(); |
| 5164 ASSERT(slot != NULL && slot->IsStackAllocated()); |
| 5165 if (slot->type() == Slot::PARAMETER) { |
| 5166 return slot->index() + 1; |
| 5167 } else { |
| 5168 return parameter_count_ + slot->index(); |
| 5169 } |
| 5170 } |
| 5171 |
| 5172 |
| 5173 HEnvironment* HEnvironment::Copy() const { |
| 5174 return new HEnvironment(this); |
| 5175 } |
| 5176 |
| 5177 |
| 5178 HEnvironment* HEnvironment::CopyWithoutHistory() const { |
| 5179 HEnvironment* result = Copy(); |
| 5180 result->ClearHistory(); |
| 5181 return result; |
| 5182 } |
| 5183 |
| 5184 |
| 5185 HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const { |
| 5186 HEnvironment* new_env = Copy(); |
| 5187 for (int i = 0; i < values_.length(); ++i) { |
| 5188 HPhi* phi = new HPhi(i); |
| 5189 phi->AddInput(values_[i]); |
| 5190 new_env->values_[i] = phi; |
| 5191 loop_header->AddPhi(phi); |
| 5192 } |
| 5193 new_env->ClearHistory(); |
| 5194 return new_env; |
| 5195 } |
| 5196 |
| 5197 |
| 5198 HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target, |
| 5199 FunctionLiteral* function, |
| 5200 bool is_speculative, |
| 5201 HConstant* undefined) const { |
| 5202 // Outer environment is a copy of this one without the arguments. |
| 5203 int arity = function->scope()->num_parameters(); |
| 5204 HEnvironment* outer = Copy(); |
| 5205 outer->Drop(arity + 1); // Including receiver. |
| 5206 outer->ClearHistory(); |
| 5207 HEnvironment* inner = new HEnvironment(outer, function->scope(), target); |
| 5208 // Get the argument values from the original environment. |
| 5209 if (is_speculative) { |
| 5210 for (int i = 0; i <= arity; ++i) { // Include receiver. |
| 5211 HValue* push = ExpressionStackAt(arity - i); |
| 5212 inner->SetValueAt(i, push); |
| 5213 } |
| 5214 } else { |
| 5215 for (int i = 0; i <= arity; ++i) { // Include receiver. |
| 5216 inner->SetValueAt(i, ExpressionStackAt(arity - i)); |
| 5217 } |
| 5218 } |
| 5219 |
| 5220 // Initialize the stack-allocated locals to undefined. |
| 5221 int local_base = arity + 1; |
| 5222 int local_count = function->scope()->num_stack_slots(); |
| 5223 for (int i = 0; i < local_count; ++i) { |
| 5224 inner->SetValueAt(local_base + i, undefined); |
| 5225 } |
| 5226 |
| 5227 inner->set_ast_id(function->id()); |
| 5228 return inner; |
| 5229 } |
| 5230 |
| 5231 |
| 5232 void HEnvironment::PrintTo(StringStream* stream) { |
| 5233 for (int i = 0; i < total_count(); i++) { |
| 5234 if (i == 0) stream->Add("parameters\n"); |
| 5235 if (i == parameter_count()) stream->Add("locals\n"); |
| 5236 if (i == parameter_count() + local_count()) stream->Add("expressions"); |
| 5237 HValue* val = values_.at(i); |
| 5238 stream->Add("%d: ", i); |
| 5239 if (val != NULL) { |
| 5240 val->PrintNameTo(stream); |
| 5241 } else { |
| 5242 stream->Add("NULL"); |
| 5243 } |
| 5244 stream->Add("\n"); |
| 5245 } |
| 5246 } |
| 5247 |
| 5248 |
| 5249 void HEnvironment::PrintToStd() { |
| 5250 HeapStringAllocator string_allocator; |
| 5251 StringStream trace(&string_allocator); |
| 5252 PrintTo(&trace); |
| 5253 PrintF("%s", *trace.ToCString()); |
| 5254 } |
| 5255 |
| 5256 |
| 5257 void HTracer::TraceCompilation(FunctionLiteral* function) { |
| 5258 Tag tag(this, "compilation"); |
| 5259 Handle<String> name = function->debug_name(); |
| 5260 PrintStringProperty("name", *name->ToCString()); |
| 5261 PrintStringProperty("method", *name->ToCString()); |
| 5262 PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis())); |
| 5263 } |
| 5264 |
| 5265 |
| 5266 void HTracer::TraceLithium(const char* name, LChunk* chunk) { |
| 5267 Trace(name, chunk->graph(), chunk); |
| 5268 } |
| 5269 |
| 5270 |
| 5271 void HTracer::TraceHydrogen(const char* name, HGraph* graph) { |
| 5272 Trace(name, graph, NULL); |
| 5273 } |
| 5274 |
| 5275 |
| 5276 void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) { |
| 5277 Tag tag(this, "cfg"); |
| 5278 PrintStringProperty("name", name); |
| 5279 const ZoneList<HBasicBlock*>* blocks = graph->blocks(); |
| 5280 for (int i = 0; i < blocks->length(); i++) { |
| 5281 HBasicBlock* current = blocks->at(i); |
| 5282 Tag block_tag(this, "block"); |
| 5283 PrintBlockProperty("name", current->block_id()); |
| 5284 PrintIntProperty("from_bci", -1); |
| 5285 PrintIntProperty("to_bci", -1); |
| 5286 |
| 5287 if (!current->predecessors()->is_empty()) { |
| 5288 PrintIndent(); |
| 5289 trace_.Add("predecessors"); |
| 5290 for (int j = 0; j < current->predecessors()->length(); ++j) { |
| 5291 trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id()); |
| 5292 } |
| 5293 trace_.Add("\n"); |
| 5294 } else { |
| 5295 PrintEmptyProperty("predecessors"); |
| 5296 } |
| 5297 |
| 5298 if (current->end() == NULL || current->end()->FirstSuccessor() == NULL) { |
| 5299 PrintEmptyProperty("successors"); |
| 5300 } else if (current->end()->SecondSuccessor() == NULL) { |
| 5301 PrintBlockProperty("successors", |
| 5302 current->end()->FirstSuccessor()->block_id()); |
| 5303 } else { |
| 5304 PrintBlockProperty("successors", |
| 5305 current->end()->FirstSuccessor()->block_id(), |
| 5306 current->end()->SecondSuccessor()->block_id()); |
| 5307 } |
| 5308 |
| 5309 PrintEmptyProperty("xhandlers"); |
| 5310 PrintEmptyProperty("flags"); |
| 5311 |
| 5312 if (current->dominator() != NULL) { |
| 5313 PrintBlockProperty("dominator", current->dominator()->block_id()); |
| 5314 } |
| 5315 |
| 5316 if (chunk != NULL) { |
| 5317 int first_index = current->first_instruction_index(); |
| 5318 int last_index = current->last_instruction_index(); |
| 5319 PrintIntProperty( |
| 5320 "first_lir_id", |
| 5321 LifetimePosition::FromInstructionIndex(first_index).Value()); |
| 5322 PrintIntProperty( |
| 5323 "last_lir_id", |
| 5324 LifetimePosition::FromInstructionIndex(last_index).Value()); |
| 5325 } |
| 5326 |
| 5327 { |
| 5328 Tag states_tag(this, "states"); |
| 5329 Tag locals_tag(this, "locals"); |
| 5330 int total = current->phis()->length(); |
| 5331 trace_.Add("size %d\n", total); |
| 5332 trace_.Add("method \"None\""); |
| 5333 for (int j = 0; j < total; ++j) { |
| 5334 HPhi* phi = current->phis()->at(j); |
| 5335 trace_.Add("%d ", phi->merged_index()); |
| 5336 phi->PrintNameTo(&trace_); |
| 5337 trace_.Add(" "); |
| 5338 phi->PrintTo(&trace_); |
| 5339 trace_.Add("\n"); |
| 5340 } |
| 5341 } |
| 5342 |
| 5343 { |
| 5344 Tag HIR_tag(this, "HIR"); |
| 5345 HInstruction* instruction = current->first(); |
| 5346 while (instruction != NULL) { |
| 5347 int bci = 0; |
| 5348 int uses = instruction->uses()->length(); |
| 5349 trace_.Add("%d %d ", bci, uses); |
| 5350 instruction->PrintNameTo(&trace_); |
| 5351 trace_.Add(" "); |
| 5352 instruction->PrintTo(&trace_); |
| 5353 trace_.Add(" <|@\n"); |
| 5354 instruction = instruction->next(); |
| 5355 } |
| 5356 } |
| 5357 |
| 5358 |
| 5359 if (chunk != NULL) { |
| 5360 Tag LIR_tag(this, "LIR"); |
| 5361 int first_index = current->first_instruction_index(); |
| 5362 int last_index = current->last_instruction_index(); |
| 5363 if (first_index != -1 && last_index != -1) { |
| 5364 const ZoneList<LInstruction*>* instructions = chunk->instructions(); |
| 5365 for (int i = first_index; i <= last_index; ++i) { |
| 5366 LInstruction* linstr = instructions->at(i); |
| 5367 if (linstr != NULL) { |
| 5368 trace_.Add("%d ", |
| 5369 LifetimePosition::FromInstructionIndex(i).Value()); |
| 5370 linstr->PrintTo(&trace_); |
| 5371 trace_.Add(" <|@\n"); |
| 5372 } |
| 5373 } |
| 5374 } |
| 5375 } |
| 5376 } |
| 5377 } |
| 5378 |
| 5379 |
| 5380 void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) { |
| 5381 Tag tag(this, "intervals"); |
| 5382 PrintStringProperty("name", name); |
| 5383 |
| 5384 const ZoneList<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges(); |
| 5385 for (int i = 0; i < fixed_d->length(); ++i) { |
| 5386 TraceLiveRange(fixed_d->at(i), "fixed"); |
| 5387 } |
| 5388 |
| 5389 const ZoneList<LiveRange*>* fixed = allocator->fixed_live_ranges(); |
| 5390 for (int i = 0; i < fixed->length(); ++i) { |
| 5391 TraceLiveRange(fixed->at(i), "fixed"); |
| 5392 } |
| 5393 |
| 5394 const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges(); |
| 5395 for (int i = 0; i < live_ranges->length(); ++i) { |
| 5396 TraceLiveRange(live_ranges->at(i), "object"); |
| 5397 } |
| 5398 } |
| 5399 |
| 5400 |
| 5401 void HTracer::TraceLiveRange(LiveRange* range, const char* type) { |
| 5402 if (range != NULL && !range->IsEmpty()) { |
| 5403 trace_.Add("%d %s", range->id(), type); |
| 5404 if (range->HasRegisterAssigned()) { |
| 5405 LOperand* op = range->CreateAssignedOperand(); |
| 5406 int assigned_reg = op->index(); |
| 5407 if (op->IsDoubleRegister()) { |
| 5408 trace_.Add(" \"%s\"", |
| 5409 DoubleRegister::AllocationIndexToString(assigned_reg)); |
| 5410 } else { |
| 5411 ASSERT(op->IsRegister()); |
| 5412 trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg)); |
| 5413 } |
| 5414 } else if (range->IsSpilled()) { |
| 5415 LOperand* op = range->TopLevel()->GetSpillOperand(); |
| 5416 if (op->IsDoubleStackSlot()) { |
| 5417 trace_.Add(" \"double_stack:%d\"", op->index()); |
| 5418 } else { |
| 5419 ASSERT(op->IsStackSlot()); |
| 5420 trace_.Add(" \"stack:%d\"", op->index()); |
| 5421 } |
| 5422 } |
| 5423 int parent_index = -1; |
| 5424 if (range->IsChild()) { |
| 5425 parent_index = range->parent()->id(); |
| 5426 } else { |
| 5427 parent_index = range->id(); |
| 5428 } |
| 5429 LOperand* op = range->FirstHint(); |
| 5430 int hint_index = -1; |
| 5431 if (op != NULL && op->IsUnallocated()) hint_index = op->VirtualRegister(); |
| 5432 trace_.Add(" %d %d", parent_index, hint_index); |
| 5433 UseInterval* cur_interval = range->first_interval(); |
| 5434 while (cur_interval != NULL) { |
| 5435 trace_.Add(" [%d, %d[", |
| 5436 cur_interval->start().Value(), |
| 5437 cur_interval->end().Value()); |
| 5438 cur_interval = cur_interval->next(); |
| 5439 } |
| 5440 |
| 5441 UsePosition* current_pos = range->first_pos(); |
| 5442 while (current_pos != NULL) { |
| 5443 if (current_pos->RegisterIsBeneficial()) { |
| 5444 trace_.Add(" %d M", current_pos->pos().Value()); |
| 5445 } |
| 5446 current_pos = current_pos->next(); |
| 5447 } |
| 5448 |
| 5449 trace_.Add(" \"\"\n"); |
| 5450 } |
| 5451 } |
| 5452 |
| 5453 |
| 5454 void HTracer::FlushToFile() { |
| 5455 AppendChars(filename_, *trace_.ToCString(), trace_.length(), false); |
| 5456 trace_.Reset(); |
| 5457 } |
| 5458 |
| 5459 |
| 5460 void HStatistics::Print() { |
| 5461 PrintF("Timing results:\n"); |
| 5462 int64_t sum = 0; |
| 5463 for (int i = 0; i < timing_.length(); ++i) { |
| 5464 sum += timing_[i]; |
| 5465 } |
| 5466 |
| 5467 for (int i = 0; i < names_.length(); ++i) { |
| 5468 PrintF("%30s", names_[i]); |
| 5469 double ms = static_cast<double>(timing_[i]) / 1000; |
| 5470 double percent = static_cast<double>(timing_[i]) * 100 / sum; |
| 5471 PrintF(" - %0.3f ms / %0.3f %% \n", ms, percent); |
| 5472 } |
| 5473 PrintF("%30s - %0.3f ms \n", "Sum", static_cast<double>(sum) / 1000); |
| 5474 PrintF("---------------------------------------------------------------\n"); |
| 5475 PrintF("%30s - %0.3f ms (%0.1f times slower than full code gen)\n", |
| 5476 "Total", |
| 5477 static_cast<double>(total_) / 1000, |
| 5478 static_cast<double>(total_) / full_code_gen_); |
| 5479 } |
| 5480 |
| 5481 |
| 5482 void HStatistics::SaveTiming(const char* name, int64_t ticks) { |
| 5483 if (name == HPhase::kFullCodeGen) { |
| 5484 full_code_gen_ += ticks; |
| 5485 } else if (name == HPhase::kTotal) { |
| 5486 total_ += ticks; |
| 5487 } else { |
| 5488 for (int i = 0; i < names_.length(); ++i) { |
| 5489 if (names_[i] == name) { |
| 5490 timing_[i] += ticks; |
| 5491 return; |
| 5492 } |
| 5493 } |
| 5494 names_.Add(name); |
| 5495 timing_.Add(ticks); |
| 5496 } |
| 5497 } |
| 5498 |
| 5499 |
| 5500 const char* const HPhase::kFullCodeGen = "Full code generator"; |
| 5501 const char* const HPhase::kTotal = "Total"; |
| 5502 |
| 5503 |
| 5504 void HPhase::Begin(const char* name, |
| 5505 HGraph* graph, |
| 5506 LChunk* chunk, |
| 5507 LAllocator* allocator) { |
| 5508 name_ = name; |
| 5509 graph_ = graph; |
| 5510 chunk_ = chunk; |
| 5511 allocator_ = allocator; |
| 5512 if (allocator != NULL && chunk_ == NULL) { |
| 5513 chunk_ = allocator->chunk(); |
| 5514 } |
| 5515 if (FLAG_time_hydrogen) start_ = OS::Ticks(); |
| 5516 } |
| 5517 |
| 5518 |
| 5519 void HPhase::End() const { |
| 5520 if (FLAG_time_hydrogen) { |
| 5521 int64_t end = OS::Ticks(); |
| 5522 HStatistics::Instance()->SaveTiming(name_, end - start_); |
| 5523 } |
| 5524 |
| 5525 if (FLAG_trace_hydrogen) { |
| 5526 if (graph_ != NULL) HTracer::Instance()->TraceHydrogen(name_, graph_); |
| 5527 if (chunk_ != NULL) HTracer::Instance()->TraceLithium(name_, chunk_); |
| 5528 if (allocator_ != NULL) { |
| 5529 HTracer::Instance()->TraceLiveRanges(name_, allocator_); |
| 5530 } |
| 5531 } |
| 5532 |
| 5533 #ifdef DEBUG |
| 5534 if (graph_ != NULL) graph_->Verify(); |
| 5535 if (chunk_ != NULL) chunk_->Verify(); |
| 5536 if (allocator_ != NULL) allocator_->Verify(); |
| 5537 #endif |
| 5538 } |
| 5539 |
| 5540 } } // namespace v8::internal |
| OLD | NEW |