1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/crankshaft/lithium.h" 6 7 #include "src/ast/scopes.h" 8 #include "src/codegen.h" 9 #include "src/objects-inl.h" 10 11 #if V8_TARGET_ARCH_IA32 12 #include "src/crankshaft/ia32/lithium-ia32.h" // NOLINT 13 #include "src/crankshaft/ia32/lithium-codegen-ia32.h" // NOLINT 14 #elif V8_TARGET_ARCH_X64 15 #include "src/crankshaft/x64/lithium-x64.h" // NOLINT 16 #include "src/crankshaft/x64/lithium-codegen-x64.h" // NOLINT 17 #elif V8_TARGET_ARCH_ARM 18 #include "src/crankshaft/arm/lithium-arm.h" // NOLINT 19 #include "src/crankshaft/arm/lithium-codegen-arm.h" // NOLINT 20 #elif V8_TARGET_ARCH_PPC 21 #include "src/crankshaft/ppc/lithium-ppc.h" // NOLINT 22 #include "src/crankshaft/ppc/lithium-codegen-ppc.h" // NOLINT 23 #elif V8_TARGET_ARCH_MIPS 24 #include "src/crankshaft/mips/lithium-mips.h" // NOLINT 25 #include "src/crankshaft/mips/lithium-codegen-mips.h" // NOLINT 26 #elif V8_TARGET_ARCH_ARM64 27 #include "src/crankshaft/arm64/lithium-arm64.h" // NOLINT 28 #include "src/crankshaft/arm64/lithium-codegen-arm64.h" // NOLINT 29 #elif V8_TARGET_ARCH_MIPS64 30 #include "src/crankshaft/mips64/lithium-mips64.h" // NOLINT 31 #include "src/crankshaft/mips64/lithium-codegen-mips64.h" // NOLINT 32 #elif V8_TARGET_ARCH_X87 33 #include "src/crankshaft/x87/lithium-x87.h" // NOLINT 34 #include "src/crankshaft/x87/lithium-codegen-x87.h" // NOLINT 35 #elif V8_TARGET_ARCH_S390 36 #include "src/crankshaft/s390/lithium-s390.h" // NOLINT 37 #include "src/crankshaft/s390/lithium-codegen-s390.h" // NOLINT 38 #else 39 #error "Unknown architecture." 40 #endif 41 42 namespace v8 { 43 namespace internal { 44 45 const auto GetRegConfig = RegisterConfiguration::Crankshaft; 46 47 void LOperand::PrintTo(StringStream* stream) { 48 LUnallocated* unalloc = NULL; 49 switch (kind()) { 50 case INVALID: 51 stream->Add("(0)"); 52 break; 53 case UNALLOCATED: 54 unalloc = LUnallocated::cast(this); 55 stream->Add("v%d", unalloc->virtual_register()); 56 if (unalloc->basic_policy() == LUnallocated::FIXED_SLOT) { 57 stream->Add("(=%dS)", unalloc->fixed_slot_index()); 58 break; 59 } 60 switch (unalloc->extended_policy()) { 61 case LUnallocated::NONE: 62 break; 63 case LUnallocated::FIXED_REGISTER: { 64 int reg_index = unalloc->fixed_register_index(); 65 if (reg_index < 0 || reg_index >= Register::kNumRegisters) { 66 stream->Add("(=invalid_reg#%d)", reg_index); 67 } else { 68 const char* register_name = 69 GetRegConfig()->GetGeneralRegisterName(reg_index); 70 stream->Add("(=%s)", register_name); 71 } 72 break; 73 } 74 case LUnallocated::FIXED_DOUBLE_REGISTER: { 75 int reg_index = unalloc->fixed_register_index(); 76 if (reg_index < 0 || reg_index >= DoubleRegister::kMaxNumRegisters) { 77 stream->Add("(=invalid_double_reg#%d)", reg_index); 78 } else { 79 const char* double_register_name = 80 GetRegConfig()->GetDoubleRegisterName(reg_index); 81 stream->Add("(=%s)", double_register_name); 82 } 83 break; 84 } 85 case LUnallocated::MUST_HAVE_REGISTER: 86 stream->Add("(R)"); 87 break; 88 case LUnallocated::MUST_HAVE_DOUBLE_REGISTER: 89 stream->Add("(D)"); 90 break; 91 case LUnallocated::WRITABLE_REGISTER: 92 stream->Add("(WR)"); 93 break; 94 case LUnallocated::SAME_AS_FIRST_INPUT: 95 stream->Add("(1)"); 96 break; 97 case LUnallocated::ANY: 98 stream->Add("(-)"); 99 break; 100 } 101 break; 102 case CONSTANT_OPERAND: 103 stream->Add("[constant:%d]", index()); 104 break; 105 case STACK_SLOT: 106 stream->Add("[stack:%d]", index()); 107 break; 108 case DOUBLE_STACK_SLOT: 109 stream->Add("[double_stack:%d]", index()); 110 break; 111 case REGISTER: { 112 int reg_index = index(); 113 if (reg_index < 0 || reg_index >= Register::kNumRegisters) { 114 stream->Add("(=invalid_reg#%d|R)", reg_index); 115 } else { 116 stream->Add("[%s|R]", 117 GetRegConfig()->GetGeneralRegisterName(reg_index)); 118 } 119 break; 120 } 121 case DOUBLE_REGISTER: { 122 int reg_index = index(); 123 if (reg_index < 0 || reg_index >= DoubleRegister::kMaxNumRegisters) { 124 stream->Add("(=invalid_double_reg#%d|R)", reg_index); 125 } else { 126 stream->Add("[%s|R]", GetRegConfig()->GetDoubleRegisterName(reg_index)); 127 } 128 break; 129 } 130 } 131 } 132 133 134 template<LOperand::Kind kOperandKind, int kNumCachedOperands> 135 LSubKindOperand<kOperandKind, kNumCachedOperands>* 136 LSubKindOperand<kOperandKind, kNumCachedOperands>::cache = NULL; 137 138 139 template<LOperand::Kind kOperandKind, int kNumCachedOperands> 140 void LSubKindOperand<kOperandKind, kNumCachedOperands>::SetUpCache() { 141 if (cache) return; 142 cache = new LSubKindOperand[kNumCachedOperands]; 143 for (int i = 0; i < kNumCachedOperands; i++) { 144 cache[i].ConvertTo(kOperandKind, i); 145 } 146 } 147 148 149 template<LOperand::Kind kOperandKind, int kNumCachedOperands> 150 void LSubKindOperand<kOperandKind, kNumCachedOperands>::TearDownCache() { 151 delete[] cache; 152 cache = NULL; 153 } 154 155 156 void LOperand::SetUpCaches() { 157 #define LITHIUM_OPERAND_SETUP(name, type, number) L##name::SetUpCache(); 158 LITHIUM_OPERAND_LIST(LITHIUM_OPERAND_SETUP) 159 #undef LITHIUM_OPERAND_SETUP 160 } 161 162 163 void LOperand::TearDownCaches() { 164 #define LITHIUM_OPERAND_TEARDOWN(name, type, number) L##name::TearDownCache(); 165 LITHIUM_OPERAND_LIST(LITHIUM_OPERAND_TEARDOWN) 166 #undef LITHIUM_OPERAND_TEARDOWN 167 } 168 169 170 bool LParallelMove::IsRedundant() const { 171 for (int i = 0; i < move_operands_.length(); ++i) { 172 if (!move_operands_[i].IsRedundant()) return false; 173 } 174 return true; 175 } 176 177 178 void LParallelMove::PrintDataTo(StringStream* stream) const { 179 bool first = true; 180 for (int i = 0; i < move_operands_.length(); ++i) { 181 if (!move_operands_[i].IsEliminated()) { 182 LOperand* source = move_operands_[i].source(); 183 LOperand* destination = move_operands_[i].destination(); 184 if (!first) stream->Add(" "); 185 first = false; 186 if (source->Equals(destination)) { 187 destination->PrintTo(stream); 188 } else { 189 destination->PrintTo(stream); 190 stream->Add(" = "); 191 source->PrintTo(stream); 192 } 193 stream->Add(";"); 194 } 195 } 196 } 197 198 199 void LEnvironment::PrintTo(StringStream* stream) { 200 stream->Add("[id=%d|", ast_id().ToInt()); 201 if (deoptimization_index() != Safepoint::kNoDeoptimizationIndex) { 202 stream->Add("deopt_id=%d|", deoptimization_index()); 203 } 204 stream->Add("parameters=%d|", parameter_count()); 205 stream->Add("arguments_stack_height=%d|", arguments_stack_height()); 206 for (int i = 0; i < values_.length(); ++i) { 207 if (i != 0) stream->Add(";"); 208 if (values_[i] == NULL) { 209 stream->Add("[hole]"); 210 } else { 211 values_[i]->PrintTo(stream); 212 } 213 } 214 stream->Add("]"); 215 } 216 217 218 void LPointerMap::RecordPointer(LOperand* op, Zone* zone) { 219 // Do not record arguments as pointers. 220 if (op->IsStackSlot() && op->index() < 0) return; 221 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); 222 pointer_operands_.Add(op, zone); 223 } 224 225 226 void LPointerMap::RemovePointer(LOperand* op) { 227 // Do not record arguments as pointers. 228 if (op->IsStackSlot() && op->index() < 0) return; 229 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); 230 for (int i = 0; i < pointer_operands_.length(); ++i) { 231 if (pointer_operands_[i]->Equals(op)) { 232 pointer_operands_.Remove(i); 233 --i; 234 } 235 } 236 } 237 238 239 void LPointerMap::RecordUntagged(LOperand* op, Zone* zone) { 240 // Do not record arguments as pointers. 241 if (op->IsStackSlot() && op->index() < 0) return; 242 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); 243 untagged_operands_.Add(op, zone); 244 } 245 246 247 void LPointerMap::PrintTo(StringStream* stream) { 248 stream->Add("{"); 249 for (int i = 0; i < pointer_operands_.length(); ++i) { 250 if (i != 0) stream->Add(";"); 251 pointer_operands_[i]->PrintTo(stream); 252 } 253 stream->Add("}"); 254 } 255 256 LChunk::LChunk(CompilationInfo* info, HGraph* graph) 257 : base_frame_slots_(info->IsStub() 258 ? TypedFrameConstants::kFixedSlotCount 259 : StandardFrameConstants::kFixedSlotCount), 260 current_frame_slots_(base_frame_slots_), 261 info_(info), 262 graph_(graph), 263 instructions_(32, info->zone()), 264 pointer_maps_(8, info->zone()), 265 deprecation_dependencies_(32, info->zone()), 266 stability_dependencies_(8, info->zone()) {} 267 268 LLabel* LChunk::GetLabel(int block_id) const { 269 HBasicBlock* block = graph_->blocks()->at(block_id); 270 int first_instruction = block->first_instruction_index(); 271 return LLabel::cast(instructions_[first_instruction]); 272 } 273 274 275 int LChunk::LookupDestination(int block_id) const { 276 LLabel* cur = GetLabel(block_id); 277 while (cur->replacement() != NULL) { 278 cur = cur->replacement(); 279 } 280 return cur->block_id(); 281 } 282 283 Label* LChunk::GetAssemblyLabel(int block_id) const { 284 LLabel* label = GetLabel(block_id); 285 DCHECK(!label->HasReplacement()); 286 return label->label(); 287 } 288 289 290 void LChunk::MarkEmptyBlocks() { 291 LPhase phase("L_Mark empty blocks", this); 292 for (int i = 0; i < graph()->blocks()->length(); ++i) { 293 HBasicBlock* block = graph()->blocks()->at(i); 294 int first = block->first_instruction_index(); 295 int last = block->last_instruction_index(); 296 LInstruction* first_instr = instructions()->at(first); 297 LInstruction* last_instr = instructions()->at(last); 298 299 LLabel* label = LLabel::cast(first_instr); 300 if (last_instr->IsGoto()) { 301 LGoto* goto_instr = LGoto::cast(last_instr); 302 if (label->IsRedundant() && 303 !label->is_loop_header()) { 304 bool can_eliminate = true; 305 for (int i = first + 1; i < last && can_eliminate; ++i) { 306 LInstruction* cur = instructions()->at(i); 307 if (cur->IsGap()) { 308 LGap* gap = LGap::cast(cur); 309 if (!gap->IsRedundant()) { 310 can_eliminate = false; 311 } 312 } else { 313 can_eliminate = false; 314 } 315 } 316 if (can_eliminate) { 317 label->set_replacement(GetLabel(goto_instr->block_id())); 318 } 319 } 320 } 321 } 322 } 323 324 325 void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) { 326 LInstructionGap* gap = new (zone()) LInstructionGap(block); 327 gap->set_hydrogen_value(instr->hydrogen_value()); 328 int index = -1; 329 if (instr->IsControl()) { 330 instructions_.Add(gap, zone()); 331 index = instructions_.length(); 332 instructions_.Add(instr, zone()); 333 } else { 334 index = instructions_.length(); 335 instructions_.Add(instr, zone()); 336 instructions_.Add(gap, zone()); 337 } 338 if (instr->HasPointerMap()) { 339 pointer_maps_.Add(instr->pointer_map(), zone()); 340 instr->pointer_map()->set_lithium_position(index); 341 } 342 } 343 344 LConstantOperand* LChunk::DefineConstantOperand(HConstant* constant) { 345 return LConstantOperand::Create(constant->id(), zone()); 346 } 347 348 349 int LChunk::GetParameterStackSlot(int index) const { 350 // The receiver is at index 0, the first parameter at index 1, so we 351 // shift all parameter indexes down by the number of parameters, and 352 // make sure they end up negative so they are distinguishable from 353 // spill slots. 354 int result = index - info()->num_parameters() - 1; 355 356 DCHECK(result < 0); 357 return result; 358 } 359 360 361 // A parameter relative to ebp in the arguments stub. 362 int LChunk::ParameterAt(int index) { 363 DCHECK(-1 <= index); // -1 is the receiver. 364 return (1 + info()->scope()->num_parameters() - index) * 365 kPointerSize; 366 } 367 368 369 LGap* LChunk::GetGapAt(int index) const { 370 return LGap::cast(instructions_[index]); 371 } 372 373 374 bool LChunk::IsGapAt(int index) const { 375 return instructions_[index]->IsGap(); 376 } 377 378 379 int LChunk::NearestGapPos(int index) const { 380 while (!IsGapAt(index)) index--; 381 return index; 382 } 383 384 385 void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) { 386 GetGapAt(index)->GetOrCreateParallelMove( 387 LGap::START, zone())->AddMove(from, to, zone()); 388 } 389 390 391 HConstant* LChunk::LookupConstant(LConstantOperand* operand) const { 392 return HConstant::cast(graph_->LookupValue(operand->index())); 393 } 394 395 396 Representation LChunk::LookupLiteralRepresentation( 397 LConstantOperand* operand) const { 398 return graph_->LookupValue(operand->index())->representation(); 399 } 400 401 402 void LChunk::CommitDependencies(Handle<Code> code) const { 403 if (!code->is_optimized_code()) return; 404 HandleScope scope(isolate()); 405 406 for (Handle<Map> map : deprecation_dependencies_) { 407 DCHECK(!map->is_deprecated()); 408 DCHECK(map->CanBeDeprecated()); 409 Map::AddDependentCode(map, DependentCode::kTransitionGroup, code); 410 } 411 412 for (Handle<Map> map : stability_dependencies_) { 413 DCHECK(map->is_stable()); 414 DCHECK(map->CanTransition()); 415 Map::AddDependentCode(map, DependentCode::kPrototypeCheckGroup, code); 416 } 417 418 info_->dependencies()->Commit(code); 419 } 420 421 422 LChunk* LChunk::NewChunk(HGraph* graph) { 423 DisallowHandleAllocation no_handles; 424 DisallowHeapAllocation no_gc; 425 graph->DisallowAddingNewValues(); 426 int values = graph->GetMaximumValueID(); 427 CompilationInfo* info = graph->info(); 428 if (values > LUnallocated::kMaxVirtualRegisters) { 429 info->AbortOptimization(kNotEnoughVirtualRegistersForValues); 430 return NULL; 431 } 432 LAllocator allocator(values, graph); 433 LChunkBuilder builder(info, graph, &allocator); 434 LChunk* chunk = builder.Build(); 435 if (chunk == NULL) return NULL; 436 437 if (!allocator.Allocate(chunk)) { 438 info->AbortOptimization(kNotEnoughVirtualRegistersRegalloc); 439 return NULL; 440 } 441 442 chunk->set_allocated_double_registers( 443 allocator.assigned_double_registers()); 444 445 return chunk; 446 } 447 448 449 Handle<Code> LChunk::Codegen() { 450 MacroAssembler assembler(info()->isolate(), NULL, 0, 451 CodeObjectRequired::kYes); 452 // Code serializer only takes unoptimized code. 453 DCHECK(!info()->will_serialize()); 454 LCodeGen generator(this, &assembler, info()); 455 456 MarkEmptyBlocks(); 457 458 if (generator.GenerateCode()) { 459 generator.CheckEnvironmentUsage(); 460 CodeGenerator::MakeCodePrologue(info(), "optimized"); 461 Handle<Code> code = CodeGenerator::MakeCodeEpilogue( 462 &assembler, nullptr, info(), assembler.CodeObject()); 463 generator.FinishCode(code); 464 CommitDependencies(code); 465 Handle<ByteArray> source_positions = 466 generator.source_position_table_builder()->ToSourcePositionTable( 467 info()->isolate(), Handle<AbstractCode>::cast(code)); 468 code->set_source_position_table(*source_positions); 469 code->set_is_crankshafted(true); 470 471 CodeGenerator::PrintCode(code, info()); 472 return code; 473 } 474 assembler.AbortedCodeGeneration(); 475 return Handle<Code>::null(); 476 } 477 478 479 void LChunk::set_allocated_double_registers(BitVector* allocated_registers) { 480 allocated_double_registers_ = allocated_registers; 481 BitVector* doubles = allocated_double_registers(); 482 BitVector::Iterator iterator(doubles); 483 while (!iterator.Done()) { 484 if (info()->saves_caller_doubles()) { 485 if (kDoubleSize == kPointerSize * 2) { 486 current_frame_slots_ += 2; 487 } else { 488 current_frame_slots_++; 489 } 490 } 491 iterator.Advance(); 492 } 493 } 494 495 496 void LChunkBuilderBase::Abort(BailoutReason reason) { 497 info()->AbortOptimization(reason); 498 status_ = ABORTED; 499 } 500 501 502 void LChunkBuilderBase::Retry(BailoutReason reason) { 503 info()->RetryOptimization(reason); 504 status_ = ABORTED; 505 } 506 507 void LChunkBuilderBase::CreateLazyBailoutForCall(HBasicBlock* current_block, 508 LInstruction* instr, 509 HInstruction* hydrogen_val) { 510 if (!instr->IsCall()) return; 511 512 HEnvironment* hydrogen_env = current_block->last_environment(); 513 HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; 514 DCHECK_NOT_NULL(hydrogen_env); 515 if (instr->IsSyntacticTailCall()) { 516 // If it was a syntactic tail call we need to drop the current frame and 517 // all the frames on top of it that are either an arguments adaptor frame 518 // or a tail caller frame. 519 hydrogen_env = hydrogen_env->outer(); 520 while (hydrogen_env != nullptr && 521 (hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR || 522 hydrogen_env->frame_type() == TAIL_CALLER_FUNCTION)) { 523 hydrogen_env = hydrogen_env->outer(); 524 } 525 if (hydrogen_env != nullptr) { 526 if (hydrogen_env->frame_type() == JS_FUNCTION) { 527 // In case an outer frame is a function frame we have to replay 528 // environment manually because 529 // 1) it does not contain a result of inlined function yet, 530 // 2) we can't find the proper simulate that corresponds to the point 531 // after inlined call to do a ReplayEnvironment() on. 532 // So we push return value on top of outer environment. 533 // As for JS_GETTER/JS_SETTER/JS_CONSTRUCT nothing has to be done here, 534 // the deoptimizer ensures that the result of the callee is correctly 535 // propagated to result register during deoptimization. 536 hydrogen_env = hydrogen_env->Copy(); 537 hydrogen_env->Push(hydrogen_val); 538 } 539 } else { 540 // Although we don't need this lazy bailout for normal execution 541 // (because when we tail call from the outermost function we should pop 542 // its frame) we still need it when debugger is on. 543 hydrogen_env = current_block->last_environment(); 544 } 545 } else { 546 if (hydrogen_val->HasObservableSideEffects()) { 547 HSimulate* sim = HSimulate::cast(hydrogen_val->next()); 548 sim->ReplayEnvironment(hydrogen_env); 549 hydrogen_value_for_lazy_bailout = sim; 550 } 551 } 552 LInstruction* bailout = LChunkBuilderBase::AssignEnvironment( 553 new (zone()) LLazyBailout(), hydrogen_env); 554 bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout); 555 chunk_->AddInstruction(bailout, current_block); 556 } 557 558 LInstruction* LChunkBuilderBase::AssignEnvironment(LInstruction* instr, 559 HEnvironment* hydrogen_env) { 560 int argument_index_accumulator = 0; 561 ZoneList<HValue*> objects_to_materialize(0, zone()); 562 DCHECK_NE(TAIL_CALLER_FUNCTION, hydrogen_env->frame_type()); 563 instr->set_environment(CreateEnvironment( 564 hydrogen_env, &argument_index_accumulator, &objects_to_materialize)); 565 return instr; 566 } 567 568 LEnvironment* LChunkBuilderBase::CreateEnvironment( 569 HEnvironment* hydrogen_env, int* argument_index_accumulator, 570 ZoneList<HValue*>* objects_to_materialize) { 571 if (hydrogen_env == NULL) return NULL; 572 573 BailoutId ast_id = hydrogen_env->ast_id(); 574 DCHECK(!ast_id.IsNone() || 575 (hydrogen_env->frame_type() != JS_FUNCTION && 576 hydrogen_env->frame_type() != TAIL_CALLER_FUNCTION)); 577 578 if (hydrogen_env->frame_type() == TAIL_CALLER_FUNCTION) { 579 // Skip potential outer arguments adaptor frame. 580 HEnvironment* outer_hydrogen_env = hydrogen_env->outer(); 581 if (outer_hydrogen_env != nullptr && 582 outer_hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) { 583 outer_hydrogen_env = outer_hydrogen_env->outer(); 584 } 585 LEnvironment* outer = CreateEnvironment( 586 outer_hydrogen_env, argument_index_accumulator, objects_to_materialize); 587 return new (zone()) 588 LEnvironment(hydrogen_env->closure(), hydrogen_env->frame_type(), 589 ast_id, 0, 0, 0, outer, hydrogen_env->entry(), zone()); 590 } 591 592 LEnvironment* outer = 593 CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator, 594 objects_to_materialize); 595 596 int omitted_count = (hydrogen_env->frame_type() == JS_FUNCTION) 597 ? 0 598 : hydrogen_env->specials_count(); 599 600 int value_count = hydrogen_env->length() - omitted_count; 601 LEnvironment* result = 602 new(zone()) LEnvironment(hydrogen_env->closure(), 603 hydrogen_env->frame_type(), 604 ast_id, 605 hydrogen_env->parameter_count(), 606 argument_count_, 607 value_count, 608 outer, 609 hydrogen_env->entry(), 610 zone()); 611 int argument_index = *argument_index_accumulator; 612 613 // Store the environment description into the environment 614 // (with holes for nested objects) 615 for (int i = 0; i < hydrogen_env->length(); ++i) { 616 if (hydrogen_env->is_special_index(i) && 617 hydrogen_env->frame_type() != JS_FUNCTION) { 618 continue; 619 } 620 LOperand* op; 621 HValue* value = hydrogen_env->values()->at(i); 622 CHECK(!value->IsPushArguments()); // Do not deopt outgoing arguments 623 if (value->IsArgumentsObject() || value->IsCapturedObject()) { 624 op = LEnvironment::materialization_marker(); 625 } else { 626 op = UseAny(value); 627 } 628 result->AddValue(op, 629 value->representation(), 630 value->CheckFlag(HInstruction::kUint32)); 631 } 632 633 // Recursively store the nested objects into the environment 634 for (int i = 0; i < hydrogen_env->length(); ++i) { 635 if (hydrogen_env->is_special_index(i)) continue; 636 637 HValue* value = hydrogen_env->values()->at(i); 638 if (value->IsArgumentsObject() || value->IsCapturedObject()) { 639 AddObjectToMaterialize(value, objects_to_materialize, result); 640 } 641 } 642 643 if (hydrogen_env->frame_type() == JS_FUNCTION) { 644 *argument_index_accumulator = argument_index; 645 } 646 647 return result; 648 } 649 650 651 // Add an object to the supplied environment and object materialization list. 652 // 653 // Notes: 654 // 655 // We are building three lists here: 656 // 657 // 1. In the result->object_mapping_ list (added to by the 658 // LEnvironment::Add*Object methods), we store the lengths (number 659 // of fields) of the captured objects in depth-first traversal order, or 660 // in case of duplicated objects, we store the index to the duplicate object 661 // (with a tag to differentiate between captured and duplicated objects). 662 // 663 // 2. The object fields are stored in the result->values_ list 664 // (added to by the LEnvironment.AddValue method) sequentially as lists 665 // of fields with holes for nested objects (the holes will be expanded 666 // later by LCodegen::AddToTranslation according to the 667 // LEnvironment.object_mapping_ list). 668 // 669 // 3. The auxiliary objects_to_materialize array stores the hydrogen values 670 // in the same order as result->object_mapping_ list. This is used 671 // to detect duplicate values and calculate the corresponding object index. 672 void LChunkBuilderBase::AddObjectToMaterialize(HValue* value, 673 ZoneList<HValue*>* objects_to_materialize, LEnvironment* result) { 674 int object_index = objects_to_materialize->length(); 675 // Store the hydrogen value into the de-duplication array 676 objects_to_materialize->Add(value, zone()); 677 // Find out whether we are storing a duplicated value 678 int previously_materialized_object = -1; 679 for (int prev = 0; prev < object_index; ++prev) { 680 if (objects_to_materialize->at(prev) == value) { 681 previously_materialized_object = prev; 682 break; 683 } 684 } 685 // Store the captured object length (or duplicated object index) 686 // into the environment. For duplicated objects, we stop here. 687 int length = value->OperandCount(); 688 bool is_arguments = value->IsArgumentsObject(); 689 if (previously_materialized_object >= 0) { 690 result->AddDuplicateObject(previously_materialized_object); 691 return; 692 } else { 693 result->AddNewObject(is_arguments ? length - 1 : length, is_arguments); 694 } 695 // Store the captured object's fields into the environment 696 for (int i = is_arguments ? 1 : 0; i < length; ++i) { 697 LOperand* op; 698 HValue* arg_value = value->OperandAt(i); 699 if (arg_value->IsArgumentsObject() || arg_value->IsCapturedObject()) { 700 // Insert a hole for nested objects 701 op = LEnvironment::materialization_marker(); 702 } else { 703 DCHECK(!arg_value->IsPushArguments()); 704 // For ordinary values, tell the register allocator we need the value 705 // to be alive here 706 op = UseAny(arg_value); 707 } 708 result->AddValue(op, 709 arg_value->representation(), 710 arg_value->CheckFlag(HInstruction::kUint32)); 711 } 712 // Recursively store all the nested captured objects into the environment 713 for (int i = is_arguments ? 1 : 0; i < length; ++i) { 714 HValue* arg_value = value->OperandAt(i); 715 if (arg_value->IsArgumentsObject() || arg_value->IsCapturedObject()) { 716 AddObjectToMaterialize(arg_value, objects_to_materialize, result); 717 } 718 } 719 } 720 721 722 LPhase::~LPhase() { 723 if (ShouldProduceTraceOutput()) { 724 isolate()->GetHTracer()->TraceLithium(name(), chunk_); 725 } 726 } 727 728 729 } // namespace internal 730 } // namespace v8 731