1// Copyright 2012 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 "v8.h" 29 30#if defined(V8_TARGET_ARCH_X64) 31 32#include "code-stubs.h" 33#include "codegen.h" 34#include "compiler.h" 35#include "debug.h" 36#include "full-codegen.h" 37#include "parser.h" 38#include "scopes.h" 39#include "stub-cache.h" 40 41namespace v8 { 42namespace internal { 43 44#define __ ACCESS_MASM(masm_) 45 46 47class JumpPatchSite BASE_EMBEDDED { 48 public: 49 explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { 50#ifdef DEBUG 51 info_emitted_ = false; 52#endif 53 } 54 55 ~JumpPatchSite() { 56 ASSERT(patch_site_.is_bound() == info_emitted_); 57 } 58 59 void EmitJumpIfNotSmi(Register reg, 60 Label* target, 61 Label::Distance near_jump = Label::kFar) { 62 __ testb(reg, Immediate(kSmiTagMask)); 63 EmitJump(not_carry, target, near_jump); // Always taken before patched. 64 } 65 66 void EmitJumpIfSmi(Register reg, 67 Label* target, 68 Label::Distance near_jump = Label::kFar) { 69 __ testb(reg, Immediate(kSmiTagMask)); 70 EmitJump(carry, target, near_jump); // Never taken before patched. 71 } 72 73 void EmitPatchInfo() { 74 if (patch_site_.is_bound()) { 75 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_); 76 ASSERT(is_int8(delta_to_patch_site)); 77 __ testl(rax, Immediate(delta_to_patch_site)); 78#ifdef DEBUG 79 info_emitted_ = true; 80#endif 81 } else { 82 __ nop(); // Signals no inlined code. 83 } 84 } 85 86 private: 87 // jc will be patched with jz, jnc will become jnz. 88 void EmitJump(Condition cc, Label* target, Label::Distance near_jump) { 89 ASSERT(!patch_site_.is_bound() && !info_emitted_); 90 ASSERT(cc == carry || cc == not_carry); 91 __ bind(&patch_site_); 92 __ j(cc, target, near_jump); 93 } 94 95 MacroAssembler* masm_; 96 Label patch_site_; 97#ifdef DEBUG 98 bool info_emitted_; 99#endif 100}; 101 102 103int FullCodeGenerator::self_optimization_header_size() { 104 return 20; 105} 106 107 108// Generate code for a JS function. On entry to the function the receiver 109// and arguments have been pushed on the stack left to right, with the 110// return address on top of them. The actual argument count matches the 111// formal parameter count expected by the function. 112// 113// The live registers are: 114// o rdi: the JS function object being called (i.e. ourselves) 115// o rsi: our context 116// o rbp: our caller's frame pointer 117// o rsp: stack pointer (pointing to return address) 118// 119// The function builds a JS frame. Please see JavaScriptFrameConstants in 120// frames-x64.h for its layout. 121void FullCodeGenerator::Generate() { 122 CompilationInfo* info = info_; 123 handler_table_ = 124 isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); 125 SetFunctionPosition(function()); 126 Comment cmnt(masm_, "[ function compiled by full code generator"); 127 128 // We can optionally optimize based on counters rather than statistical 129 // sampling. 130 if (info->ShouldSelfOptimize()) { 131 if (FLAG_trace_opt_verbose) { 132 PrintF("[adding self-optimization header to %s]\n", 133 *info->function()->debug_name()->ToCString()); 134 } 135 has_self_optimization_header_ = true; 136 MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell( 137 Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt)); 138 JSGlobalPropertyCell* cell; 139 if (maybe_cell->To(&cell)) { 140 __ movq(rax, Handle<JSGlobalPropertyCell>(cell), 141 RelocInfo::EMBEDDED_OBJECT); 142 __ SmiAddConstant(FieldOperand(rax, JSGlobalPropertyCell::kValueOffset), 143 Smi::FromInt(-1)); 144 Handle<Code> compile_stub( 145 isolate()->builtins()->builtin(Builtins::kLazyRecompile)); 146 __ j(zero, compile_stub, RelocInfo::CODE_TARGET); 147 ASSERT(masm_->pc_offset() == self_optimization_header_size()); 148 } 149 } 150 151#ifdef DEBUG 152 if (strlen(FLAG_stop_at) > 0 && 153 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { 154 __ int3(); 155 } 156#endif 157 158 // Strict mode functions and builtins need to replace the receiver 159 // with undefined when called as functions (without an explicit 160 // receiver object). rcx is zero for method calls and non-zero for 161 // function calls. 162 if (!info->is_classic_mode() || info->is_native()) { 163 Label ok; 164 __ testq(rcx, rcx); 165 __ j(zero, &ok, Label::kNear); 166 // +1 for return address. 167 int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize; 168 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); 169 __ movq(Operand(rsp, receiver_offset), kScratchRegister); 170 __ bind(&ok); 171 } 172 173 // Open a frame scope to indicate that there is a frame on the stack. The 174 // MANUAL indicates that the scope shouldn't actually generate code to set up 175 // the frame (that is done below). 176 FrameScope frame_scope(masm_, StackFrame::MANUAL); 177 178 __ push(rbp); // Caller's frame pointer. 179 __ movq(rbp, rsp); 180 __ push(rsi); // Callee's context. 181 __ push(rdi); // Callee's JS Function. 182 183 { Comment cmnt(masm_, "[ Allocate locals"); 184 int locals_count = info->scope()->num_stack_slots(); 185 if (locals_count == 1) { 186 __ PushRoot(Heap::kUndefinedValueRootIndex); 187 } else if (locals_count > 1) { 188 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); 189 for (int i = 0; i < locals_count; i++) { 190 __ push(rdx); 191 } 192 } 193 } 194 195 bool function_in_register = true; 196 197 // Possibly allocate a local context. 198 int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; 199 if (heap_slots > 0) { 200 Comment cmnt(masm_, "[ Allocate local context"); 201 // Argument to NewContext is the function, which is still in rdi. 202 __ push(rdi); 203 if (heap_slots <= FastNewContextStub::kMaximumSlots) { 204 FastNewContextStub stub(heap_slots); 205 __ CallStub(&stub); 206 } else { 207 __ CallRuntime(Runtime::kNewFunctionContext, 1); 208 } 209 function_in_register = false; 210 // Context is returned in both rax and rsi. It replaces the context 211 // passed to us. It's saved in the stack and kept live in rsi. 212 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); 213 214 // Copy any necessary parameters into the context. 215 int num_parameters = info->scope()->num_parameters(); 216 for (int i = 0; i < num_parameters; i++) { 217 Variable* var = scope()->parameter(i); 218 if (var->IsContextSlot()) { 219 int parameter_offset = StandardFrameConstants::kCallerSPOffset + 220 (num_parameters - 1 - i) * kPointerSize; 221 // Load parameter from stack. 222 __ movq(rax, Operand(rbp, parameter_offset)); 223 // Store it in the context. 224 int context_offset = Context::SlotOffset(var->index()); 225 __ movq(Operand(rsi, context_offset), rax); 226 // Update the write barrier. This clobbers rax and rbx. 227 __ RecordWriteContextSlot( 228 rsi, context_offset, rax, rbx, kDontSaveFPRegs); 229 } 230 } 231 } 232 233 // Possibly allocate an arguments object. 234 Variable* arguments = scope()->arguments(); 235 if (arguments != NULL) { 236 // Arguments object must be allocated after the context object, in 237 // case the "arguments" or ".arguments" variables are in the context. 238 Comment cmnt(masm_, "[ Allocate arguments object"); 239 if (function_in_register) { 240 __ push(rdi); 241 } else { 242 __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 243 } 244 // The receiver is just before the parameters on the caller's stack. 245 int num_parameters = info->scope()->num_parameters(); 246 int offset = num_parameters * kPointerSize; 247 __ lea(rdx, 248 Operand(rbp, StandardFrameConstants::kCallerSPOffset + offset)); 249 __ push(rdx); 250 __ Push(Smi::FromInt(num_parameters)); 251 // Arguments to ArgumentsAccessStub: 252 // function, receiver address, parameter count. 253 // The stub will rewrite receiver and parameter count if the previous 254 // stack frame was an arguments adapter frame. 255 ArgumentsAccessStub::Type type; 256 if (!is_classic_mode()) { 257 type = ArgumentsAccessStub::NEW_STRICT; 258 } else if (function()->has_duplicate_parameters()) { 259 type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; 260 } else { 261 type = ArgumentsAccessStub::NEW_NON_STRICT_FAST; 262 } 263 ArgumentsAccessStub stub(type); 264 __ CallStub(&stub); 265 266 SetVar(arguments, rax, rbx, rdx); 267 } 268 269 if (FLAG_trace) { 270 __ CallRuntime(Runtime::kTraceEnter, 0); 271 } 272 273 // Visit the declarations and body unless there is an illegal 274 // redeclaration. 275 if (scope()->HasIllegalRedeclaration()) { 276 Comment cmnt(masm_, "[ Declarations"); 277 scope()->VisitIllegalRedeclaration(this); 278 279 } else { 280 PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); 281 { Comment cmnt(masm_, "[ Declarations"); 282 // For named function expressions, declare the function name as a 283 // constant. 284 if (scope()->is_function_scope() && scope()->function() != NULL) { 285 VariableProxy* proxy = scope()->function(); 286 ASSERT(proxy->var()->mode() == CONST || 287 proxy->var()->mode() == CONST_HARMONY); 288 ASSERT(proxy->var()->location() != Variable::UNALLOCATED); 289 EmitDeclaration(proxy, proxy->var()->mode(), NULL); 290 } 291 VisitDeclarations(scope()->declarations()); 292 } 293 294 { Comment cmnt(masm_, "[ Stack check"); 295 PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); 296 Label ok; 297 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); 298 __ j(above_equal, &ok, Label::kNear); 299 StackCheckStub stub; 300 __ CallStub(&stub); 301 __ bind(&ok); 302 } 303 304 { Comment cmnt(masm_, "[ Body"); 305 ASSERT(loop_depth() == 0); 306 VisitStatements(function()->body()); 307 ASSERT(loop_depth() == 0); 308 } 309 } 310 311 // Always emit a 'return undefined' in case control fell off the end of 312 // the body. 313 { Comment cmnt(masm_, "[ return <undefined>;"); 314 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 315 EmitReturnSequence(); 316 } 317} 318 319 320void FullCodeGenerator::ClearAccumulator() { 321 __ Set(rax, 0); 322} 323 324 325void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt, 326 Label* back_edge_target) { 327 Comment cmnt(masm_, "[ Stack check"); 328 Label ok; 329 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); 330 __ j(above_equal, &ok, Label::kNear); 331 StackCheckStub stub; 332 __ CallStub(&stub); 333 // Record a mapping of this PC offset to the OSR id. This is used to find 334 // the AST id from the unoptimized code in order to use it as a key into 335 // the deoptimization input data found in the optimized code. 336 RecordStackCheck(stmt->OsrEntryId()); 337 338 // Loop stack checks can be patched to perform on-stack replacement. In 339 // order to decide whether or not to perform OSR we embed the loop depth 340 // in a test instruction after the call so we can extract it from the OSR 341 // builtin. 342 ASSERT(loop_depth() > 0); 343 __ testl(rax, Immediate(Min(loop_depth(), Code::kMaxLoopNestingMarker))); 344 345 __ bind(&ok); 346 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); 347 // Record a mapping of the OSR id to this PC. This is used if the OSR 348 // entry becomes the target of a bailout. We don't expect it to be, but 349 // we want it to work if it is. 350 PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS); 351} 352 353 354void FullCodeGenerator::EmitReturnSequence() { 355 Comment cmnt(masm_, "[ Return sequence"); 356 if (return_label_.is_bound()) { 357 __ jmp(&return_label_); 358 } else { 359 __ bind(&return_label_); 360 if (FLAG_trace) { 361 __ push(rax); 362 __ CallRuntime(Runtime::kTraceExit, 1); 363 } 364#ifdef DEBUG 365 // Add a label for checking the size of the code used for returning. 366 Label check_exit_codesize; 367 masm_->bind(&check_exit_codesize); 368#endif 369 CodeGenerator::RecordPositions(masm_, function()->end_position() - 1); 370 __ RecordJSReturn(); 371 // Do not use the leave instruction here because it is too short to 372 // patch with the code required by the debugger. 373 __ movq(rsp, rbp); 374 __ pop(rbp); 375 376 int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize; 377 __ Ret(arguments_bytes, rcx); 378 379#ifdef ENABLE_DEBUGGER_SUPPORT 380 // Add padding that will be overwritten by a debugger breakpoint. We 381 // have just generated at least 7 bytes: "movq rsp, rbp; pop rbp; ret k" 382 // (3 + 1 + 3). 383 const int kPadding = Assembler::kJSReturnSequenceLength - 7; 384 for (int i = 0; i < kPadding; ++i) { 385 masm_->int3(); 386 } 387 // Check that the size of the code used for returning is large enough 388 // for the debugger's requirements. 389 ASSERT(Assembler::kJSReturnSequenceLength <= 390 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); 391#endif 392 } 393} 394 395 396void FullCodeGenerator::EffectContext::Plug(Variable* var) const { 397 ASSERT(var->IsStackAllocated() || var->IsContextSlot()); 398} 399 400 401void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { 402 ASSERT(var->IsStackAllocated() || var->IsContextSlot()); 403 codegen()->GetVar(result_register(), var); 404} 405 406 407void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { 408 ASSERT(var->IsStackAllocated() || var->IsContextSlot()); 409 MemOperand operand = codegen()->VarOperand(var, result_register()); 410 __ push(operand); 411} 412 413 414void FullCodeGenerator::TestContext::Plug(Variable* var) const { 415 codegen()->GetVar(result_register(), var); 416 codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); 417 codegen()->DoTest(this); 418} 419 420 421void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const { 422} 423 424 425void FullCodeGenerator::AccumulatorValueContext::Plug( 426 Heap::RootListIndex index) const { 427 __ LoadRoot(result_register(), index); 428} 429 430 431void FullCodeGenerator::StackValueContext::Plug( 432 Heap::RootListIndex index) const { 433 __ PushRoot(index); 434} 435 436 437void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { 438 codegen()->PrepareForBailoutBeforeSplit(condition(), 439 true, 440 true_label_, 441 false_label_); 442 if (index == Heap::kUndefinedValueRootIndex || 443 index == Heap::kNullValueRootIndex || 444 index == Heap::kFalseValueRootIndex) { 445 if (false_label_ != fall_through_) __ jmp(false_label_); 446 } else if (index == Heap::kTrueValueRootIndex) { 447 if (true_label_ != fall_through_) __ jmp(true_label_); 448 } else { 449 __ LoadRoot(result_register(), index); 450 codegen()->DoTest(this); 451 } 452} 453 454 455void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const { 456} 457 458 459void FullCodeGenerator::AccumulatorValueContext::Plug( 460 Handle<Object> lit) const { 461 __ Move(result_register(), lit); 462} 463 464 465void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { 466 __ Push(lit); 467} 468 469 470void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { 471 codegen()->PrepareForBailoutBeforeSplit(condition(), 472 true, 473 true_label_, 474 false_label_); 475 ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals. 476 if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) { 477 if (false_label_ != fall_through_) __ jmp(false_label_); 478 } else if (lit->IsTrue() || lit->IsJSObject()) { 479 if (true_label_ != fall_through_) __ jmp(true_label_); 480 } else if (lit->IsString()) { 481 if (String::cast(*lit)->length() == 0) { 482 if (false_label_ != fall_through_) __ jmp(false_label_); 483 } else { 484 if (true_label_ != fall_through_) __ jmp(true_label_); 485 } 486 } else if (lit->IsSmi()) { 487 if (Smi::cast(*lit)->value() == 0) { 488 if (false_label_ != fall_through_) __ jmp(false_label_); 489 } else { 490 if (true_label_ != fall_through_) __ jmp(true_label_); 491 } 492 } else { 493 // For simplicity we always test the accumulator register. 494 __ Move(result_register(), lit); 495 codegen()->DoTest(this); 496 } 497} 498 499 500void FullCodeGenerator::EffectContext::DropAndPlug(int count, 501 Register reg) const { 502 ASSERT(count > 0); 503 __ Drop(count); 504} 505 506 507void FullCodeGenerator::AccumulatorValueContext::DropAndPlug( 508 int count, 509 Register reg) const { 510 ASSERT(count > 0); 511 __ Drop(count); 512 __ Move(result_register(), reg); 513} 514 515 516void FullCodeGenerator::StackValueContext::DropAndPlug(int count, 517 Register reg) const { 518 ASSERT(count > 0); 519 if (count > 1) __ Drop(count - 1); 520 __ movq(Operand(rsp, 0), reg); 521} 522 523 524void FullCodeGenerator::TestContext::DropAndPlug(int count, 525 Register reg) const { 526 ASSERT(count > 0); 527 // For simplicity we always test the accumulator register. 528 __ Drop(count); 529 __ Move(result_register(), reg); 530 codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); 531 codegen()->DoTest(this); 532} 533 534 535void FullCodeGenerator::EffectContext::Plug(Label* materialize_true, 536 Label* materialize_false) const { 537 ASSERT(materialize_true == materialize_false); 538 __ bind(materialize_true); 539} 540 541 542void FullCodeGenerator::AccumulatorValueContext::Plug( 543 Label* materialize_true, 544 Label* materialize_false) const { 545 Label done; 546 __ bind(materialize_true); 547 __ Move(result_register(), isolate()->factory()->true_value()); 548 __ jmp(&done, Label::kNear); 549 __ bind(materialize_false); 550 __ Move(result_register(), isolate()->factory()->false_value()); 551 __ bind(&done); 552} 553 554 555void FullCodeGenerator::StackValueContext::Plug( 556 Label* materialize_true, 557 Label* materialize_false) const { 558 Label done; 559 __ bind(materialize_true); 560 __ Push(isolate()->factory()->true_value()); 561 __ jmp(&done, Label::kNear); 562 __ bind(materialize_false); 563 __ Push(isolate()->factory()->false_value()); 564 __ bind(&done); 565} 566 567 568void FullCodeGenerator::TestContext::Plug(Label* materialize_true, 569 Label* materialize_false) const { 570 ASSERT(materialize_true == true_label_); 571 ASSERT(materialize_false == false_label_); 572} 573 574 575void FullCodeGenerator::EffectContext::Plug(bool flag) const { 576} 577 578 579void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const { 580 Heap::RootListIndex value_root_index = 581 flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; 582 __ LoadRoot(result_register(), value_root_index); 583} 584 585 586void FullCodeGenerator::StackValueContext::Plug(bool flag) const { 587 Heap::RootListIndex value_root_index = 588 flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; 589 __ PushRoot(value_root_index); 590} 591 592 593void FullCodeGenerator::TestContext::Plug(bool flag) const { 594 codegen()->PrepareForBailoutBeforeSplit(condition(), 595 true, 596 true_label_, 597 false_label_); 598 if (flag) { 599 if (true_label_ != fall_through_) __ jmp(true_label_); 600 } else { 601 if (false_label_ != fall_through_) __ jmp(false_label_); 602 } 603} 604 605 606void FullCodeGenerator::DoTest(Expression* condition, 607 Label* if_true, 608 Label* if_false, 609 Label* fall_through) { 610 ToBooleanStub stub(result_register()); 611 __ push(result_register()); 612 __ CallStub(&stub); 613 __ testq(result_register(), result_register()); 614 // The stub returns nonzero for true. 615 Split(not_zero, if_true, if_false, fall_through); 616} 617 618 619void FullCodeGenerator::Split(Condition cc, 620 Label* if_true, 621 Label* if_false, 622 Label* fall_through) { 623 if (if_false == fall_through) { 624 __ j(cc, if_true); 625 } else if (if_true == fall_through) { 626 __ j(NegateCondition(cc), if_false); 627 } else { 628 __ j(cc, if_true); 629 __ jmp(if_false); 630 } 631} 632 633 634MemOperand FullCodeGenerator::StackOperand(Variable* var) { 635 ASSERT(var->IsStackAllocated()); 636 // Offset is negative because higher indexes are at lower addresses. 637 int offset = -var->index() * kPointerSize; 638 // Adjust by a (parameter or local) base offset. 639 if (var->IsParameter()) { 640 offset += (info_->scope()->num_parameters() + 1) * kPointerSize; 641 } else { 642 offset += JavaScriptFrameConstants::kLocal0Offset; 643 } 644 return Operand(rbp, offset); 645} 646 647 648MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { 649 ASSERT(var->IsContextSlot() || var->IsStackAllocated()); 650 if (var->IsContextSlot()) { 651 int context_chain_length = scope()->ContextChainLength(var->scope()); 652 __ LoadContext(scratch, context_chain_length); 653 return ContextOperand(scratch, var->index()); 654 } else { 655 return StackOperand(var); 656 } 657} 658 659 660void FullCodeGenerator::GetVar(Register dest, Variable* var) { 661 ASSERT(var->IsContextSlot() || var->IsStackAllocated()); 662 MemOperand location = VarOperand(var, dest); 663 __ movq(dest, location); 664} 665 666 667void FullCodeGenerator::SetVar(Variable* var, 668 Register src, 669 Register scratch0, 670 Register scratch1) { 671 ASSERT(var->IsContextSlot() || var->IsStackAllocated()); 672 ASSERT(!scratch0.is(src)); 673 ASSERT(!scratch0.is(scratch1)); 674 ASSERT(!scratch1.is(src)); 675 MemOperand location = VarOperand(var, scratch0); 676 __ movq(location, src); 677 678 // Emit the write barrier code if the location is in the heap. 679 if (var->IsContextSlot()) { 680 int offset = Context::SlotOffset(var->index()); 681 __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); 682 } 683} 684 685 686void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, 687 bool should_normalize, 688 Label* if_true, 689 Label* if_false) { 690 // Only prepare for bailouts before splits if we're in a test 691 // context. Otherwise, we let the Visit function deal with the 692 // preparation to avoid preparing with the same AST id twice. 693 if (!context()->IsTest() || !info_->IsOptimizable()) return; 694 695 Label skip; 696 if (should_normalize) __ jmp(&skip, Label::kNear); 697 PrepareForBailout(expr, TOS_REG); 698 if (should_normalize) { 699 __ CompareRoot(rax, Heap::kTrueValueRootIndex); 700 Split(equal, if_true, if_false, NULL); 701 __ bind(&skip); 702 } 703} 704 705 706void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, 707 VariableMode mode, 708 FunctionLiteral* function) { 709 // If it was not possible to allocate the variable at compile time, we 710 // need to "declare" it at runtime to make sure it actually exists in the 711 // local context. 712 Variable* variable = proxy->var(); 713 bool binding_needs_init = (function == NULL) && 714 (mode == CONST || mode == CONST_HARMONY || mode == LET); 715 switch (variable->location()) { 716 case Variable::UNALLOCATED: 717 ++global_count_; 718 break; 719 720 case Variable::PARAMETER: 721 case Variable::LOCAL: 722 if (function != NULL) { 723 Comment cmnt(masm_, "[ Declaration"); 724 VisitForAccumulatorValue(function); 725 __ movq(StackOperand(variable), result_register()); 726 } else if (binding_needs_init) { 727 Comment cmnt(masm_, "[ Declaration"); 728 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); 729 __ movq(StackOperand(variable), kScratchRegister); 730 } 731 break; 732 733 case Variable::CONTEXT: 734 // The variable in the decl always resides in the current function 735 // context. 736 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); 737 if (FLAG_debug_code) { 738 // Check that we're not inside a with or catch context. 739 __ movq(rbx, FieldOperand(rsi, HeapObject::kMapOffset)); 740 __ CompareRoot(rbx, Heap::kWithContextMapRootIndex); 741 __ Check(not_equal, "Declaration in with context."); 742 __ CompareRoot(rbx, Heap::kCatchContextMapRootIndex); 743 __ Check(not_equal, "Declaration in catch context."); 744 } 745 if (function != NULL) { 746 Comment cmnt(masm_, "[ Declaration"); 747 VisitForAccumulatorValue(function); 748 __ movq(ContextOperand(rsi, variable->index()), result_register()); 749 int offset = Context::SlotOffset(variable->index()); 750 // We know that we have written a function, which is not a smi. 751 __ RecordWriteContextSlot(rsi, 752 offset, 753 result_register(), 754 rcx, 755 kDontSaveFPRegs, 756 EMIT_REMEMBERED_SET, 757 OMIT_SMI_CHECK); 758 PrepareForBailoutForId(proxy->id(), NO_REGISTERS); 759 } else if (binding_needs_init) { 760 Comment cmnt(masm_, "[ Declaration"); 761 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); 762 __ movq(ContextOperand(rsi, variable->index()), kScratchRegister); 763 // No write barrier since the hole value is in old space. 764 PrepareForBailoutForId(proxy->id(), NO_REGISTERS); 765 } 766 break; 767 768 case Variable::LOOKUP: { 769 Comment cmnt(masm_, "[ Declaration"); 770 __ push(rsi); 771 __ Push(variable->name()); 772 // Declaration nodes are always introduced in one of four modes. 773 ASSERT(mode == VAR || 774 mode == CONST || 775 mode == CONST_HARMONY || 776 mode == LET); 777 PropertyAttributes attr = 778 (mode == CONST || mode == CONST_HARMONY) ? READ_ONLY : NONE; 779 __ Push(Smi::FromInt(attr)); 780 // Push initial value, if any. 781 // Note: For variables we must not push an initial value (such as 782 // 'undefined') because we may have a (legal) redeclaration and we 783 // must not destroy the current value. 784 if (function != NULL) { 785 VisitForStackValue(function); 786 } else if (binding_needs_init) { 787 __ PushRoot(Heap::kTheHoleValueRootIndex); 788 } else { 789 __ Push(Smi::FromInt(0)); // Indicates no initial value. 790 } 791 __ CallRuntime(Runtime::kDeclareContextSlot, 4); 792 break; 793 } 794 } 795} 796 797 798void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { 799 // Call the runtime to declare the globals. 800 __ push(rsi); // The context is the first argument. 801 __ Push(pairs); 802 __ Push(Smi::FromInt(DeclareGlobalsFlags())); 803 __ CallRuntime(Runtime::kDeclareGlobals, 3); 804 // Return value is ignored. 805} 806 807 808void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { 809 Comment cmnt(masm_, "[ SwitchStatement"); 810 Breakable nested_statement(this, stmt); 811 SetStatementPosition(stmt); 812 813 // Keep the switch value on the stack until a case matches. 814 VisitForStackValue(stmt->tag()); 815 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); 816 817 ZoneList<CaseClause*>* clauses = stmt->cases(); 818 CaseClause* default_clause = NULL; // Can occur anywhere in the list. 819 820 Label next_test; // Recycled for each test. 821 // Compile all the tests with branches to their bodies. 822 for (int i = 0; i < clauses->length(); i++) { 823 CaseClause* clause = clauses->at(i); 824 clause->body_target()->Unuse(); 825 826 // The default is not a test, but remember it as final fall through. 827 if (clause->is_default()) { 828 default_clause = clause; 829 continue; 830 } 831 832 Comment cmnt(masm_, "[ Case comparison"); 833 __ bind(&next_test); 834 next_test.Unuse(); 835 836 // Compile the label expression. 837 VisitForAccumulatorValue(clause->label()); 838 839 // Perform the comparison as if via '==='. 840 __ movq(rdx, Operand(rsp, 0)); // Switch value. 841 bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); 842 JumpPatchSite patch_site(masm_); 843 if (inline_smi_code) { 844 Label slow_case; 845 __ movq(rcx, rdx); 846 __ or_(rcx, rax); 847 patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear); 848 849 __ cmpq(rdx, rax); 850 __ j(not_equal, &next_test); 851 __ Drop(1); // Switch value is no longer needed. 852 __ jmp(clause->body_target()); 853 __ bind(&slow_case); 854 } 855 856 // Record position before stub call for type feedback. 857 SetSourcePosition(clause->position()); 858 Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT); 859 __ call(ic, RelocInfo::CODE_TARGET, clause->CompareId()); 860 patch_site.EmitPatchInfo(); 861 862 __ testq(rax, rax); 863 __ j(not_equal, &next_test); 864 __ Drop(1); // Switch value is no longer needed. 865 __ jmp(clause->body_target()); 866 } 867 868 // Discard the test value and jump to the default if present, otherwise to 869 // the end of the statement. 870 __ bind(&next_test); 871 __ Drop(1); // Switch value is no longer needed. 872 if (default_clause == NULL) { 873 __ jmp(nested_statement.break_label()); 874 } else { 875 __ jmp(default_clause->body_target()); 876 } 877 878 // Compile all the case bodies. 879 for (int i = 0; i < clauses->length(); i++) { 880 Comment cmnt(masm_, "[ Case body"); 881 CaseClause* clause = clauses->at(i); 882 __ bind(clause->body_target()); 883 PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS); 884 VisitStatements(clause->statements()); 885 } 886 887 __ bind(nested_statement.break_label()); 888 PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); 889} 890 891 892void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { 893 Comment cmnt(masm_, "[ ForInStatement"); 894 SetStatementPosition(stmt); 895 896 Label loop, exit; 897 ForIn loop_statement(this, stmt); 898 increment_loop_depth(); 899 900 // Get the object to enumerate over. Both SpiderMonkey and JSC 901 // ignore null and undefined in contrast to the specification; see 902 // ECMA-262 section 12.6.4. 903 VisitForAccumulatorValue(stmt->enumerable()); 904 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); 905 __ j(equal, &exit); 906 Register null_value = rdi; 907 __ LoadRoot(null_value, Heap::kNullValueRootIndex); 908 __ cmpq(rax, null_value); 909 __ j(equal, &exit); 910 911 PrepareForBailoutForId(stmt->PrepareId(), TOS_REG); 912 913 // Convert the object to a JS object. 914 Label convert, done_convert; 915 __ JumpIfSmi(rax, &convert); 916 __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); 917 __ j(above_equal, &done_convert); 918 __ bind(&convert); 919 __ push(rax); 920 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); 921 __ bind(&done_convert); 922 __ push(rax); 923 924 // Check for proxies. 925 Label call_runtime; 926 STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); 927 __ CmpObjectType(rax, LAST_JS_PROXY_TYPE, rcx); 928 __ j(below_equal, &call_runtime); 929 930 // Check cache validity in generated code. This is a fast case for 931 // the JSObject::IsSimpleEnum cache validity checks. If we cannot 932 // guarantee cache validity, call the runtime system to check cache 933 // validity or get the property names in a fixed array. 934 __ CheckEnumCache(null_value, &call_runtime); 935 936 // The enum cache is valid. Load the map of the object being 937 // iterated over and use the cache for the iteration. 938 Label use_cache; 939 __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); 940 __ jmp(&use_cache, Label::kNear); 941 942 // Get the set of properties to enumerate. 943 __ bind(&call_runtime); 944 __ push(rax); // Duplicate the enumerable object on the stack. 945 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); 946 947 // If we got a map from the runtime call, we can do a fast 948 // modification check. Otherwise, we got a fixed array, and we have 949 // to do a slow check. 950 Label fixed_array; 951 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), 952 Heap::kMetaMapRootIndex); 953 __ j(not_equal, &fixed_array, Label::kNear); 954 955 // We got a map in register rax. Get the enumeration cache from it. 956 __ bind(&use_cache); 957 __ LoadInstanceDescriptors(rax, rcx); 958 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); 959 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); 960 961 // Set up the four remaining stack slots. 962 __ push(rax); // Map. 963 __ push(rdx); // Enumeration cache. 964 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); 965 __ push(rax); // Enumeration cache length (as smi). 966 __ Push(Smi::FromInt(0)); // Initial index. 967 __ jmp(&loop); 968 969 // We got a fixed array in register rax. Iterate through that. 970 Label non_proxy; 971 __ bind(&fixed_array); 972 973 Handle<JSGlobalPropertyCell> cell = 974 isolate()->factory()->NewJSGlobalPropertyCell( 975 Handle<Object>( 976 Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker))); 977 RecordTypeFeedbackCell(stmt->PrepareId(), cell); 978 __ LoadHeapObject(rbx, cell); 979 __ Move(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), 980 Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)); 981 982 __ Move(rbx, Smi::FromInt(1)); // Smi indicates slow check 983 __ movq(rcx, Operand(rsp, 0 * kPointerSize)); // Get enumerated object 984 STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); 985 __ CmpObjectType(rcx, LAST_JS_PROXY_TYPE, rcx); 986 __ j(above, &non_proxy); 987 __ Move(rbx, Smi::FromInt(0)); // Zero indicates proxy 988 __ bind(&non_proxy); 989 __ push(rbx); // Smi 990 __ push(rax); // Array 991 __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset)); 992 __ push(rax); // Fixed array length (as smi). 993 __ Push(Smi::FromInt(0)); // Initial index. 994 995 // Generate code for doing the condition check. 996 PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); 997 __ bind(&loop); 998 __ movq(rax, Operand(rsp, 0 * kPointerSize)); // Get the current index. 999 __ cmpq(rax, Operand(rsp, 1 * kPointerSize)); // Compare to the array length. 1000 __ j(above_equal, loop_statement.break_label()); 1001 1002 // Get the current entry of the array into register rbx. 1003 __ movq(rbx, Operand(rsp, 2 * kPointerSize)); 1004 SmiIndex index = masm()->SmiToIndex(rax, rax, kPointerSizeLog2); 1005 __ movq(rbx, FieldOperand(rbx, 1006 index.reg, 1007 index.scale, 1008 FixedArray::kHeaderSize)); 1009 1010 // Get the expected map from the stack or a smi in the 1011 // permanent slow case into register rdx. 1012 __ movq(rdx, Operand(rsp, 3 * kPointerSize)); 1013 1014 // Check if the expected map still matches that of the enumerable. 1015 // If not, we may have to filter the key. 1016 Label update_each; 1017 __ movq(rcx, Operand(rsp, 4 * kPointerSize)); 1018 __ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); 1019 __ j(equal, &update_each, Label::kNear); 1020 1021 // For proxies, no filtering is done. 1022 // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. 1023 __ Cmp(rdx, Smi::FromInt(0)); 1024 __ j(equal, &update_each, Label::kNear); 1025 1026 // Convert the entry to a string or null if it isn't a property 1027 // anymore. If the property has been removed while iterating, we 1028 // just skip it. 1029 __ push(rcx); // Enumerable. 1030 __ push(rbx); // Current entry. 1031 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); 1032 __ Cmp(rax, Smi::FromInt(0)); 1033 __ j(equal, loop_statement.continue_label()); 1034 __ movq(rbx, rax); 1035 1036 // Update the 'each' property or variable from the possibly filtered 1037 // entry in register rbx. 1038 __ bind(&update_each); 1039 __ movq(result_register(), rbx); 1040 // Perform the assignment as if via '='. 1041 { EffectContext context(this); 1042 EmitAssignment(stmt->each()); 1043 } 1044 1045 // Generate code for the body of the loop. 1046 Visit(stmt->body()); 1047 1048 // Generate code for going to the next element by incrementing the 1049 // index (smi) stored on top of the stack. 1050 __ bind(loop_statement.continue_label()); 1051 __ SmiAddConstant(Operand(rsp, 0 * kPointerSize), Smi::FromInt(1)); 1052 1053 EmitStackCheck(stmt, &loop); 1054 __ jmp(&loop); 1055 1056 // Remove the pointers stored on the stack. 1057 __ bind(loop_statement.break_label()); 1058 __ addq(rsp, Immediate(5 * kPointerSize)); 1059 1060 // Exit and decrement the loop depth. 1061 PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); 1062 __ bind(&exit); 1063 decrement_loop_depth(); 1064} 1065 1066 1067void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, 1068 bool pretenure) { 1069 // Use the fast case closure allocation code that allocates in new 1070 // space for nested functions that don't need literals cloning. If 1071 // we're running with the --always-opt or the --prepare-always-opt 1072 // flag, we need to use the runtime function so that the new function 1073 // we are creating here gets a chance to have its code optimized and 1074 // doesn't just get a copy of the existing unoptimized code. 1075 if (!FLAG_always_opt && 1076 !FLAG_prepare_always_opt && 1077 !pretenure && 1078 scope()->is_function_scope() && 1079 info->num_literals() == 0) { 1080 FastNewClosureStub stub(info->language_mode()); 1081 __ Push(info); 1082 __ CallStub(&stub); 1083 } else { 1084 __ push(rsi); 1085 __ Push(info); 1086 __ Push(pretenure 1087 ? isolate()->factory()->true_value() 1088 : isolate()->factory()->false_value()); 1089 __ CallRuntime(Runtime::kNewClosure, 3); 1090 } 1091 context()->Plug(rax); 1092} 1093 1094 1095void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { 1096 Comment cmnt(masm_, "[ VariableProxy"); 1097 EmitVariableLoad(expr); 1098} 1099 1100 1101void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, 1102 TypeofState typeof_state, 1103 Label* slow) { 1104 Register context = rsi; 1105 Register temp = rdx; 1106 1107 Scope* s = scope(); 1108 while (s != NULL) { 1109 if (s->num_heap_slots() > 0) { 1110 if (s->calls_non_strict_eval()) { 1111 // Check that extension is NULL. 1112 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), 1113 Immediate(0)); 1114 __ j(not_equal, slow); 1115 } 1116 // Load next context in chain. 1117 __ movq(temp, ContextOperand(context, Context::PREVIOUS_INDEX)); 1118 // Walk the rest of the chain without clobbering rsi. 1119 context = temp; 1120 } 1121 // If no outer scope calls eval, we do not need to check more 1122 // context extensions. If we have reached an eval scope, we check 1123 // all extensions from this point. 1124 if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; 1125 s = s->outer_scope(); 1126 } 1127 1128 if (s != NULL && s->is_eval_scope()) { 1129 // Loop up the context chain. There is no frame effect so it is 1130 // safe to use raw labels here. 1131 Label next, fast; 1132 if (!context.is(temp)) { 1133 __ movq(temp, context); 1134 } 1135 // Load map for comparison into register, outside loop. 1136 __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex); 1137 __ bind(&next); 1138 // Terminate at global context. 1139 __ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset)); 1140 __ j(equal, &fast, Label::kNear); 1141 // Check that extension is NULL. 1142 __ cmpq(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0)); 1143 __ j(not_equal, slow); 1144 // Load next context in chain. 1145 __ movq(temp, ContextOperand(temp, Context::PREVIOUS_INDEX)); 1146 __ jmp(&next); 1147 __ bind(&fast); 1148 } 1149 1150 // All extension objects were empty and it is safe to use a global 1151 // load IC call. 1152 __ movq(rax, GlobalObjectOperand()); 1153 __ Move(rcx, var->name()); 1154 Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); 1155 RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) 1156 ? RelocInfo::CODE_TARGET 1157 : RelocInfo::CODE_TARGET_CONTEXT; 1158 __ call(ic, mode); 1159} 1160 1161 1162MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, 1163 Label* slow) { 1164 ASSERT(var->IsContextSlot()); 1165 Register context = rsi; 1166 Register temp = rbx; 1167 1168 for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { 1169 if (s->num_heap_slots() > 0) { 1170 if (s->calls_non_strict_eval()) { 1171 // Check that extension is NULL. 1172 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), 1173 Immediate(0)); 1174 __ j(not_equal, slow); 1175 } 1176 __ movq(temp, ContextOperand(context, Context::PREVIOUS_INDEX)); 1177 // Walk the rest of the chain without clobbering rsi. 1178 context = temp; 1179 } 1180 } 1181 // Check that last extension is NULL. 1182 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); 1183 __ j(not_equal, slow); 1184 1185 // This function is used only for loads, not stores, so it's safe to 1186 // return an rsi-based operand (the write barrier cannot be allowed to 1187 // destroy the rsi register). 1188 return ContextOperand(context, var->index()); 1189} 1190 1191 1192void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, 1193 TypeofState typeof_state, 1194 Label* slow, 1195 Label* done) { 1196 // Generate fast-case code for variables that might be shadowed by 1197 // eval-introduced variables. Eval is used a lot without 1198 // introducing variables. In those cases, we do not want to 1199 // perform a runtime call for all variables in the scope 1200 // containing the eval. 1201 if (var->mode() == DYNAMIC_GLOBAL) { 1202 EmitLoadGlobalCheckExtensions(var, typeof_state, slow); 1203 __ jmp(done); 1204 } else if (var->mode() == DYNAMIC_LOCAL) { 1205 Variable* local = var->local_if_not_shadowed(); 1206 __ movq(rax, ContextSlotOperandCheckExtensions(local, slow)); 1207 if (local->mode() == CONST || 1208 local->mode() == CONST_HARMONY || 1209 local->mode() == LET) { 1210 __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); 1211 __ j(not_equal, done); 1212 if (local->mode() == CONST) { 1213 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 1214 } else { // LET || CONST_HARMONY 1215 __ Push(var->name()); 1216 __ CallRuntime(Runtime::kThrowReferenceError, 1); 1217 } 1218 } 1219 __ jmp(done); 1220 } 1221} 1222 1223 1224void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { 1225 // Record position before possible IC call. 1226 SetSourcePosition(proxy->position()); 1227 Variable* var = proxy->var(); 1228 1229 // Three cases: global variables, lookup variables, and all other types of 1230 // variables. 1231 switch (var->location()) { 1232 case Variable::UNALLOCATED: { 1233 Comment cmnt(masm_, "Global variable"); 1234 // Use inline caching. Variable name is passed in rcx and the global 1235 // object on the stack. 1236 __ Move(rcx, var->name()); 1237 __ movq(rax, GlobalObjectOperand()); 1238 Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); 1239 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); 1240 context()->Plug(rax); 1241 break; 1242 } 1243 1244 case Variable::PARAMETER: 1245 case Variable::LOCAL: 1246 case Variable::CONTEXT: { 1247 Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot"); 1248 if (var->binding_needs_init()) { 1249 // var->scope() may be NULL when the proxy is located in eval code and 1250 // refers to a potential outside binding. Currently those bindings are 1251 // always looked up dynamically, i.e. in that case 1252 // var->location() == LOOKUP. 1253 // always holds. 1254 ASSERT(var->scope() != NULL); 1255 1256 // Check if the binding really needs an initialization check. The check 1257 // can be skipped in the following situation: we have a LET or CONST 1258 // binding in harmony mode, both the Variable and the VariableProxy have 1259 // the same declaration scope (i.e. they are both in global code, in the 1260 // same function or in the same eval code) and the VariableProxy is in 1261 // the source physically located after the initializer of the variable. 1262 // 1263 // We cannot skip any initialization checks for CONST in non-harmony 1264 // mode because const variables may be declared but never initialized: 1265 // if (false) { const x; }; var y = x; 1266 // 1267 // The condition on the declaration scopes is a conservative check for 1268 // nested functions that access a binding and are called before the 1269 // binding is initialized: 1270 // function() { f(); let x = 1; function f() { x = 2; } } 1271 // 1272 bool skip_init_check; 1273 if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { 1274 skip_init_check = false; 1275 } else { 1276 // Check that we always have valid source position. 1277 ASSERT(var->initializer_position() != RelocInfo::kNoPosition); 1278 ASSERT(proxy->position() != RelocInfo::kNoPosition); 1279 skip_init_check = var->mode() != CONST && 1280 var->initializer_position() < proxy->position(); 1281 } 1282 1283 if (!skip_init_check) { 1284 // Let and const need a read barrier. 1285 Label done; 1286 GetVar(rax, var); 1287 __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); 1288 __ j(not_equal, &done, Label::kNear); 1289 if (var->mode() == LET || var->mode() == CONST_HARMONY) { 1290 // Throw a reference error when using an uninitialized let/const 1291 // binding in harmony mode. 1292 __ Push(var->name()); 1293 __ CallRuntime(Runtime::kThrowReferenceError, 1); 1294 } else { 1295 // Uninitalized const bindings outside of harmony mode are unholed. 1296 ASSERT(var->mode() == CONST); 1297 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 1298 } 1299 __ bind(&done); 1300 context()->Plug(rax); 1301 break; 1302 } 1303 } 1304 context()->Plug(var); 1305 break; 1306 } 1307 1308 case Variable::LOOKUP: { 1309 Label done, slow; 1310 // Generate code for loading from variables potentially shadowed 1311 // by eval-introduced variables. 1312 EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); 1313 __ bind(&slow); 1314 Comment cmnt(masm_, "Lookup slot"); 1315 __ push(rsi); // Context. 1316 __ Push(var->name()); 1317 __ CallRuntime(Runtime::kLoadContextSlot, 2); 1318 __ bind(&done); 1319 context()->Plug(rax); 1320 break; 1321 } 1322 } 1323} 1324 1325 1326void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { 1327 Comment cmnt(masm_, "[ RegExpLiteral"); 1328 Label materialized; 1329 // Registers will be used as follows: 1330 // rdi = JS function. 1331 // rcx = literals array. 1332 // rbx = regexp literal. 1333 // rax = regexp literal clone. 1334 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 1335 __ movq(rcx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); 1336 int literal_offset = 1337 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; 1338 __ movq(rbx, FieldOperand(rcx, literal_offset)); 1339 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); 1340 __ j(not_equal, &materialized, Label::kNear); 1341 1342 // Create regexp literal using runtime function 1343 // Result will be in rax. 1344 __ push(rcx); 1345 __ Push(Smi::FromInt(expr->literal_index())); 1346 __ Push(expr->pattern()); 1347 __ Push(expr->flags()); 1348 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); 1349 __ movq(rbx, rax); 1350 1351 __ bind(&materialized); 1352 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; 1353 Label allocated, runtime_allocate; 1354 __ AllocateInNewSpace(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT); 1355 __ jmp(&allocated); 1356 1357 __ bind(&runtime_allocate); 1358 __ push(rbx); 1359 __ Push(Smi::FromInt(size)); 1360 __ CallRuntime(Runtime::kAllocateInNewSpace, 1); 1361 __ pop(rbx); 1362 1363 __ bind(&allocated); 1364 // Copy the content into the newly allocated memory. 1365 // (Unroll copy loop once for better throughput). 1366 for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) { 1367 __ movq(rdx, FieldOperand(rbx, i)); 1368 __ movq(rcx, FieldOperand(rbx, i + kPointerSize)); 1369 __ movq(FieldOperand(rax, i), rdx); 1370 __ movq(FieldOperand(rax, i + kPointerSize), rcx); 1371 } 1372 if ((size % (2 * kPointerSize)) != 0) { 1373 __ movq(rdx, FieldOperand(rbx, size - kPointerSize)); 1374 __ movq(FieldOperand(rax, size - kPointerSize), rdx); 1375 } 1376 context()->Plug(rax); 1377} 1378 1379 1380void FullCodeGenerator::EmitAccessor(Expression* expression) { 1381 if (expression == NULL) { 1382 __ PushRoot(Heap::kNullValueRootIndex); 1383 } else { 1384 VisitForStackValue(expression); 1385 } 1386} 1387 1388 1389void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { 1390 Comment cmnt(masm_, "[ ObjectLiteral"); 1391 Handle<FixedArray> constant_properties = expr->constant_properties(); 1392 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 1393 __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset)); 1394 __ Push(Smi::FromInt(expr->literal_index())); 1395 __ Push(constant_properties); 1396 int flags = expr->fast_elements() 1397 ? ObjectLiteral::kFastElements 1398 : ObjectLiteral::kNoFlags; 1399 flags |= expr->has_function() 1400 ? ObjectLiteral::kHasFunction 1401 : ObjectLiteral::kNoFlags; 1402 __ Push(Smi::FromInt(flags)); 1403 int properties_count = constant_properties->length() / 2; 1404 if (expr->depth() > 1) { 1405 __ CallRuntime(Runtime::kCreateObjectLiteral, 4); 1406 } else if (flags != ObjectLiteral::kFastElements || 1407 properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { 1408 __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); 1409 } else { 1410 FastCloneShallowObjectStub stub(properties_count); 1411 __ CallStub(&stub); 1412 } 1413 1414 // If result_saved is true the result is on top of the stack. If 1415 // result_saved is false the result is in rax. 1416 bool result_saved = false; 1417 1418 // Mark all computed expressions that are bound to a key that 1419 // is shadowed by a later occurrence of the same key. For the 1420 // marked expressions, no store code is emitted. 1421 expr->CalculateEmitStore(); 1422 1423 AccessorTable accessor_table(isolate()->zone()); 1424 for (int i = 0; i < expr->properties()->length(); i++) { 1425 ObjectLiteral::Property* property = expr->properties()->at(i); 1426 if (property->IsCompileTimeValue()) continue; 1427 1428 Literal* key = property->key(); 1429 Expression* value = property->value(); 1430 if (!result_saved) { 1431 __ push(rax); // Save result on the stack 1432 result_saved = true; 1433 } 1434 switch (property->kind()) { 1435 case ObjectLiteral::Property::CONSTANT: 1436 UNREACHABLE(); 1437 case ObjectLiteral::Property::MATERIALIZED_LITERAL: 1438 ASSERT(!CompileTimeValue::IsCompileTimeValue(value)); 1439 // Fall through. 1440 case ObjectLiteral::Property::COMPUTED: 1441 if (key->handle()->IsSymbol()) { 1442 if (property->emit_store()) { 1443 VisitForAccumulatorValue(value); 1444 __ Move(rcx, key->handle()); 1445 __ movq(rdx, Operand(rsp, 0)); 1446 Handle<Code> ic = is_classic_mode() 1447 ? isolate()->builtins()->StoreIC_Initialize() 1448 : isolate()->builtins()->StoreIC_Initialize_Strict(); 1449 __ call(ic, RelocInfo::CODE_TARGET, key->id()); 1450 PrepareForBailoutForId(key->id(), NO_REGISTERS); 1451 } else { 1452 VisitForEffect(value); 1453 } 1454 break; 1455 } 1456 // Fall through. 1457 case ObjectLiteral::Property::PROTOTYPE: 1458 __ push(Operand(rsp, 0)); // Duplicate receiver. 1459 VisitForStackValue(key); 1460 VisitForStackValue(value); 1461 if (property->emit_store()) { 1462 __ Push(Smi::FromInt(NONE)); // PropertyAttributes 1463 __ CallRuntime(Runtime::kSetProperty, 4); 1464 } else { 1465 __ Drop(3); 1466 } 1467 break; 1468 case ObjectLiteral::Property::GETTER: 1469 accessor_table.lookup(key)->second->getter = value; 1470 break; 1471 case ObjectLiteral::Property::SETTER: 1472 accessor_table.lookup(key)->second->setter = value; 1473 break; 1474 } 1475 } 1476 1477 // Emit code to define accessors, using only a single call to the runtime for 1478 // each pair of corresponding getters and setters. 1479 for (AccessorTable::Iterator it = accessor_table.begin(); 1480 it != accessor_table.end(); 1481 ++it) { 1482 __ push(Operand(rsp, 0)); // Duplicate receiver. 1483 VisitForStackValue(it->first); 1484 EmitAccessor(it->second->getter); 1485 EmitAccessor(it->second->setter); 1486 __ Push(Smi::FromInt(NONE)); 1487 __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); 1488 } 1489 1490 if (expr->has_function()) { 1491 ASSERT(result_saved); 1492 __ push(Operand(rsp, 0)); 1493 __ CallRuntime(Runtime::kToFastProperties, 1); 1494 } 1495 1496 if (result_saved) { 1497 context()->PlugTOS(); 1498 } else { 1499 context()->Plug(rax); 1500 } 1501} 1502 1503 1504void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { 1505 Comment cmnt(masm_, "[ ArrayLiteral"); 1506 1507 ZoneList<Expression*>* subexprs = expr->values(); 1508 int length = subexprs->length(); 1509 Handle<FixedArray> constant_elements = expr->constant_elements(); 1510 ASSERT_EQ(2, constant_elements->length()); 1511 ElementsKind constant_elements_kind = 1512 static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); 1513 bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; 1514 Handle<FixedArrayBase> constant_elements_values( 1515 FixedArrayBase::cast(constant_elements->get(1))); 1516 1517 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 1518 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); 1519 __ Push(Smi::FromInt(expr->literal_index())); 1520 __ Push(constant_elements); 1521 Heap* heap = isolate()->heap(); 1522 if (has_constant_fast_elements && 1523 constant_elements_values->map() == heap->fixed_cow_array_map()) { 1524 // If the elements are already FAST_ELEMENTS, the boilerplate cannot 1525 // change, so it's possible to specialize the stub in advance. 1526 __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); 1527 FastCloneShallowArrayStub stub( 1528 FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, 1529 length); 1530 __ CallStub(&stub); 1531 } else if (expr->depth() > 1) { 1532 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); 1533 } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { 1534 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); 1535 } else { 1536 ASSERT(constant_elements_kind == FAST_ELEMENTS || 1537 constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || 1538 FLAG_smi_only_arrays); 1539 // If the elements are already FAST_ELEMENTS, the boilerplate cannot 1540 // change, so it's possible to specialize the stub in advance. 1541 FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements 1542 ? FastCloneShallowArrayStub::CLONE_ELEMENTS 1543 : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; 1544 FastCloneShallowArrayStub stub(mode, length); 1545 __ CallStub(&stub); 1546 } 1547 1548 bool result_saved = false; // Is the result saved to the stack? 1549 1550 // Emit code to evaluate all the non-constant subexpressions and to store 1551 // them into the newly cloned array. 1552 for (int i = 0; i < length; i++) { 1553 Expression* subexpr = subexprs->at(i); 1554 // If the subexpression is a literal or a simple materialized literal it 1555 // is already set in the cloned array. 1556 if (subexpr->AsLiteral() != NULL || 1557 CompileTimeValue::IsCompileTimeValue(subexpr)) { 1558 continue; 1559 } 1560 1561 if (!result_saved) { 1562 __ push(rax); 1563 result_saved = true; 1564 } 1565 VisitForAccumulatorValue(subexpr); 1566 1567 if (constant_elements_kind == FAST_ELEMENTS) { 1568 // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot 1569 // transition and don't need to call the runtime stub. 1570 int offset = FixedArray::kHeaderSize + (i * kPointerSize); 1571 __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. 1572 __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); 1573 // Store the subexpression value in the array's elements. 1574 __ movq(FieldOperand(rbx, offset), result_register()); 1575 // Update the write barrier for the array store. 1576 __ RecordWriteField(rbx, offset, result_register(), rcx, 1577 kDontSaveFPRegs, 1578 EMIT_REMEMBERED_SET, 1579 INLINE_SMI_CHECK); 1580 } else { 1581 // Store the subexpression value in the array's elements. 1582 __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. 1583 __ movq(rdi, FieldOperand(rbx, JSObject::kMapOffset)); 1584 __ Move(rcx, Smi::FromInt(i)); 1585 __ Move(rdx, Smi::FromInt(expr->literal_index())); 1586 StoreArrayLiteralElementStub stub; 1587 __ CallStub(&stub); 1588 } 1589 1590 PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); 1591 } 1592 1593 if (result_saved) { 1594 context()->PlugTOS(); 1595 } else { 1596 context()->Plug(rax); 1597 } 1598} 1599 1600 1601void FullCodeGenerator::VisitAssignment(Assignment* expr) { 1602 Comment cmnt(masm_, "[ Assignment"); 1603 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' 1604 // on the left-hand side. 1605 if (!expr->target()->IsValidLeftHandSide()) { 1606 VisitForEffect(expr->target()); 1607 return; 1608 } 1609 1610 // Left-hand side can only be a property, a global or a (parameter or local) 1611 // slot. 1612 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; 1613 LhsKind assign_type = VARIABLE; 1614 Property* property = expr->target()->AsProperty(); 1615 if (property != NULL) { 1616 assign_type = (property->key()->IsPropertyName()) 1617 ? NAMED_PROPERTY 1618 : KEYED_PROPERTY; 1619 } 1620 1621 // Evaluate LHS expression. 1622 switch (assign_type) { 1623 case VARIABLE: 1624 // Nothing to do here. 1625 break; 1626 case NAMED_PROPERTY: 1627 if (expr->is_compound()) { 1628 // We need the receiver both on the stack and in the accumulator. 1629 VisitForAccumulatorValue(property->obj()); 1630 __ push(result_register()); 1631 } else { 1632 VisitForStackValue(property->obj()); 1633 } 1634 break; 1635 case KEYED_PROPERTY: { 1636 if (expr->is_compound()) { 1637 VisitForStackValue(property->obj()); 1638 VisitForAccumulatorValue(property->key()); 1639 __ movq(rdx, Operand(rsp, 0)); 1640 __ push(rax); 1641 } else { 1642 VisitForStackValue(property->obj()); 1643 VisitForStackValue(property->key()); 1644 } 1645 break; 1646 } 1647 } 1648 1649 // For compound assignments we need another deoptimization point after the 1650 // variable/property load. 1651 if (expr->is_compound()) { 1652 { AccumulatorValueContext context(this); 1653 switch (assign_type) { 1654 case VARIABLE: 1655 EmitVariableLoad(expr->target()->AsVariableProxy()); 1656 PrepareForBailout(expr->target(), TOS_REG); 1657 break; 1658 case NAMED_PROPERTY: 1659 EmitNamedPropertyLoad(property); 1660 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); 1661 break; 1662 case KEYED_PROPERTY: 1663 EmitKeyedPropertyLoad(property); 1664 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); 1665 break; 1666 } 1667 } 1668 1669 Token::Value op = expr->binary_op(); 1670 __ push(rax); // Left operand goes on the stack. 1671 VisitForAccumulatorValue(expr->value()); 1672 1673 OverwriteMode mode = expr->value()->ResultOverwriteAllowed() 1674 ? OVERWRITE_RIGHT 1675 : NO_OVERWRITE; 1676 SetSourcePosition(expr->position() + 1); 1677 AccumulatorValueContext context(this); 1678 if (ShouldInlineSmiCase(op)) { 1679 EmitInlineSmiBinaryOp(expr->binary_operation(), 1680 op, 1681 mode, 1682 expr->target(), 1683 expr->value()); 1684 } else { 1685 EmitBinaryOp(expr->binary_operation(), op, mode); 1686 } 1687 // Deoptimization point in case the binary operation may have side effects. 1688 PrepareForBailout(expr->binary_operation(), TOS_REG); 1689 } else { 1690 VisitForAccumulatorValue(expr->value()); 1691 } 1692 1693 // Record source position before possible IC call. 1694 SetSourcePosition(expr->position()); 1695 1696 // Store the value. 1697 switch (assign_type) { 1698 case VARIABLE: 1699 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), 1700 expr->op()); 1701 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 1702 context()->Plug(rax); 1703 break; 1704 case NAMED_PROPERTY: 1705 EmitNamedPropertyAssignment(expr); 1706 break; 1707 case KEYED_PROPERTY: 1708 EmitKeyedPropertyAssignment(expr); 1709 break; 1710 } 1711} 1712 1713 1714void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { 1715 SetSourcePosition(prop->position()); 1716 Literal* key = prop->key()->AsLiteral(); 1717 __ Move(rcx, key->handle()); 1718 Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); 1719 __ call(ic, RelocInfo::CODE_TARGET, prop->id()); 1720} 1721 1722 1723void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { 1724 SetSourcePosition(prop->position()); 1725 Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); 1726 __ call(ic, RelocInfo::CODE_TARGET, prop->id()); 1727} 1728 1729 1730void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, 1731 Token::Value op, 1732 OverwriteMode mode, 1733 Expression* left, 1734 Expression* right) { 1735 // Do combined smi check of the operands. Left operand is on the 1736 // stack (popped into rdx). Right operand is in rax but moved into 1737 // rcx to make the shifts easier. 1738 Label done, stub_call, smi_case; 1739 __ pop(rdx); 1740 __ movq(rcx, rax); 1741 __ or_(rax, rdx); 1742 JumpPatchSite patch_site(masm_); 1743 patch_site.EmitJumpIfSmi(rax, &smi_case, Label::kNear); 1744 1745 __ bind(&stub_call); 1746 __ movq(rax, rcx); 1747 BinaryOpStub stub(op, mode); 1748 __ call(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); 1749 patch_site.EmitPatchInfo(); 1750 __ jmp(&done, Label::kNear); 1751 1752 __ bind(&smi_case); 1753 switch (op) { 1754 case Token::SAR: 1755 __ SmiShiftArithmeticRight(rax, rdx, rcx); 1756 break; 1757 case Token::SHL: 1758 __ SmiShiftLeft(rax, rdx, rcx); 1759 break; 1760 case Token::SHR: 1761 __ SmiShiftLogicalRight(rax, rdx, rcx, &stub_call); 1762 break; 1763 case Token::ADD: 1764 __ SmiAdd(rax, rdx, rcx, &stub_call); 1765 break; 1766 case Token::SUB: 1767 __ SmiSub(rax, rdx, rcx, &stub_call); 1768 break; 1769 case Token::MUL: 1770 __ SmiMul(rax, rdx, rcx, &stub_call); 1771 break; 1772 case Token::BIT_OR: 1773 __ SmiOr(rax, rdx, rcx); 1774 break; 1775 case Token::BIT_AND: 1776 __ SmiAnd(rax, rdx, rcx); 1777 break; 1778 case Token::BIT_XOR: 1779 __ SmiXor(rax, rdx, rcx); 1780 break; 1781 default: 1782 UNREACHABLE(); 1783 break; 1784 } 1785 1786 __ bind(&done); 1787 context()->Plug(rax); 1788} 1789 1790 1791void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, 1792 Token::Value op, 1793 OverwriteMode mode) { 1794 __ pop(rdx); 1795 BinaryOpStub stub(op, mode); 1796 JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. 1797 __ call(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); 1798 patch_site.EmitPatchInfo(); 1799 context()->Plug(rax); 1800} 1801 1802 1803void FullCodeGenerator::EmitAssignment(Expression* expr) { 1804 // Invalid left-hand sides are rewritten to have a 'throw 1805 // ReferenceError' on the left-hand side. 1806 if (!expr->IsValidLeftHandSide()) { 1807 VisitForEffect(expr); 1808 return; 1809 } 1810 1811 // Left-hand side can only be a property, a global or a (parameter or local) 1812 // slot. 1813 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; 1814 LhsKind assign_type = VARIABLE; 1815 Property* prop = expr->AsProperty(); 1816 if (prop != NULL) { 1817 assign_type = (prop->key()->IsPropertyName()) 1818 ? NAMED_PROPERTY 1819 : KEYED_PROPERTY; 1820 } 1821 1822 switch (assign_type) { 1823 case VARIABLE: { 1824 Variable* var = expr->AsVariableProxy()->var(); 1825 EffectContext context(this); 1826 EmitVariableAssignment(var, Token::ASSIGN); 1827 break; 1828 } 1829 case NAMED_PROPERTY: { 1830 __ push(rax); // Preserve value. 1831 VisitForAccumulatorValue(prop->obj()); 1832 __ movq(rdx, rax); 1833 __ pop(rax); // Restore value. 1834 __ Move(rcx, prop->key()->AsLiteral()->handle()); 1835 Handle<Code> ic = is_classic_mode() 1836 ? isolate()->builtins()->StoreIC_Initialize() 1837 : isolate()->builtins()->StoreIC_Initialize_Strict(); 1838 __ call(ic); 1839 break; 1840 } 1841 case KEYED_PROPERTY: { 1842 __ push(rax); // Preserve value. 1843 VisitForStackValue(prop->obj()); 1844 VisitForAccumulatorValue(prop->key()); 1845 __ movq(rcx, rax); 1846 __ pop(rdx); 1847 __ pop(rax); // Restore value. 1848 Handle<Code> ic = is_classic_mode() 1849 ? isolate()->builtins()->KeyedStoreIC_Initialize() 1850 : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); 1851 __ call(ic); 1852 break; 1853 } 1854 } 1855 context()->Plug(rax); 1856} 1857 1858 1859void FullCodeGenerator::EmitVariableAssignment(Variable* var, 1860 Token::Value op) { 1861 if (var->IsUnallocated()) { 1862 // Global var, const, or let. 1863 __ Move(rcx, var->name()); 1864 __ movq(rdx, GlobalObjectOperand()); 1865 Handle<Code> ic = is_classic_mode() 1866 ? isolate()->builtins()->StoreIC_Initialize() 1867 : isolate()->builtins()->StoreIC_Initialize_Strict(); 1868 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); 1869 } else if (op == Token::INIT_CONST) { 1870 // Const initializers need a write barrier. 1871 ASSERT(!var->IsParameter()); // No const parameters. 1872 if (var->IsStackLocal()) { 1873 Label skip; 1874 __ movq(rdx, StackOperand(var)); 1875 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); 1876 __ j(not_equal, &skip); 1877 __ movq(StackOperand(var), rax); 1878 __ bind(&skip); 1879 } else { 1880 ASSERT(var->IsContextSlot() || var->IsLookupSlot()); 1881 // Like var declarations, const declarations are hoisted to function 1882 // scope. However, unlike var initializers, const initializers are 1883 // able to drill a hole to that function context, even from inside a 1884 // 'with' context. We thus bypass the normal static scope lookup for 1885 // var->IsContextSlot(). 1886 __ push(rax); 1887 __ push(rsi); 1888 __ Push(var->name()); 1889 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); 1890 } 1891 1892 } else if (var->mode() == LET && op != Token::INIT_LET) { 1893 // Non-initializing assignment to let variable needs a write barrier. 1894 if (var->IsLookupSlot()) { 1895 __ push(rax); // Value. 1896 __ push(rsi); // Context. 1897 __ Push(var->name()); 1898 __ Push(Smi::FromInt(language_mode())); 1899 __ CallRuntime(Runtime::kStoreContextSlot, 4); 1900 } else { 1901 ASSERT(var->IsStackAllocated() || var->IsContextSlot()); 1902 Label assign; 1903 MemOperand location = VarOperand(var, rcx); 1904 __ movq(rdx, location); 1905 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); 1906 __ j(not_equal, &assign, Label::kNear); 1907 __ Push(var->name()); 1908 __ CallRuntime(Runtime::kThrowReferenceError, 1); 1909 __ bind(&assign); 1910 __ movq(location, rax); 1911 if (var->IsContextSlot()) { 1912 __ movq(rdx, rax); 1913 __ RecordWriteContextSlot( 1914 rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); 1915 } 1916 } 1917 1918 } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { 1919 // Assignment to var or initializing assignment to let/const 1920 // in harmony mode. 1921 if (var->IsStackAllocated() || var->IsContextSlot()) { 1922 MemOperand location = VarOperand(var, rcx); 1923 if (FLAG_debug_code && op == Token::INIT_LET) { 1924 // Check for an uninitialized let binding. 1925 __ movq(rdx, location); 1926 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); 1927 __ Check(equal, "Let binding re-initialization."); 1928 } 1929 // Perform the assignment. 1930 __ movq(location, rax); 1931 if (var->IsContextSlot()) { 1932 __ movq(rdx, rax); 1933 __ RecordWriteContextSlot( 1934 rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); 1935 } 1936 } else { 1937 ASSERT(var->IsLookupSlot()); 1938 __ push(rax); // Value. 1939 __ push(rsi); // Context. 1940 __ Push(var->name()); 1941 __ Push(Smi::FromInt(language_mode())); 1942 __ CallRuntime(Runtime::kStoreContextSlot, 4); 1943 } 1944 } 1945 // Non-initializing assignments to consts are ignored. 1946} 1947 1948 1949void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { 1950 // Assignment to a property, using a named store IC. 1951 Property* prop = expr->target()->AsProperty(); 1952 ASSERT(prop != NULL); 1953 ASSERT(prop->key()->AsLiteral() != NULL); 1954 1955 // If the assignment starts a block of assignments to the same object, 1956 // change to slow case to avoid the quadratic behavior of repeatedly 1957 // adding fast properties. 1958 if (expr->starts_initialization_block()) { 1959 __ push(result_register()); 1960 __ push(Operand(rsp, kPointerSize)); // Receiver is now under value. 1961 __ CallRuntime(Runtime::kToSlowProperties, 1); 1962 __ pop(result_register()); 1963 } 1964 1965 // Record source code position before IC call. 1966 SetSourcePosition(expr->position()); 1967 __ Move(rcx, prop->key()->AsLiteral()->handle()); 1968 if (expr->ends_initialization_block()) { 1969 __ movq(rdx, Operand(rsp, 0)); 1970 } else { 1971 __ pop(rdx); 1972 } 1973 Handle<Code> ic = is_classic_mode() 1974 ? isolate()->builtins()->StoreIC_Initialize() 1975 : isolate()->builtins()->StoreIC_Initialize_Strict(); 1976 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 1977 1978 // If the assignment ends an initialization block, revert to fast case. 1979 if (expr->ends_initialization_block()) { 1980 __ push(rax); // Result of assignment, saved even if not needed. 1981 __ push(Operand(rsp, kPointerSize)); // Receiver is under value. 1982 __ CallRuntime(Runtime::kToFastProperties, 1); 1983 __ pop(rax); 1984 __ Drop(1); 1985 } 1986 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 1987 context()->Plug(rax); 1988} 1989 1990 1991void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { 1992 // Assignment to a property, using a keyed store IC. 1993 1994 // If the assignment starts a block of assignments to the same object, 1995 // change to slow case to avoid the quadratic behavior of repeatedly 1996 // adding fast properties. 1997 if (expr->starts_initialization_block()) { 1998 __ push(result_register()); 1999 // Receiver is now under the key and value. 2000 __ push(Operand(rsp, 2 * kPointerSize)); 2001 __ CallRuntime(Runtime::kToSlowProperties, 1); 2002 __ pop(result_register()); 2003 } 2004 2005 __ pop(rcx); 2006 if (expr->ends_initialization_block()) { 2007 __ movq(rdx, Operand(rsp, 0)); // Leave receiver on the stack for later. 2008 } else { 2009 __ pop(rdx); 2010 } 2011 // Record source code position before IC call. 2012 SetSourcePosition(expr->position()); 2013 Handle<Code> ic = is_classic_mode() 2014 ? isolate()->builtins()->KeyedStoreIC_Initialize() 2015 : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); 2016 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 2017 2018 // If the assignment ends an initialization block, revert to fast case. 2019 if (expr->ends_initialization_block()) { 2020 __ pop(rdx); 2021 __ push(rax); // Result of assignment, saved even if not needed. 2022 __ push(rdx); 2023 __ CallRuntime(Runtime::kToFastProperties, 1); 2024 __ pop(rax); 2025 } 2026 2027 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 2028 context()->Plug(rax); 2029} 2030 2031 2032void FullCodeGenerator::VisitProperty(Property* expr) { 2033 Comment cmnt(masm_, "[ Property"); 2034 Expression* key = expr->key(); 2035 2036 if (key->IsPropertyName()) { 2037 VisitForAccumulatorValue(expr->obj()); 2038 EmitNamedPropertyLoad(expr); 2039 context()->Plug(rax); 2040 } else { 2041 VisitForStackValue(expr->obj()); 2042 VisitForAccumulatorValue(expr->key()); 2043 __ pop(rdx); 2044 EmitKeyedPropertyLoad(expr); 2045 context()->Plug(rax); 2046 } 2047} 2048 2049 2050void FullCodeGenerator::EmitCallWithIC(Call* expr, 2051 Handle<Object> name, 2052 RelocInfo::Mode mode) { 2053 // Code common for calls using the IC. 2054 ZoneList<Expression*>* args = expr->arguments(); 2055 int arg_count = args->length(); 2056 { PreservePositionScope scope(masm()->positions_recorder()); 2057 for (int i = 0; i < arg_count; i++) { 2058 VisitForStackValue(args->at(i)); 2059 } 2060 __ Move(rcx, name); 2061 } 2062 // Record source position for debugger. 2063 SetSourcePosition(expr->position()); 2064 // Call the IC initialization code. 2065 Handle<Code> ic = 2066 isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode); 2067 __ call(ic, mode, expr->id()); 2068 RecordJSReturnSite(expr); 2069 // Restore context register. 2070 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 2071 context()->Plug(rax); 2072} 2073 2074 2075void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, 2076 Expression* key) { 2077 // Load the key. 2078 VisitForAccumulatorValue(key); 2079 2080 // Swap the name of the function and the receiver on the stack to follow 2081 // the calling convention for call ICs. 2082 __ pop(rcx); 2083 __ push(rax); 2084 __ push(rcx); 2085 2086 // Load the arguments. 2087 ZoneList<Expression*>* args = expr->arguments(); 2088 int arg_count = args->length(); 2089 { PreservePositionScope scope(masm()->positions_recorder()); 2090 for (int i = 0; i < arg_count; i++) { 2091 VisitForStackValue(args->at(i)); 2092 } 2093 } 2094 // Record source position for debugger. 2095 SetSourcePosition(expr->position()); 2096 // Call the IC initialization code. 2097 Handle<Code> ic = 2098 isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count); 2099 __ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key. 2100 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 2101 RecordJSReturnSite(expr); 2102 // Restore context register. 2103 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 2104 context()->DropAndPlug(1, rax); // Drop the key still on the stack. 2105} 2106 2107 2108void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { 2109 // Code common for calls using the call stub. 2110 ZoneList<Expression*>* args = expr->arguments(); 2111 int arg_count = args->length(); 2112 { PreservePositionScope scope(masm()->positions_recorder()); 2113 for (int i = 0; i < arg_count; i++) { 2114 VisitForStackValue(args->at(i)); 2115 } 2116 } 2117 // Record source position for debugger. 2118 SetSourcePosition(expr->position()); 2119 CallFunctionStub stub(arg_count, flags); 2120 __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); 2121 __ CallStub(&stub); 2122 RecordJSReturnSite(expr); 2123 // Restore context register. 2124 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 2125 // Discard the function left on TOS. 2126 context()->DropAndPlug(1, rax); 2127} 2128 2129 2130void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { 2131 // Push copy of the first argument or undefined if it doesn't exist. 2132 if (arg_count > 0) { 2133 __ push(Operand(rsp, arg_count * kPointerSize)); 2134 } else { 2135 __ PushRoot(Heap::kUndefinedValueRootIndex); 2136 } 2137 2138 // Push the receiver of the enclosing function and do runtime call. 2139 __ push(Operand(rbp, (2 + info_->scope()->num_parameters()) * kPointerSize)); 2140 2141 // Push the language mode. 2142 __ Push(Smi::FromInt(language_mode())); 2143 2144 // Push the start position of the scope the calls resides in. 2145 __ Push(Smi::FromInt(scope()->start_position())); 2146 2147 // Do the runtime call. 2148 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); 2149} 2150 2151 2152void FullCodeGenerator::VisitCall(Call* expr) { 2153#ifdef DEBUG 2154 // We want to verify that RecordJSReturnSite gets called on all paths 2155 // through this function. Avoid early returns. 2156 expr->return_is_recorded_ = false; 2157#endif 2158 2159 Comment cmnt(masm_, "[ Call"); 2160 Expression* callee = expr->expression(); 2161 VariableProxy* proxy = callee->AsVariableProxy(); 2162 Property* property = callee->AsProperty(); 2163 2164 if (proxy != NULL && proxy->var()->is_possibly_eval()) { 2165 // In a call to eval, we first call %ResolvePossiblyDirectEval to 2166 // resolve the function we need to call and the receiver of the call. 2167 // Then we call the resolved function using the given arguments. 2168 ZoneList<Expression*>* args = expr->arguments(); 2169 int arg_count = args->length(); 2170 { PreservePositionScope pos_scope(masm()->positions_recorder()); 2171 VisitForStackValue(callee); 2172 __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. 2173 2174 // Push the arguments. 2175 for (int i = 0; i < arg_count; i++) { 2176 VisitForStackValue(args->at(i)); 2177 } 2178 2179 // Push a copy of the function (found below the arguments) and resolve 2180 // eval. 2181 __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); 2182 EmitResolvePossiblyDirectEval(arg_count); 2183 2184 // The runtime call returns a pair of values in rax (function) and 2185 // rdx (receiver). Touch up the stack with the right values. 2186 __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx); 2187 __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax); 2188 } 2189 // Record source position for debugger. 2190 SetSourcePosition(expr->position()); 2191 CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); 2192 __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); 2193 __ CallStub(&stub); 2194 RecordJSReturnSite(expr); 2195 // Restore context register. 2196 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 2197 context()->DropAndPlug(1, rax); 2198 } else if (proxy != NULL && proxy->var()->IsUnallocated()) { 2199 // Call to a global variable. Push global object as receiver for the 2200 // call IC lookup. 2201 __ push(GlobalObjectOperand()); 2202 EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); 2203 } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { 2204 // Call to a lookup slot (dynamically introduced variable). 2205 Label slow, done; 2206 2207 { PreservePositionScope scope(masm()->positions_recorder()); 2208 // Generate code for loading from variables potentially shadowed by 2209 // eval-introduced variables. 2210 EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); 2211 } 2212 __ bind(&slow); 2213 // Call the runtime to find the function to call (returned in rax) and 2214 // the object holding it (returned in rdx). 2215 __ push(context_register()); 2216 __ Push(proxy->name()); 2217 __ CallRuntime(Runtime::kLoadContextSlot, 2); 2218 __ push(rax); // Function. 2219 __ push(rdx); // Receiver. 2220 2221 // If fast case code has been generated, emit code to push the function 2222 // and receiver and have the slow path jump around this code. 2223 if (done.is_linked()) { 2224 Label call; 2225 __ jmp(&call, Label::kNear); 2226 __ bind(&done); 2227 // Push function. 2228 __ push(rax); 2229 // The receiver is implicitly the global receiver. Indicate this by 2230 // passing the hole to the call function stub. 2231 __ PushRoot(Heap::kTheHoleValueRootIndex); 2232 __ bind(&call); 2233 } 2234 2235 // The receiver is either the global receiver or an object found by 2236 // LoadContextSlot. That object could be the hole if the receiver is 2237 // implicitly the global object. 2238 EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT); 2239 } else if (property != NULL) { 2240 { PreservePositionScope scope(masm()->positions_recorder()); 2241 VisitForStackValue(property->obj()); 2242 } 2243 if (property->key()->IsPropertyName()) { 2244 EmitCallWithIC(expr, 2245 property->key()->AsLiteral()->handle(), 2246 RelocInfo::CODE_TARGET); 2247 } else { 2248 EmitKeyedCallWithIC(expr, property->key()); 2249 } 2250 } else { 2251 // Call to an arbitrary expression not handled specially above. 2252 { PreservePositionScope scope(masm()->positions_recorder()); 2253 VisitForStackValue(callee); 2254 } 2255 // Load global receiver object. 2256 __ movq(rbx, GlobalObjectOperand()); 2257 __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); 2258 // Emit function call. 2259 EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS); 2260 } 2261 2262#ifdef DEBUG 2263 // RecordJSReturnSite should have been called. 2264 ASSERT(expr->return_is_recorded_); 2265#endif 2266} 2267 2268 2269void FullCodeGenerator::VisitCallNew(CallNew* expr) { 2270 Comment cmnt(masm_, "[ CallNew"); 2271 // According to ECMA-262, section 11.2.2, page 44, the function 2272 // expression in new calls must be evaluated before the 2273 // arguments. 2274 2275 // Push constructor on the stack. If it's not a function it's used as 2276 // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is 2277 // ignored. 2278 VisitForStackValue(expr->expression()); 2279 2280 // Push the arguments ("left-to-right") on the stack. 2281 ZoneList<Expression*>* args = expr->arguments(); 2282 int arg_count = args->length(); 2283 for (int i = 0; i < arg_count; i++) { 2284 VisitForStackValue(args->at(i)); 2285 } 2286 2287 // Call the construct call builtin that handles allocation and 2288 // constructor invocation. 2289 SetSourcePosition(expr->position()); 2290 2291 // Load function and argument count into rdi and rax. 2292 __ Set(rax, arg_count); 2293 __ movq(rdi, Operand(rsp, arg_count * kPointerSize)); 2294 2295 // Record call targets in unoptimized code, but not in the snapshot. 2296 CallFunctionFlags flags; 2297 if (!Serializer::enabled()) { 2298 flags = RECORD_CALL_TARGET; 2299 Handle<Object> uninitialized = 2300 TypeFeedbackCells::UninitializedSentinel(isolate()); 2301 Handle<JSGlobalPropertyCell> cell = 2302 isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); 2303 RecordTypeFeedbackCell(expr->id(), cell); 2304 __ Move(rbx, cell); 2305 } else { 2306 flags = NO_CALL_FUNCTION_FLAGS; 2307 } 2308 2309 CallConstructStub stub(flags); 2310 __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); 2311 PrepareForBailoutForId(expr->ReturnId(), TOS_REG); 2312 context()->Plug(rax); 2313} 2314 2315 2316void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { 2317 ZoneList<Expression*>* args = expr->arguments(); 2318 ASSERT(args->length() == 1); 2319 2320 VisitForAccumulatorValue(args->at(0)); 2321 2322 Label materialize_true, materialize_false; 2323 Label* if_true = NULL; 2324 Label* if_false = NULL; 2325 Label* fall_through = NULL; 2326 context()->PrepareTest(&materialize_true, &materialize_false, 2327 &if_true, &if_false, &fall_through); 2328 2329 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2330 __ JumpIfSmi(rax, if_true); 2331 __ jmp(if_false); 2332 2333 context()->Plug(if_true, if_false); 2334} 2335 2336 2337void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { 2338 ZoneList<Expression*>* args = expr->arguments(); 2339 ASSERT(args->length() == 1); 2340 2341 VisitForAccumulatorValue(args->at(0)); 2342 2343 Label materialize_true, materialize_false; 2344 Label* if_true = NULL; 2345 Label* if_false = NULL; 2346 Label* fall_through = NULL; 2347 context()->PrepareTest(&materialize_true, &materialize_false, 2348 &if_true, &if_false, &fall_through); 2349 2350 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2351 Condition non_negative_smi = masm()->CheckNonNegativeSmi(rax); 2352 Split(non_negative_smi, if_true, if_false, fall_through); 2353 2354 context()->Plug(if_true, if_false); 2355} 2356 2357 2358void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { 2359 ZoneList<Expression*>* args = expr->arguments(); 2360 ASSERT(args->length() == 1); 2361 2362 VisitForAccumulatorValue(args->at(0)); 2363 2364 Label materialize_true, materialize_false; 2365 Label* if_true = NULL; 2366 Label* if_false = NULL; 2367 Label* fall_through = NULL; 2368 context()->PrepareTest(&materialize_true, &materialize_false, 2369 &if_true, &if_false, &fall_through); 2370 2371 __ JumpIfSmi(rax, if_false); 2372 __ CompareRoot(rax, Heap::kNullValueRootIndex); 2373 __ j(equal, if_true); 2374 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); 2375 // Undetectable objects behave like undefined when tested with typeof. 2376 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), 2377 Immediate(1 << Map::kIsUndetectable)); 2378 __ j(not_zero, if_false); 2379 __ movzxbq(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); 2380 __ cmpq(rbx, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); 2381 __ j(below, if_false); 2382 __ cmpq(rbx, Immediate(LAST_NONCALLABLE_SPEC_OBJECT_TYPE)); 2383 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2384 Split(below_equal, if_true, if_false, fall_through); 2385 2386 context()->Plug(if_true, if_false); 2387} 2388 2389 2390void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { 2391 ZoneList<Expression*>* args = expr->arguments(); 2392 ASSERT(args->length() == 1); 2393 2394 VisitForAccumulatorValue(args->at(0)); 2395 2396 Label materialize_true, materialize_false; 2397 Label* if_true = NULL; 2398 Label* if_false = NULL; 2399 Label* fall_through = NULL; 2400 context()->PrepareTest(&materialize_true, &materialize_false, 2401 &if_true, &if_false, &fall_through); 2402 2403 __ JumpIfSmi(rax, if_false); 2404 __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rbx); 2405 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2406 Split(above_equal, if_true, if_false, fall_through); 2407 2408 context()->Plug(if_true, if_false); 2409} 2410 2411 2412void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { 2413 ZoneList<Expression*>* args = expr->arguments(); 2414 ASSERT(args->length() == 1); 2415 2416 VisitForAccumulatorValue(args->at(0)); 2417 2418 Label materialize_true, materialize_false; 2419 Label* if_true = NULL; 2420 Label* if_false = NULL; 2421 Label* fall_through = NULL; 2422 context()->PrepareTest(&materialize_true, &materialize_false, 2423 &if_true, &if_false, &fall_through); 2424 2425 __ JumpIfSmi(rax, if_false); 2426 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); 2427 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), 2428 Immediate(1 << Map::kIsUndetectable)); 2429 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2430 Split(not_zero, if_true, if_false, fall_through); 2431 2432 context()->Plug(if_true, if_false); 2433} 2434 2435 2436void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( 2437 CallRuntime* expr) { 2438 ZoneList<Expression*>* args = expr->arguments(); 2439 ASSERT(args->length() == 1); 2440 2441 VisitForAccumulatorValue(args->at(0)); 2442 2443 Label materialize_true, materialize_false; 2444 Label* if_true = NULL; 2445 Label* if_false = NULL; 2446 Label* fall_through = NULL; 2447 context()->PrepareTest(&materialize_true, &materialize_false, 2448 &if_true, &if_false, &fall_through); 2449 2450 if (FLAG_debug_code) __ AbortIfSmi(rax); 2451 2452 // Check whether this map has already been checked to be safe for default 2453 // valueOf. 2454 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); 2455 __ testb(FieldOperand(rbx, Map::kBitField2Offset), 2456 Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf)); 2457 __ j(not_zero, if_true); 2458 2459 // Check for fast case object. Generate false result for slow case object. 2460 __ movq(rcx, FieldOperand(rax, JSObject::kPropertiesOffset)); 2461 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset)); 2462 __ CompareRoot(rcx, Heap::kHashTableMapRootIndex); 2463 __ j(equal, if_false); 2464 2465 // Look for valueOf symbol in the descriptor array, and indicate false if 2466 // found. The type is not checked, so if it is a transition it is a false 2467 // negative. 2468 __ LoadInstanceDescriptors(rbx, rbx); 2469 __ movq(rcx, FieldOperand(rbx, FixedArray::kLengthOffset)); 2470 // rbx: descriptor array 2471 // rcx: length of descriptor array 2472 // Calculate the end of the descriptor array. 2473 SmiIndex index = masm_->SmiToIndex(rdx, rcx, kPointerSizeLog2); 2474 __ lea(rcx, 2475 Operand( 2476 rbx, index.reg, index.scale, FixedArray::kHeaderSize)); 2477 // Calculate location of the first key name. 2478 __ addq(rbx, 2479 Immediate(FixedArray::kHeaderSize + 2480 DescriptorArray::kFirstIndex * kPointerSize)); 2481 // Loop through all the keys in the descriptor array. If one of these is the 2482 // symbol valueOf the result is false. 2483 Label entry, loop; 2484 __ jmp(&entry); 2485 __ bind(&loop); 2486 __ movq(rdx, FieldOperand(rbx, 0)); 2487 __ Cmp(rdx, FACTORY->value_of_symbol()); 2488 __ j(equal, if_false); 2489 __ addq(rbx, Immediate(kPointerSize)); 2490 __ bind(&entry); 2491 __ cmpq(rbx, rcx); 2492 __ j(not_equal, &loop); 2493 2494 // Reload map as register rbx was used as temporary above. 2495 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); 2496 2497 // If a valueOf property is not found on the object check that it's 2498 // prototype is the un-modified String prototype. If not result is false. 2499 __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); 2500 __ testq(rcx, Immediate(kSmiTagMask)); 2501 __ j(zero, if_false); 2502 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset)); 2503 __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); 2504 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset)); 2505 __ cmpq(rcx, 2506 ContextOperand(rdx, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); 2507 __ j(not_equal, if_false); 2508 // Set the bit in the map to indicate that it has been checked safe for 2509 // default valueOf and set true result. 2510 __ or_(FieldOperand(rbx, Map::kBitField2Offset), 2511 Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf)); 2512 __ jmp(if_true); 2513 2514 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2515 context()->Plug(if_true, if_false); 2516} 2517 2518 2519void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { 2520 ZoneList<Expression*>* args = expr->arguments(); 2521 ASSERT(args->length() == 1); 2522 2523 VisitForAccumulatorValue(args->at(0)); 2524 2525 Label materialize_true, materialize_false; 2526 Label* if_true = NULL; 2527 Label* if_false = NULL; 2528 Label* fall_through = NULL; 2529 context()->PrepareTest(&materialize_true, &materialize_false, 2530 &if_true, &if_false, &fall_through); 2531 2532 __ JumpIfSmi(rax, if_false); 2533 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); 2534 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2535 Split(equal, if_true, if_false, fall_through); 2536 2537 context()->Plug(if_true, if_false); 2538} 2539 2540 2541void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { 2542 ZoneList<Expression*>* args = expr->arguments(); 2543 ASSERT(args->length() == 1); 2544 2545 VisitForAccumulatorValue(args->at(0)); 2546 2547 Label materialize_true, materialize_false; 2548 Label* if_true = NULL; 2549 Label* if_false = NULL; 2550 Label* fall_through = NULL; 2551 context()->PrepareTest(&materialize_true, &materialize_false, 2552 &if_true, &if_false, &fall_through); 2553 2554 __ JumpIfSmi(rax, if_false); 2555 __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx); 2556 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2557 Split(equal, if_true, if_false, fall_through); 2558 2559 context()->Plug(if_true, if_false); 2560} 2561 2562 2563void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { 2564 ZoneList<Expression*>* args = expr->arguments(); 2565 ASSERT(args->length() == 1); 2566 2567 VisitForAccumulatorValue(args->at(0)); 2568 2569 Label materialize_true, materialize_false; 2570 Label* if_true = NULL; 2571 Label* if_false = NULL; 2572 Label* fall_through = NULL; 2573 context()->PrepareTest(&materialize_true, &materialize_false, 2574 &if_true, &if_false, &fall_through); 2575 2576 __ JumpIfSmi(rax, if_false); 2577 __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx); 2578 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2579 Split(equal, if_true, if_false, fall_through); 2580 2581 context()->Plug(if_true, if_false); 2582} 2583 2584 2585 2586void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { 2587 ASSERT(expr->arguments()->length() == 0); 2588 2589 Label materialize_true, materialize_false; 2590 Label* if_true = NULL; 2591 Label* if_false = NULL; 2592 Label* fall_through = NULL; 2593 context()->PrepareTest(&materialize_true, &materialize_false, 2594 &if_true, &if_false, &fall_through); 2595 2596 // Get the frame pointer for the calling frame. 2597 __ movq(rax, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); 2598 2599 // Skip the arguments adaptor frame if it exists. 2600 Label check_frame_marker; 2601 __ Cmp(Operand(rax, StandardFrameConstants::kContextOffset), 2602 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 2603 __ j(not_equal, &check_frame_marker); 2604 __ movq(rax, Operand(rax, StandardFrameConstants::kCallerFPOffset)); 2605 2606 // Check the marker in the calling frame. 2607 __ bind(&check_frame_marker); 2608 __ Cmp(Operand(rax, StandardFrameConstants::kMarkerOffset), 2609 Smi::FromInt(StackFrame::CONSTRUCT)); 2610 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2611 Split(equal, if_true, if_false, fall_through); 2612 2613 context()->Plug(if_true, if_false); 2614} 2615 2616 2617void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { 2618 ZoneList<Expression*>* args = expr->arguments(); 2619 ASSERT(args->length() == 2); 2620 2621 // Load the two objects into registers and perform the comparison. 2622 VisitForStackValue(args->at(0)); 2623 VisitForAccumulatorValue(args->at(1)); 2624 2625 Label materialize_true, materialize_false; 2626 Label* if_true = NULL; 2627 Label* if_false = NULL; 2628 Label* fall_through = NULL; 2629 context()->PrepareTest(&materialize_true, &materialize_false, 2630 &if_true, &if_false, &fall_through); 2631 2632 __ pop(rbx); 2633 __ cmpq(rax, rbx); 2634 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2635 Split(equal, if_true, if_false, fall_through); 2636 2637 context()->Plug(if_true, if_false); 2638} 2639 2640 2641void FullCodeGenerator::EmitArguments(CallRuntime* expr) { 2642 ZoneList<Expression*>* args = expr->arguments(); 2643 ASSERT(args->length() == 1); 2644 2645 // ArgumentsAccessStub expects the key in rdx and the formal 2646 // parameter count in rax. 2647 VisitForAccumulatorValue(args->at(0)); 2648 __ movq(rdx, rax); 2649 __ Move(rax, Smi::FromInt(info_->scope()->num_parameters())); 2650 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); 2651 __ CallStub(&stub); 2652 context()->Plug(rax); 2653} 2654 2655 2656void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { 2657 ASSERT(expr->arguments()->length() == 0); 2658 2659 Label exit; 2660 // Get the number of formal parameters. 2661 __ Move(rax, Smi::FromInt(info_->scope()->num_parameters())); 2662 2663 // Check if the calling frame is an arguments adaptor frame. 2664 __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); 2665 __ Cmp(Operand(rbx, StandardFrameConstants::kContextOffset), 2666 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 2667 __ j(not_equal, &exit, Label::kNear); 2668 2669 // Arguments adaptor case: Read the arguments length from the 2670 // adaptor frame. 2671 __ movq(rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); 2672 2673 __ bind(&exit); 2674 if (FLAG_debug_code) __ AbortIfNotSmi(rax); 2675 context()->Plug(rax); 2676} 2677 2678 2679void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { 2680 ZoneList<Expression*>* args = expr->arguments(); 2681 ASSERT(args->length() == 1); 2682 Label done, null, function, non_function_constructor; 2683 2684 VisitForAccumulatorValue(args->at(0)); 2685 2686 // If the object is a smi, we return null. 2687 __ JumpIfSmi(rax, &null); 2688 2689 // Check that the object is a JS object but take special care of JS 2690 // functions to make sure they have 'Function' as their class. 2691 // Assume that there are only two callable types, and one of them is at 2692 // either end of the type range for JS object types. Saves extra comparisons. 2693 STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); 2694 __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rax); 2695 // Map is now in rax. 2696 __ j(below, &null); 2697 STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == 2698 FIRST_SPEC_OBJECT_TYPE + 1); 2699 __ j(equal, &function); 2700 2701 __ CmpInstanceType(rax, LAST_SPEC_OBJECT_TYPE); 2702 STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == 2703 LAST_SPEC_OBJECT_TYPE - 1); 2704 __ j(equal, &function); 2705 // Assume that there is no larger type. 2706 STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); 2707 2708 // Check if the constructor in the map is a JS function. 2709 __ movq(rax, FieldOperand(rax, Map::kConstructorOffset)); 2710 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); 2711 __ j(not_equal, &non_function_constructor); 2712 2713 // rax now contains the constructor function. Grab the 2714 // instance class name from there. 2715 __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset)); 2716 __ movq(rax, FieldOperand(rax, SharedFunctionInfo::kInstanceClassNameOffset)); 2717 __ jmp(&done); 2718 2719 // Functions have class 'Function'. 2720 __ bind(&function); 2721 __ Move(rax, isolate()->factory()->function_class_symbol()); 2722 __ jmp(&done); 2723 2724 // Objects with a non-function constructor have class 'Object'. 2725 __ bind(&non_function_constructor); 2726 __ Move(rax, isolate()->factory()->Object_symbol()); 2727 __ jmp(&done); 2728 2729 // Non-JS objects have class null. 2730 __ bind(&null); 2731 __ LoadRoot(rax, Heap::kNullValueRootIndex); 2732 2733 // All done. 2734 __ bind(&done); 2735 2736 context()->Plug(rax); 2737} 2738 2739 2740void FullCodeGenerator::EmitLog(CallRuntime* expr) { 2741 // Conditionally generate a log call. 2742 // Args: 2743 // 0 (literal string): The type of logging (corresponds to the flags). 2744 // This is used to determine whether or not to generate the log call. 2745 // 1 (string): Format string. Access the string at argument index 2 2746 // with '%2s' (see Logger::LogRuntime for all the formats). 2747 // 2 (array): Arguments to the format string. 2748 ZoneList<Expression*>* args = expr->arguments(); 2749 ASSERT_EQ(args->length(), 3); 2750 if (CodeGenerator::ShouldGenerateLog(args->at(0))) { 2751 VisitForStackValue(args->at(1)); 2752 VisitForStackValue(args->at(2)); 2753 __ CallRuntime(Runtime::kLog, 2); 2754 } 2755 // Finally, we're expected to leave a value on the top of the stack. 2756 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 2757 context()->Plug(rax); 2758} 2759 2760 2761void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { 2762 ASSERT(expr->arguments()->length() == 0); 2763 2764 Label slow_allocate_heapnumber; 2765 Label heapnumber_allocated; 2766 2767 __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber); 2768 __ jmp(&heapnumber_allocated); 2769 2770 __ bind(&slow_allocate_heapnumber); 2771 // Allocate a heap number. 2772 __ CallRuntime(Runtime::kNumberAlloc, 0); 2773 __ movq(rbx, rax); 2774 2775 __ bind(&heapnumber_allocated); 2776 2777 // Return a random uint32 number in rax. 2778 // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. 2779 __ PrepareCallCFunction(1); 2780#ifdef _WIN64 2781 __ movq(rcx, ContextOperand(context_register(), Context::GLOBAL_INDEX)); 2782 __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); 2783 2784#else 2785 __ movq(rdi, ContextOperand(context_register(), Context::GLOBAL_INDEX)); 2786 __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); 2787#endif 2788 __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); 2789 2790 // Convert 32 random bits in rax to 0.(32 random bits) in a double 2791 // by computing: 2792 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). 2793 __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single. 2794 __ movd(xmm1, rcx); 2795 __ movd(xmm0, rax); 2796 __ cvtss2sd(xmm1, xmm1); 2797 __ xorps(xmm0, xmm1); 2798 __ subsd(xmm0, xmm1); 2799 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0); 2800 2801 __ movq(rax, rbx); 2802 context()->Plug(rax); 2803} 2804 2805 2806void FullCodeGenerator::EmitSubString(CallRuntime* expr) { 2807 // Load the arguments on the stack and call the stub. 2808 SubStringStub stub; 2809 ZoneList<Expression*>* args = expr->arguments(); 2810 ASSERT(args->length() == 3); 2811 VisitForStackValue(args->at(0)); 2812 VisitForStackValue(args->at(1)); 2813 VisitForStackValue(args->at(2)); 2814 __ CallStub(&stub); 2815 context()->Plug(rax); 2816} 2817 2818 2819void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { 2820 // Load the arguments on the stack and call the stub. 2821 RegExpExecStub stub; 2822 ZoneList<Expression*>* args = expr->arguments(); 2823 ASSERT(args->length() == 4); 2824 VisitForStackValue(args->at(0)); 2825 VisitForStackValue(args->at(1)); 2826 VisitForStackValue(args->at(2)); 2827 VisitForStackValue(args->at(3)); 2828 __ CallStub(&stub); 2829 context()->Plug(rax); 2830} 2831 2832 2833void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { 2834 ZoneList<Expression*>* args = expr->arguments(); 2835 ASSERT(args->length() == 1); 2836 2837 VisitForAccumulatorValue(args->at(0)); // Load the object. 2838 2839 Label done; 2840 // If the object is a smi return the object. 2841 __ JumpIfSmi(rax, &done); 2842 // If the object is not a value type, return the object. 2843 __ CmpObjectType(rax, JS_VALUE_TYPE, rbx); 2844 __ j(not_equal, &done); 2845 __ movq(rax, FieldOperand(rax, JSValue::kValueOffset)); 2846 2847 __ bind(&done); 2848 context()->Plug(rax); 2849} 2850 2851 2852void FullCodeGenerator::EmitDateField(CallRuntime* expr) { 2853 ZoneList<Expression*>* args = expr->arguments(); 2854 ASSERT(args->length() == 2); 2855 ASSERT_NE(NULL, args->at(1)->AsLiteral()); 2856 Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle())); 2857 2858 VisitForAccumulatorValue(args->at(0)); // Load the object. 2859 2860 Label runtime, done; 2861 Register object = rax; 2862 Register result = rax; 2863 Register scratch = rcx; 2864 2865#ifdef DEBUG 2866 __ AbortIfSmi(object); 2867 __ CmpObjectType(object, JS_DATE_TYPE, scratch); 2868 __ Assert(equal, "Trying to get date field from non-date."); 2869#endif 2870 2871 if (index->value() == 0) { 2872 __ movq(result, FieldOperand(object, JSDate::kValueOffset)); 2873 } else { 2874 if (index->value() < JSDate::kFirstUncachedField) { 2875 ExternalReference stamp = ExternalReference::date_cache_stamp(isolate()); 2876 __ movq(scratch, stamp); 2877 __ cmpq(scratch, FieldOperand(object, JSDate::kCacheStampOffset)); 2878 __ j(not_equal, &runtime, Label::kNear); 2879 __ movq(result, FieldOperand(object, JSDate::kValueOffset + 2880 kPointerSize * index->value())); 2881 __ jmp(&done); 2882 } 2883 __ bind(&runtime); 2884 __ PrepareCallCFunction(2); 2885#ifdef _WIN64 2886 __ movq(rcx, object); 2887 __ movq(rdx, index, RelocInfo::NONE); 2888#else 2889 __ movq(rdi, object); 2890 __ movq(rsi, index, RelocInfo::NONE); 2891#endif 2892 __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2); 2893 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 2894 __ bind(&done); 2895 } 2896 context()->Plug(rax); 2897} 2898 2899 2900void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { 2901 // Load the arguments on the stack and call the runtime function. 2902 ZoneList<Expression*>* args = expr->arguments(); 2903 ASSERT(args->length() == 2); 2904 VisitForStackValue(args->at(0)); 2905 VisitForStackValue(args->at(1)); 2906 MathPowStub stub(MathPowStub::ON_STACK); 2907 __ CallStub(&stub); 2908 context()->Plug(rax); 2909} 2910 2911 2912void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { 2913 ZoneList<Expression*>* args = expr->arguments(); 2914 ASSERT(args->length() == 2); 2915 2916 VisitForStackValue(args->at(0)); // Load the object. 2917 VisitForAccumulatorValue(args->at(1)); // Load the value. 2918 __ pop(rbx); // rax = value. rbx = object. 2919 2920 Label done; 2921 // If the object is a smi, return the value. 2922 __ JumpIfSmi(rbx, &done); 2923 2924 // If the object is not a value type, return the value. 2925 __ CmpObjectType(rbx, JS_VALUE_TYPE, rcx); 2926 __ j(not_equal, &done); 2927 2928 // Store the value. 2929 __ movq(FieldOperand(rbx, JSValue::kValueOffset), rax); 2930 // Update the write barrier. Save the value as it will be 2931 // overwritten by the write barrier code and is needed afterward. 2932 __ movq(rdx, rax); 2933 __ RecordWriteField(rbx, JSValue::kValueOffset, rdx, rcx, kDontSaveFPRegs); 2934 2935 __ bind(&done); 2936 context()->Plug(rax); 2937} 2938 2939 2940void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { 2941 ZoneList<Expression*>* args = expr->arguments(); 2942 ASSERT_EQ(args->length(), 1); 2943 2944 // Load the argument on the stack and call the stub. 2945 VisitForStackValue(args->at(0)); 2946 2947 NumberToStringStub stub; 2948 __ CallStub(&stub); 2949 context()->Plug(rax); 2950} 2951 2952 2953void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { 2954 ZoneList<Expression*>* args = expr->arguments(); 2955 ASSERT(args->length() == 1); 2956 2957 VisitForAccumulatorValue(args->at(0)); 2958 2959 Label done; 2960 StringCharFromCodeGenerator generator(rax, rbx); 2961 generator.GenerateFast(masm_); 2962 __ jmp(&done); 2963 2964 NopRuntimeCallHelper call_helper; 2965 generator.GenerateSlow(masm_, call_helper); 2966 2967 __ bind(&done); 2968 context()->Plug(rbx); 2969} 2970 2971 2972void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { 2973 ZoneList<Expression*>* args = expr->arguments(); 2974 ASSERT(args->length() == 2); 2975 2976 VisitForStackValue(args->at(0)); 2977 VisitForAccumulatorValue(args->at(1)); 2978 2979 Register object = rbx; 2980 Register index = rax; 2981 Register result = rdx; 2982 2983 __ pop(object); 2984 2985 Label need_conversion; 2986 Label index_out_of_range; 2987 Label done; 2988 StringCharCodeAtGenerator generator(object, 2989 index, 2990 result, 2991 &need_conversion, 2992 &need_conversion, 2993 &index_out_of_range, 2994 STRING_INDEX_IS_NUMBER); 2995 generator.GenerateFast(masm_); 2996 __ jmp(&done); 2997 2998 __ bind(&index_out_of_range); 2999 // When the index is out of range, the spec requires us to return 3000 // NaN. 3001 __ LoadRoot(result, Heap::kNanValueRootIndex); 3002 __ jmp(&done); 3003 3004 __ bind(&need_conversion); 3005 // Move the undefined value into the result register, which will 3006 // trigger conversion. 3007 __ LoadRoot(result, Heap::kUndefinedValueRootIndex); 3008 __ jmp(&done); 3009 3010 NopRuntimeCallHelper call_helper; 3011 generator.GenerateSlow(masm_, call_helper); 3012 3013 __ bind(&done); 3014 context()->Plug(result); 3015} 3016 3017 3018void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { 3019 ZoneList<Expression*>* args = expr->arguments(); 3020 ASSERT(args->length() == 2); 3021 3022 VisitForStackValue(args->at(0)); 3023 VisitForAccumulatorValue(args->at(1)); 3024 3025 Register object = rbx; 3026 Register index = rax; 3027 Register scratch = rdx; 3028 Register result = rax; 3029 3030 __ pop(object); 3031 3032 Label need_conversion; 3033 Label index_out_of_range; 3034 Label done; 3035 StringCharAtGenerator generator(object, 3036 index, 3037 scratch, 3038 result, 3039 &need_conversion, 3040 &need_conversion, 3041 &index_out_of_range, 3042 STRING_INDEX_IS_NUMBER); 3043 generator.GenerateFast(masm_); 3044 __ jmp(&done); 3045 3046 __ bind(&index_out_of_range); 3047 // When the index is out of range, the spec requires us to return 3048 // the empty string. 3049 __ LoadRoot(result, Heap::kEmptyStringRootIndex); 3050 __ jmp(&done); 3051 3052 __ bind(&need_conversion); 3053 // Move smi zero into the result register, which will trigger 3054 // conversion. 3055 __ Move(result, Smi::FromInt(0)); 3056 __ jmp(&done); 3057 3058 NopRuntimeCallHelper call_helper; 3059 generator.GenerateSlow(masm_, call_helper); 3060 3061 __ bind(&done); 3062 context()->Plug(result); 3063} 3064 3065 3066void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { 3067 ZoneList<Expression*>* args = expr->arguments(); 3068 ASSERT_EQ(2, args->length()); 3069 3070 VisitForStackValue(args->at(0)); 3071 VisitForStackValue(args->at(1)); 3072 3073 StringAddStub stub(NO_STRING_ADD_FLAGS); 3074 __ CallStub(&stub); 3075 context()->Plug(rax); 3076} 3077 3078 3079void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { 3080 ZoneList<Expression*>* args = expr->arguments(); 3081 ASSERT_EQ(2, args->length()); 3082 3083 VisitForStackValue(args->at(0)); 3084 VisitForStackValue(args->at(1)); 3085 3086 StringCompareStub stub; 3087 __ CallStub(&stub); 3088 context()->Plug(rax); 3089} 3090 3091 3092void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { 3093 // Load the argument on the stack and call the stub. 3094 TranscendentalCacheStub stub(TranscendentalCache::SIN, 3095 TranscendentalCacheStub::TAGGED); 3096 ZoneList<Expression*>* args = expr->arguments(); 3097 ASSERT(args->length() == 1); 3098 VisitForStackValue(args->at(0)); 3099 __ CallStub(&stub); 3100 context()->Plug(rax); 3101} 3102 3103 3104void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { 3105 // Load the argument on the stack and call the stub. 3106 TranscendentalCacheStub stub(TranscendentalCache::COS, 3107 TranscendentalCacheStub::TAGGED); 3108 ZoneList<Expression*>* args = expr->arguments(); 3109 ASSERT(args->length() == 1); 3110 VisitForStackValue(args->at(0)); 3111 __ CallStub(&stub); 3112 context()->Plug(rax); 3113} 3114 3115 3116void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { 3117 // Load the argument on the stack and call the stub. 3118 TranscendentalCacheStub stub(TranscendentalCache::TAN, 3119 TranscendentalCacheStub::TAGGED); 3120 ZoneList<Expression*>* args = expr->arguments(); 3121 ASSERT(args->length() == 1); 3122 VisitForStackValue(args->at(0)); 3123 __ CallStub(&stub); 3124 context()->Plug(rax); 3125} 3126 3127 3128void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { 3129 // Load the argument on the stack and call the stub. 3130 TranscendentalCacheStub stub(TranscendentalCache::LOG, 3131 TranscendentalCacheStub::TAGGED); 3132 ZoneList<Expression*>* args = expr->arguments(); 3133 ASSERT(args->length() == 1); 3134 VisitForStackValue(args->at(0)); 3135 __ CallStub(&stub); 3136 context()->Plug(rax); 3137} 3138 3139 3140void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { 3141 // Load the argument on the stack and call the runtime function. 3142 ZoneList<Expression*>* args = expr->arguments(); 3143 ASSERT(args->length() == 1); 3144 VisitForStackValue(args->at(0)); 3145 __ CallRuntime(Runtime::kMath_sqrt, 1); 3146 context()->Plug(rax); 3147} 3148 3149 3150void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { 3151 ZoneList<Expression*>* args = expr->arguments(); 3152 ASSERT(args->length() >= 2); 3153 3154 int arg_count = args->length() - 2; // 2 ~ receiver and function. 3155 for (int i = 0; i < arg_count + 1; i++) { 3156 VisitForStackValue(args->at(i)); 3157 } 3158 VisitForAccumulatorValue(args->last()); // Function. 3159 3160 // Check for proxy. 3161 Label proxy, done; 3162 __ CmpObjectType(rax, JS_FUNCTION_PROXY_TYPE, rbx); 3163 __ j(equal, &proxy); 3164 3165 // InvokeFunction requires the function in rdi. Move it in there. 3166 __ movq(rdi, result_register()); 3167 ParameterCount count(arg_count); 3168 __ InvokeFunction(rdi, count, CALL_FUNCTION, 3169 NullCallWrapper(), CALL_AS_METHOD); 3170 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 3171 __ jmp(&done); 3172 3173 __ bind(&proxy); 3174 __ push(rax); 3175 __ CallRuntime(Runtime::kCall, args->length()); 3176 __ bind(&done); 3177 3178 context()->Plug(rax); 3179} 3180 3181 3182void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { 3183 RegExpConstructResultStub stub; 3184 ZoneList<Expression*>* args = expr->arguments(); 3185 ASSERT(args->length() == 3); 3186 VisitForStackValue(args->at(0)); 3187 VisitForStackValue(args->at(1)); 3188 VisitForStackValue(args->at(2)); 3189 __ CallStub(&stub); 3190 context()->Plug(rax); 3191} 3192 3193 3194void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { 3195 ZoneList<Expression*>* args = expr->arguments(); 3196 ASSERT_EQ(2, args->length()); 3197 3198 ASSERT_NE(NULL, args->at(0)->AsLiteral()); 3199 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); 3200 3201 Handle<FixedArray> jsfunction_result_caches( 3202 isolate()->global_context()->jsfunction_result_caches()); 3203 if (jsfunction_result_caches->length() <= cache_id) { 3204 __ Abort("Attempt to use undefined cache."); 3205 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 3206 context()->Plug(rax); 3207 return; 3208 } 3209 3210 VisitForAccumulatorValue(args->at(1)); 3211 3212 Register key = rax; 3213 Register cache = rbx; 3214 Register tmp = rcx; 3215 __ movq(cache, ContextOperand(rsi, Context::GLOBAL_INDEX)); 3216 __ movq(cache, 3217 FieldOperand(cache, GlobalObject::kGlobalContextOffset)); 3218 __ movq(cache, 3219 ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); 3220 __ movq(cache, 3221 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); 3222 3223 Label done, not_found; 3224 // tmp now holds finger offset as a smi. 3225 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); 3226 __ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset)); 3227 SmiIndex index = 3228 __ SmiToIndex(kScratchRegister, tmp, kPointerSizeLog2); 3229 __ cmpq(key, FieldOperand(cache, 3230 index.reg, 3231 index.scale, 3232 FixedArray::kHeaderSize)); 3233 __ j(not_equal, ¬_found, Label::kNear); 3234 __ movq(rax, FieldOperand(cache, 3235 index.reg, 3236 index.scale, 3237 FixedArray::kHeaderSize + kPointerSize)); 3238 __ jmp(&done, Label::kNear); 3239 3240 __ bind(¬_found); 3241 // Call runtime to perform the lookup. 3242 __ push(cache); 3243 __ push(key); 3244 __ CallRuntime(Runtime::kGetFromCache, 2); 3245 3246 __ bind(&done); 3247 context()->Plug(rax); 3248} 3249 3250 3251void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { 3252 ZoneList<Expression*>* args = expr->arguments(); 3253 ASSERT_EQ(2, args->length()); 3254 3255 Register right = rax; 3256 Register left = rbx; 3257 Register tmp = rcx; 3258 3259 VisitForStackValue(args->at(0)); 3260 VisitForAccumulatorValue(args->at(1)); 3261 __ pop(left); 3262 3263 Label done, fail, ok; 3264 __ cmpq(left, right); 3265 __ j(equal, &ok, Label::kNear); 3266 // Fail if either is a non-HeapObject. 3267 Condition either_smi = masm()->CheckEitherSmi(left, right, tmp); 3268 __ j(either_smi, &fail, Label::kNear); 3269 __ j(zero, &fail, Label::kNear); 3270 __ movq(tmp, FieldOperand(left, HeapObject::kMapOffset)); 3271 __ cmpb(FieldOperand(tmp, Map::kInstanceTypeOffset), 3272 Immediate(JS_REGEXP_TYPE)); 3273 __ j(not_equal, &fail, Label::kNear); 3274 __ cmpq(tmp, FieldOperand(right, HeapObject::kMapOffset)); 3275 __ j(not_equal, &fail, Label::kNear); 3276 __ movq(tmp, FieldOperand(left, JSRegExp::kDataOffset)); 3277 __ cmpq(tmp, FieldOperand(right, JSRegExp::kDataOffset)); 3278 __ j(equal, &ok, Label::kNear); 3279 __ bind(&fail); 3280 __ Move(rax, isolate()->factory()->false_value()); 3281 __ jmp(&done, Label::kNear); 3282 __ bind(&ok); 3283 __ Move(rax, isolate()->factory()->true_value()); 3284 __ bind(&done); 3285 3286 context()->Plug(rax); 3287} 3288 3289 3290void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { 3291 ZoneList<Expression*>* args = expr->arguments(); 3292 ASSERT(args->length() == 1); 3293 3294 VisitForAccumulatorValue(args->at(0)); 3295 3296 Label materialize_true, materialize_false; 3297 Label* if_true = NULL; 3298 Label* if_false = NULL; 3299 Label* fall_through = NULL; 3300 context()->PrepareTest(&materialize_true, &materialize_false, 3301 &if_true, &if_false, &fall_through); 3302 3303 __ testl(FieldOperand(rax, String::kHashFieldOffset), 3304 Immediate(String::kContainsCachedArrayIndexMask)); 3305 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 3306 __ j(zero, if_true); 3307 __ jmp(if_false); 3308 3309 context()->Plug(if_true, if_false); 3310} 3311 3312 3313void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { 3314 ZoneList<Expression*>* args = expr->arguments(); 3315 ASSERT(args->length() == 1); 3316 VisitForAccumulatorValue(args->at(0)); 3317 3318 if (FLAG_debug_code) { 3319 __ AbortIfNotString(rax); 3320 } 3321 3322 __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); 3323 ASSERT(String::kHashShift >= kSmiTagSize); 3324 __ IndexFromHash(rax, rax); 3325 3326 context()->Plug(rax); 3327} 3328 3329 3330void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { 3331 Label bailout, return_result, done, one_char_separator, long_separator, 3332 non_trivial_array, not_size_one_array, loop, 3333 loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry; 3334 ZoneList<Expression*>* args = expr->arguments(); 3335 ASSERT(args->length() == 2); 3336 // We will leave the separator on the stack until the end of the function. 3337 VisitForStackValue(args->at(1)); 3338 // Load this to rax (= array) 3339 VisitForAccumulatorValue(args->at(0)); 3340 // All aliases of the same register have disjoint lifetimes. 3341 Register array = rax; 3342 Register elements = no_reg; // Will be rax. 3343 3344 Register index = rdx; 3345 3346 Register string_length = rcx; 3347 3348 Register string = rsi; 3349 3350 Register scratch = rbx; 3351 3352 Register array_length = rdi; 3353 Register result_pos = no_reg; // Will be rdi. 3354 3355 Operand separator_operand = Operand(rsp, 2 * kPointerSize); 3356 Operand result_operand = Operand(rsp, 1 * kPointerSize); 3357 Operand array_length_operand = Operand(rsp, 0 * kPointerSize); 3358 // Separator operand is already pushed. Make room for the two 3359 // other stack fields, and clear the direction flag in anticipation 3360 // of calling CopyBytes. 3361 __ subq(rsp, Immediate(2 * kPointerSize)); 3362 __ cld(); 3363 // Check that the array is a JSArray 3364 __ JumpIfSmi(array, &bailout); 3365 __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); 3366 __ j(not_equal, &bailout); 3367 3368 // Check that the array has fast elements. 3369 __ CheckFastElements(scratch, &bailout); 3370 3371 // Array has fast elements, so its length must be a smi. 3372 // If the array has length zero, return the empty string. 3373 __ movq(array_length, FieldOperand(array, JSArray::kLengthOffset)); 3374 __ SmiCompare(array_length, Smi::FromInt(0)); 3375 __ j(not_zero, &non_trivial_array); 3376 __ LoadRoot(rax, Heap::kEmptyStringRootIndex); 3377 __ jmp(&return_result); 3378 3379 // Save the array length on the stack. 3380 __ bind(&non_trivial_array); 3381 __ SmiToInteger32(array_length, array_length); 3382 __ movl(array_length_operand, array_length); 3383 3384 // Save the FixedArray containing array's elements. 3385 // End of array's live range. 3386 elements = array; 3387 __ movq(elements, FieldOperand(array, JSArray::kElementsOffset)); 3388 array = no_reg; 3389 3390 3391 // Check that all array elements are sequential ASCII strings, and 3392 // accumulate the sum of their lengths, as a smi-encoded value. 3393 __ Set(index, 0); 3394 __ Set(string_length, 0); 3395 // Loop condition: while (index < array_length). 3396 // Live loop registers: index(int32), array_length(int32), string(String*), 3397 // scratch, string_length(int32), elements(FixedArray*). 3398 if (FLAG_debug_code) { 3399 __ cmpq(index, array_length); 3400 __ Assert(below, "No empty arrays here in EmitFastAsciiArrayJoin"); 3401 } 3402 __ bind(&loop); 3403 __ movq(string, FieldOperand(elements, 3404 index, 3405 times_pointer_size, 3406 FixedArray::kHeaderSize)); 3407 __ JumpIfSmi(string, &bailout); 3408 __ movq(scratch, FieldOperand(string, HeapObject::kMapOffset)); 3409 __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); 3410 __ andb(scratch, Immediate( 3411 kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); 3412 __ cmpb(scratch, Immediate(kStringTag | kAsciiStringTag | kSeqStringTag)); 3413 __ j(not_equal, &bailout); 3414 __ AddSmiField(string_length, 3415 FieldOperand(string, SeqAsciiString::kLengthOffset)); 3416 __ j(overflow, &bailout); 3417 __ incl(index); 3418 __ cmpl(index, array_length); 3419 __ j(less, &loop); 3420 3421 // Live registers: 3422 // string_length: Sum of string lengths. 3423 // elements: FixedArray of strings. 3424 // index: Array length. 3425 // array_length: Array length. 3426 3427 // If array_length is 1, return elements[0], a string. 3428 __ cmpl(array_length, Immediate(1)); 3429 __ j(not_equal, ¬_size_one_array); 3430 __ movq(rax, FieldOperand(elements, FixedArray::kHeaderSize)); 3431 __ jmp(&return_result); 3432 3433 __ bind(¬_size_one_array); 3434 3435 // End of array_length live range. 3436 result_pos = array_length; 3437 array_length = no_reg; 3438 3439 // Live registers: 3440 // string_length: Sum of string lengths. 3441 // elements: FixedArray of strings. 3442 // index: Array length. 3443 3444 // Check that the separator is a sequential ASCII string. 3445 __ movq(string, separator_operand); 3446 __ JumpIfSmi(string, &bailout); 3447 __ movq(scratch, FieldOperand(string, HeapObject::kMapOffset)); 3448 __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); 3449 __ andb(scratch, Immediate( 3450 kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); 3451 __ cmpb(scratch, Immediate(kStringTag | kAsciiStringTag | kSeqStringTag)); 3452 __ j(not_equal, &bailout); 3453 3454 // Live registers: 3455 // string_length: Sum of string lengths. 3456 // elements: FixedArray of strings. 3457 // index: Array length. 3458 // string: Separator string. 3459 3460 // Add (separator length times (array_length - 1)) to string_length. 3461 __ SmiToInteger32(scratch, 3462 FieldOperand(string, SeqAsciiString::kLengthOffset)); 3463 __ decl(index); 3464 __ imull(scratch, index); 3465 __ j(overflow, &bailout); 3466 __ addl(string_length, scratch); 3467 __ j(overflow, &bailout); 3468 3469 // Live registers and stack values: 3470 // string_length: Total length of result string. 3471 // elements: FixedArray of strings. 3472 __ AllocateAsciiString(result_pos, string_length, scratch, 3473 index, string, &bailout); 3474 __ movq(result_operand, result_pos); 3475 __ lea(result_pos, FieldOperand(result_pos, SeqAsciiString::kHeaderSize)); 3476 3477 __ movq(string, separator_operand); 3478 __ SmiCompare(FieldOperand(string, SeqAsciiString::kLengthOffset), 3479 Smi::FromInt(1)); 3480 __ j(equal, &one_char_separator); 3481 __ j(greater, &long_separator); 3482 3483 3484 // Empty separator case: 3485 __ Set(index, 0); 3486 __ movl(scratch, array_length_operand); 3487 __ jmp(&loop_1_condition); 3488 // Loop condition: while (index < array_length). 3489 __ bind(&loop_1); 3490 // Each iteration of the loop concatenates one string to the result. 3491 // Live values in registers: 3492 // index: which element of the elements array we are adding to the result. 3493 // result_pos: the position to which we are currently copying characters. 3494 // elements: the FixedArray of strings we are joining. 3495 // scratch: array length. 3496 3497 // Get string = array[index]. 3498 __ movq(string, FieldOperand(elements, index, 3499 times_pointer_size, 3500 FixedArray::kHeaderSize)); 3501 __ SmiToInteger32(string_length, 3502 FieldOperand(string, String::kLengthOffset)); 3503 __ lea(string, 3504 FieldOperand(string, SeqAsciiString::kHeaderSize)); 3505 __ CopyBytes(result_pos, string, string_length); 3506 __ incl(index); 3507 __ bind(&loop_1_condition); 3508 __ cmpl(index, scratch); 3509 __ j(less, &loop_1); // Loop while (index < array_length). 3510 __ jmp(&done); 3511 3512 // Generic bailout code used from several places. 3513 __ bind(&bailout); 3514 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); 3515 __ jmp(&return_result); 3516 3517 3518 // One-character separator case 3519 __ bind(&one_char_separator); 3520 // Get the separator ASCII character value. 3521 // Register "string" holds the separator. 3522 __ movzxbl(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize)); 3523 __ Set(index, 0); 3524 // Jump into the loop after the code that copies the separator, so the first 3525 // element is not preceded by a separator 3526 __ jmp(&loop_2_entry); 3527 // Loop condition: while (index < length). 3528 __ bind(&loop_2); 3529 // Each iteration of the loop concatenates one string to the result. 3530 // Live values in registers: 3531 // elements: The FixedArray of strings we are joining. 3532 // index: which element of the elements array we are adding to the result. 3533 // result_pos: the position to which we are currently copying characters. 3534 // scratch: Separator character. 3535 3536 // Copy the separator character to the result. 3537 __ movb(Operand(result_pos, 0), scratch); 3538 __ incq(result_pos); 3539 3540 __ bind(&loop_2_entry); 3541 // Get string = array[index]. 3542 __ movq(string, FieldOperand(elements, index, 3543 times_pointer_size, 3544 FixedArray::kHeaderSize)); 3545 __ SmiToInteger32(string_length, 3546 FieldOperand(string, String::kLengthOffset)); 3547 __ lea(string, 3548 FieldOperand(string, SeqAsciiString::kHeaderSize)); 3549 __ CopyBytes(result_pos, string, string_length); 3550 __ incl(index); 3551 __ cmpl(index, array_length_operand); 3552 __ j(less, &loop_2); // End while (index < length). 3553 __ jmp(&done); 3554 3555 3556 // Long separator case (separator is more than one character). 3557 __ bind(&long_separator); 3558 3559 // Make elements point to end of elements array, and index 3560 // count from -array_length to zero, so we don't need to maintain 3561 // a loop limit. 3562 __ movl(index, array_length_operand); 3563 __ lea(elements, FieldOperand(elements, index, times_pointer_size, 3564 FixedArray::kHeaderSize)); 3565 __ neg(index); 3566 3567 // Replace separator string with pointer to its first character, and 3568 // make scratch be its length. 3569 __ movq(string, separator_operand); 3570 __ SmiToInteger32(scratch, 3571 FieldOperand(string, String::kLengthOffset)); 3572 __ lea(string, 3573 FieldOperand(string, SeqAsciiString::kHeaderSize)); 3574 __ movq(separator_operand, string); 3575 3576 // Jump into the loop after the code that copies the separator, so the first 3577 // element is not preceded by a separator 3578 __ jmp(&loop_3_entry); 3579 // Loop condition: while (index < length). 3580 __ bind(&loop_3); 3581 // Each iteration of the loop concatenates one string to the result. 3582 // Live values in registers: 3583 // index: which element of the elements array we are adding to the result. 3584 // result_pos: the position to which we are currently copying characters. 3585 // scratch: Separator length. 3586 // separator_operand (rsp[0x10]): Address of first char of separator. 3587 3588 // Copy the separator to the result. 3589 __ movq(string, separator_operand); 3590 __ movl(string_length, scratch); 3591 __ CopyBytes(result_pos, string, string_length, 2); 3592 3593 __ bind(&loop_3_entry); 3594 // Get string = array[index]. 3595 __ movq(string, Operand(elements, index, times_pointer_size, 0)); 3596 __ SmiToInteger32(string_length, 3597 FieldOperand(string, String::kLengthOffset)); 3598 __ lea(string, 3599 FieldOperand(string, SeqAsciiString::kHeaderSize)); 3600 __ CopyBytes(result_pos, string, string_length); 3601 __ incq(index); 3602 __ j(not_equal, &loop_3); // Loop while (index < 0). 3603 3604 __ bind(&done); 3605 __ movq(rax, result_operand); 3606 3607 __ bind(&return_result); 3608 // Drop temp values from the stack, and restore context register. 3609 __ addq(rsp, Immediate(3 * kPointerSize)); 3610 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 3611 context()->Plug(rax); 3612} 3613 3614 3615void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { 3616 Handle<String> name = expr->name(); 3617 if (name->length() > 0 && name->Get(0) == '_') { 3618 Comment cmnt(masm_, "[ InlineRuntimeCall"); 3619 EmitInlineRuntimeCall(expr); 3620 return; 3621 } 3622 3623 Comment cmnt(masm_, "[ CallRuntime"); 3624 ZoneList<Expression*>* args = expr->arguments(); 3625 3626 if (expr->is_jsruntime()) { 3627 // Prepare for calling JS runtime function. 3628 __ movq(rax, GlobalObjectOperand()); 3629 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); 3630 } 3631 3632 // Push the arguments ("left-to-right"). 3633 int arg_count = args->length(); 3634 for (int i = 0; i < arg_count; i++) { 3635 VisitForStackValue(args->at(i)); 3636 } 3637 3638 if (expr->is_jsruntime()) { 3639 // Call the JS runtime function using a call IC. 3640 __ Move(rcx, expr->name()); 3641 RelocInfo::Mode mode = RelocInfo::CODE_TARGET; 3642 Handle<Code> ic = 3643 isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode); 3644 __ call(ic, mode, expr->id()); 3645 // Restore context register. 3646 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 3647 } else { 3648 __ CallRuntime(expr->function(), arg_count); 3649 } 3650 context()->Plug(rax); 3651} 3652 3653 3654void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 3655 switch (expr->op()) { 3656 case Token::DELETE: { 3657 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); 3658 Property* property = expr->expression()->AsProperty(); 3659 VariableProxy* proxy = expr->expression()->AsVariableProxy(); 3660 3661 if (property != NULL) { 3662 VisitForStackValue(property->obj()); 3663 VisitForStackValue(property->key()); 3664 StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) 3665 ? kNonStrictMode : kStrictMode; 3666 __ Push(Smi::FromInt(strict_mode_flag)); 3667 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); 3668 context()->Plug(rax); 3669 } else if (proxy != NULL) { 3670 Variable* var = proxy->var(); 3671 // Delete of an unqualified identifier is disallowed in strict mode 3672 // but "delete this" is allowed. 3673 ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); 3674 if (var->IsUnallocated()) { 3675 __ push(GlobalObjectOperand()); 3676 __ Push(var->name()); 3677 __ Push(Smi::FromInt(kNonStrictMode)); 3678 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); 3679 context()->Plug(rax); 3680 } else if (var->IsStackAllocated() || var->IsContextSlot()) { 3681 // Result of deleting non-global variables is false. 'this' is 3682 // not really a variable, though we implement it as one. The 3683 // subexpression does not have side effects. 3684 context()->Plug(var->is_this()); 3685 } else { 3686 // Non-global variable. Call the runtime to try to delete from the 3687 // context where the variable was introduced. 3688 __ push(context_register()); 3689 __ Push(var->name()); 3690 __ CallRuntime(Runtime::kDeleteContextSlot, 2); 3691 context()->Plug(rax); 3692 } 3693 } else { 3694 // Result of deleting non-property, non-variable reference is true. 3695 // The subexpression may have side effects. 3696 VisitForEffect(expr->expression()); 3697 context()->Plug(true); 3698 } 3699 break; 3700 } 3701 3702 case Token::VOID: { 3703 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); 3704 VisitForEffect(expr->expression()); 3705 context()->Plug(Heap::kUndefinedValueRootIndex); 3706 break; 3707 } 3708 3709 case Token::NOT: { 3710 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); 3711 if (context()->IsEffect()) { 3712 // Unary NOT has no side effects so it's only necessary to visit the 3713 // subexpression. Match the optimizing compiler by not branching. 3714 VisitForEffect(expr->expression()); 3715 } else if (context()->IsTest()) { 3716 const TestContext* test = TestContext::cast(context()); 3717 // The labels are swapped for the recursive call. 3718 VisitForControl(expr->expression(), 3719 test->false_label(), 3720 test->true_label(), 3721 test->fall_through()); 3722 context()->Plug(test->true_label(), test->false_label()); 3723 } else { 3724 // We handle value contexts explicitly rather than simply visiting 3725 // for control and plugging the control flow into the context, 3726 // because we need to prepare a pair of extra administrative AST ids 3727 // for the optimizing compiler. 3728 ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); 3729 Label materialize_true, materialize_false, done; 3730 VisitForControl(expr->expression(), 3731 &materialize_false, 3732 &materialize_true, 3733 &materialize_true); 3734 __ bind(&materialize_true); 3735 PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); 3736 if (context()->IsAccumulatorValue()) { 3737 __ LoadRoot(rax, Heap::kTrueValueRootIndex); 3738 } else { 3739 __ PushRoot(Heap::kTrueValueRootIndex); 3740 } 3741 __ jmp(&done, Label::kNear); 3742 __ bind(&materialize_false); 3743 PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); 3744 if (context()->IsAccumulatorValue()) { 3745 __ LoadRoot(rax, Heap::kFalseValueRootIndex); 3746 } else { 3747 __ PushRoot(Heap::kFalseValueRootIndex); 3748 } 3749 __ bind(&done); 3750 } 3751 break; 3752 } 3753 3754 case Token::TYPEOF: { 3755 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); 3756 { StackValueContext context(this); 3757 VisitForTypeofValue(expr->expression()); 3758 } 3759 __ CallRuntime(Runtime::kTypeof, 1); 3760 context()->Plug(rax); 3761 break; 3762 } 3763 3764 case Token::ADD: { 3765 Comment cmt(masm_, "[ UnaryOperation (ADD)"); 3766 VisitForAccumulatorValue(expr->expression()); 3767 Label no_conversion; 3768 __ JumpIfSmi(result_register(), &no_conversion); 3769 ToNumberStub convert_stub; 3770 __ CallStub(&convert_stub); 3771 __ bind(&no_conversion); 3772 context()->Plug(result_register()); 3773 break; 3774 } 3775 3776 case Token::SUB: 3777 EmitUnaryOperation(expr, "[ UnaryOperation (SUB)"); 3778 break; 3779 3780 case Token::BIT_NOT: 3781 EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)"); 3782 break; 3783 3784 default: 3785 UNREACHABLE(); 3786 } 3787} 3788 3789 3790void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr, 3791 const char* comment) { 3792 // TODO(svenpanne): Allowing format strings in Comment would be nice here... 3793 Comment cmt(masm_, comment); 3794 bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); 3795 UnaryOverwriteMode overwrite = 3796 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; 3797 UnaryOpStub stub(expr->op(), overwrite); 3798 // UnaryOpStub expects the argument to be in the 3799 // accumulator register rax. 3800 VisitForAccumulatorValue(expr->expression()); 3801 SetSourcePosition(expr->position()); 3802 __ call(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); 3803 context()->Plug(rax); 3804} 3805 3806 3807void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { 3808 Comment cmnt(masm_, "[ CountOperation"); 3809 SetSourcePosition(expr->position()); 3810 3811 // Invalid left-hand-sides are rewritten to have a 'throw 3812 // ReferenceError' as the left-hand side. 3813 if (!expr->expression()->IsValidLeftHandSide()) { 3814 VisitForEffect(expr->expression()); 3815 return; 3816 } 3817 3818 // Expression can only be a property, a global or a (parameter or local) 3819 // slot. 3820 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; 3821 LhsKind assign_type = VARIABLE; 3822 Property* prop = expr->expression()->AsProperty(); 3823 // In case of a property we use the uninitialized expression context 3824 // of the key to detect a named property. 3825 if (prop != NULL) { 3826 assign_type = 3827 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; 3828 } 3829 3830 // Evaluate expression and get value. 3831 if (assign_type == VARIABLE) { 3832 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); 3833 AccumulatorValueContext context(this); 3834 EmitVariableLoad(expr->expression()->AsVariableProxy()); 3835 } else { 3836 // Reserve space for result of postfix operation. 3837 if (expr->is_postfix() && !context()->IsEffect()) { 3838 __ Push(Smi::FromInt(0)); 3839 } 3840 if (assign_type == NAMED_PROPERTY) { 3841 VisitForAccumulatorValue(prop->obj()); 3842 __ push(rax); // Copy of receiver, needed for later store. 3843 EmitNamedPropertyLoad(prop); 3844 } else { 3845 VisitForStackValue(prop->obj()); 3846 VisitForAccumulatorValue(prop->key()); 3847 __ movq(rdx, Operand(rsp, 0)); // Leave receiver on stack 3848 __ push(rax); // Copy of key, needed for later store. 3849 EmitKeyedPropertyLoad(prop); 3850 } 3851 } 3852 3853 // We need a second deoptimization point after loading the value 3854 // in case evaluating the property load my have a side effect. 3855 if (assign_type == VARIABLE) { 3856 PrepareForBailout(expr->expression(), TOS_REG); 3857 } else { 3858 PrepareForBailoutForId(expr->CountId(), TOS_REG); 3859 } 3860 3861 // Call ToNumber only if operand is not a smi. 3862 Label no_conversion; 3863 __ JumpIfSmi(rax, &no_conversion, Label::kNear); 3864 ToNumberStub convert_stub; 3865 __ CallStub(&convert_stub); 3866 __ bind(&no_conversion); 3867 3868 // Save result for postfix expressions. 3869 if (expr->is_postfix()) { 3870 if (!context()->IsEffect()) { 3871 // Save the result on the stack. If we have a named or keyed property 3872 // we store the result under the receiver that is currently on top 3873 // of the stack. 3874 switch (assign_type) { 3875 case VARIABLE: 3876 __ push(rax); 3877 break; 3878 case NAMED_PROPERTY: 3879 __ movq(Operand(rsp, kPointerSize), rax); 3880 break; 3881 case KEYED_PROPERTY: 3882 __ movq(Operand(rsp, 2 * kPointerSize), rax); 3883 break; 3884 } 3885 } 3886 } 3887 3888 // Inline smi case if we are in a loop. 3889 Label done, stub_call; 3890 JumpPatchSite patch_site(masm_); 3891 3892 if (ShouldInlineSmiCase(expr->op())) { 3893 if (expr->op() == Token::INC) { 3894 __ SmiAddConstant(rax, rax, Smi::FromInt(1)); 3895 } else { 3896 __ SmiSubConstant(rax, rax, Smi::FromInt(1)); 3897 } 3898 __ j(overflow, &stub_call, Label::kNear); 3899 // We could eliminate this smi check if we split the code at 3900 // the first smi check before calling ToNumber. 3901 patch_site.EmitJumpIfSmi(rax, &done, Label::kNear); 3902 3903 __ bind(&stub_call); 3904 // Call stub. Undo operation first. 3905 if (expr->op() == Token::INC) { 3906 __ SmiSubConstant(rax, rax, Smi::FromInt(1)); 3907 } else { 3908 __ SmiAddConstant(rax, rax, Smi::FromInt(1)); 3909 } 3910 } 3911 3912 // Record position before stub call. 3913 SetSourcePosition(expr->position()); 3914 3915 // Call stub for +1/-1. 3916 BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE); 3917 if (expr->op() == Token::INC) { 3918 __ Move(rdx, Smi::FromInt(1)); 3919 } else { 3920 __ movq(rdx, rax); 3921 __ Move(rax, Smi::FromInt(1)); 3922 } 3923 __ call(stub.GetCode(), RelocInfo::CODE_TARGET, expr->CountId()); 3924 patch_site.EmitPatchInfo(); 3925 __ bind(&done); 3926 3927 // Store the value returned in rax. 3928 switch (assign_type) { 3929 case VARIABLE: 3930 if (expr->is_postfix()) { 3931 // Perform the assignment as if via '='. 3932 { EffectContext context(this); 3933 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), 3934 Token::ASSIGN); 3935 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 3936 context.Plug(rax); 3937 } 3938 // For all contexts except kEffect: We have the result on 3939 // top of the stack. 3940 if (!context()->IsEffect()) { 3941 context()->PlugTOS(); 3942 } 3943 } else { 3944 // Perform the assignment as if via '='. 3945 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), 3946 Token::ASSIGN); 3947 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 3948 context()->Plug(rax); 3949 } 3950 break; 3951 case NAMED_PROPERTY: { 3952 __ Move(rcx, prop->key()->AsLiteral()->handle()); 3953 __ pop(rdx); 3954 Handle<Code> ic = is_classic_mode() 3955 ? isolate()->builtins()->StoreIC_Initialize() 3956 : isolate()->builtins()->StoreIC_Initialize_Strict(); 3957 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 3958 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 3959 if (expr->is_postfix()) { 3960 if (!context()->IsEffect()) { 3961 context()->PlugTOS(); 3962 } 3963 } else { 3964 context()->Plug(rax); 3965 } 3966 break; 3967 } 3968 case KEYED_PROPERTY: { 3969 __ pop(rcx); 3970 __ pop(rdx); 3971 Handle<Code> ic = is_classic_mode() 3972 ? isolate()->builtins()->KeyedStoreIC_Initialize() 3973 : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); 3974 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 3975 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); 3976 if (expr->is_postfix()) { 3977 if (!context()->IsEffect()) { 3978 context()->PlugTOS(); 3979 } 3980 } else { 3981 context()->Plug(rax); 3982 } 3983 break; 3984 } 3985 } 3986} 3987 3988 3989void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { 3990 VariableProxy* proxy = expr->AsVariableProxy(); 3991 ASSERT(!context()->IsEffect()); 3992 ASSERT(!context()->IsTest()); 3993 3994 if (proxy != NULL && proxy->var()->IsUnallocated()) { 3995 Comment cmnt(masm_, "Global variable"); 3996 __ Move(rcx, proxy->name()); 3997 __ movq(rax, GlobalObjectOperand()); 3998 Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); 3999 // Use a regular load, not a contextual load, to avoid a reference 4000 // error. 4001 __ call(ic); 4002 PrepareForBailout(expr, TOS_REG); 4003 context()->Plug(rax); 4004 } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { 4005 Label done, slow; 4006 4007 // Generate code for loading from variables potentially shadowed 4008 // by eval-introduced variables. 4009 EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); 4010 4011 __ bind(&slow); 4012 __ push(rsi); 4013 __ Push(proxy->name()); 4014 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); 4015 PrepareForBailout(expr, TOS_REG); 4016 __ bind(&done); 4017 4018 context()->Plug(rax); 4019 } else { 4020 // This expression cannot throw a reference error at the top level. 4021 VisitInDuplicateContext(expr); 4022 } 4023} 4024 4025 4026void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, 4027 Expression* sub_expr, 4028 Handle<String> check) { 4029 Label materialize_true, materialize_false; 4030 Label* if_true = NULL; 4031 Label* if_false = NULL; 4032 Label* fall_through = NULL; 4033 context()->PrepareTest(&materialize_true, &materialize_false, 4034 &if_true, &if_false, &fall_through); 4035 4036 { AccumulatorValueContext context(this); 4037 VisitForTypeofValue(sub_expr); 4038 } 4039 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 4040 4041 if (check->Equals(isolate()->heap()->number_symbol())) { 4042 __ JumpIfSmi(rax, if_true); 4043 __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); 4044 __ CompareRoot(rax, Heap::kHeapNumberMapRootIndex); 4045 Split(equal, if_true, if_false, fall_through); 4046 } else if (check->Equals(isolate()->heap()->string_symbol())) { 4047 __ JumpIfSmi(rax, if_false); 4048 // Check for undetectable objects => false. 4049 __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdx); 4050 __ j(above_equal, if_false); 4051 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 4052 Immediate(1 << Map::kIsUndetectable)); 4053 Split(zero, if_true, if_false, fall_through); 4054 } else if (check->Equals(isolate()->heap()->boolean_symbol())) { 4055 __ CompareRoot(rax, Heap::kTrueValueRootIndex); 4056 __ j(equal, if_true); 4057 __ CompareRoot(rax, Heap::kFalseValueRootIndex); 4058 Split(equal, if_true, if_false, fall_through); 4059 } else if (FLAG_harmony_typeof && 4060 check->Equals(isolate()->heap()->null_symbol())) { 4061 __ CompareRoot(rax, Heap::kNullValueRootIndex); 4062 Split(equal, if_true, if_false, fall_through); 4063 } else if (check->Equals(isolate()->heap()->undefined_symbol())) { 4064 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); 4065 __ j(equal, if_true); 4066 __ JumpIfSmi(rax, if_false); 4067 // Check for undetectable objects => true. 4068 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); 4069 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 4070 Immediate(1 << Map::kIsUndetectable)); 4071 Split(not_zero, if_true, if_false, fall_through); 4072 } else if (check->Equals(isolate()->heap()->function_symbol())) { 4073 __ JumpIfSmi(rax, if_false); 4074 STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); 4075 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rdx); 4076 __ j(equal, if_true); 4077 __ CmpInstanceType(rdx, JS_FUNCTION_PROXY_TYPE); 4078 Split(equal, if_true, if_false, fall_through); 4079 } else if (check->Equals(isolate()->heap()->object_symbol())) { 4080 __ JumpIfSmi(rax, if_false); 4081 if (!FLAG_harmony_typeof) { 4082 __ CompareRoot(rax, Heap::kNullValueRootIndex); 4083 __ j(equal, if_true); 4084 } 4085 __ CmpObjectType(rax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, rdx); 4086 __ j(below, if_false); 4087 __ CmpInstanceType(rdx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); 4088 __ j(above, if_false); 4089 // Check for undetectable objects => false. 4090 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 4091 Immediate(1 << Map::kIsUndetectable)); 4092 Split(zero, if_true, if_false, fall_through); 4093 } else { 4094 if (if_false != fall_through) __ jmp(if_false); 4095 } 4096 context()->Plug(if_true, if_false); 4097} 4098 4099 4100void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { 4101 Comment cmnt(masm_, "[ CompareOperation"); 4102 SetSourcePosition(expr->position()); 4103 4104 // First we try a fast inlined version of the compare when one of 4105 // the operands is a literal. 4106 if (TryLiteralCompare(expr)) return; 4107 4108 // Always perform the comparison for its control flow. Pack the result 4109 // into the expression's context after the comparison is performed. 4110 Label materialize_true, materialize_false; 4111 Label* if_true = NULL; 4112 Label* if_false = NULL; 4113 Label* fall_through = NULL; 4114 context()->PrepareTest(&materialize_true, &materialize_false, 4115 &if_true, &if_false, &fall_through); 4116 4117 Token::Value op = expr->op(); 4118 VisitForStackValue(expr->left()); 4119 switch (op) { 4120 case Token::IN: 4121 VisitForStackValue(expr->right()); 4122 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); 4123 PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); 4124 __ CompareRoot(rax, Heap::kTrueValueRootIndex); 4125 Split(equal, if_true, if_false, fall_through); 4126 break; 4127 4128 case Token::INSTANCEOF: { 4129 VisitForStackValue(expr->right()); 4130 InstanceofStub stub(InstanceofStub::kNoFlags); 4131 __ CallStub(&stub); 4132 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 4133 __ testq(rax, rax); 4134 // The stub returns 0 for true. 4135 Split(zero, if_true, if_false, fall_through); 4136 break; 4137 } 4138 4139 default: { 4140 VisitForAccumulatorValue(expr->right()); 4141 Condition cc = no_condition; 4142 switch (op) { 4143 case Token::EQ_STRICT: 4144 case Token::EQ: 4145 cc = equal; 4146 break; 4147 case Token::LT: 4148 cc = less; 4149 break; 4150 case Token::GT: 4151 cc = greater; 4152 break; 4153 case Token::LTE: 4154 cc = less_equal; 4155 break; 4156 case Token::GTE: 4157 cc = greater_equal; 4158 break; 4159 case Token::IN: 4160 case Token::INSTANCEOF: 4161 default: 4162 UNREACHABLE(); 4163 } 4164 __ pop(rdx); 4165 4166 bool inline_smi_code = ShouldInlineSmiCase(op); 4167 JumpPatchSite patch_site(masm_); 4168 if (inline_smi_code) { 4169 Label slow_case; 4170 __ movq(rcx, rdx); 4171 __ or_(rcx, rax); 4172 patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear); 4173 __ cmpq(rdx, rax); 4174 Split(cc, if_true, if_false, NULL); 4175 __ bind(&slow_case); 4176 } 4177 4178 // Record position and call the compare IC. 4179 SetSourcePosition(expr->position()); 4180 Handle<Code> ic = CompareIC::GetUninitialized(op); 4181 __ call(ic, RelocInfo::CODE_TARGET, expr->id()); 4182 patch_site.EmitPatchInfo(); 4183 4184 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 4185 __ testq(rax, rax); 4186 Split(cc, if_true, if_false, fall_through); 4187 } 4188 } 4189 4190 // Convert the result of the comparison into one expected for this 4191 // expression's context. 4192 context()->Plug(if_true, if_false); 4193} 4194 4195 4196void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, 4197 Expression* sub_expr, 4198 NilValue nil) { 4199 Label materialize_true, materialize_false; 4200 Label* if_true = NULL; 4201 Label* if_false = NULL; 4202 Label* fall_through = NULL; 4203 context()->PrepareTest(&materialize_true, &materialize_false, 4204 &if_true, &if_false, &fall_through); 4205 4206 VisitForAccumulatorValue(sub_expr); 4207 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 4208 Heap::RootListIndex nil_value = nil == kNullValue ? 4209 Heap::kNullValueRootIndex : 4210 Heap::kUndefinedValueRootIndex; 4211 __ CompareRoot(rax, nil_value); 4212 if (expr->op() == Token::EQ_STRICT) { 4213 Split(equal, if_true, if_false, fall_through); 4214 } else { 4215 Heap::RootListIndex other_nil_value = nil == kNullValue ? 4216 Heap::kUndefinedValueRootIndex : 4217 Heap::kNullValueRootIndex; 4218 __ j(equal, if_true); 4219 __ CompareRoot(rax, other_nil_value); 4220 __ j(equal, if_true); 4221 __ JumpIfSmi(rax, if_false); 4222 // It can be an undetectable object. 4223 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); 4224 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 4225 Immediate(1 << Map::kIsUndetectable)); 4226 Split(not_zero, if_true, if_false, fall_through); 4227 } 4228 context()->Plug(if_true, if_false); 4229} 4230 4231 4232void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) { 4233 __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 4234 context()->Plug(rax); 4235} 4236 4237 4238Register FullCodeGenerator::result_register() { 4239 return rax; 4240} 4241 4242 4243Register FullCodeGenerator::context_register() { 4244 return rsi; 4245} 4246 4247 4248void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { 4249 ASSERT(IsAligned(frame_offset, kPointerSize)); 4250 __ movq(Operand(rbp, frame_offset), value); 4251} 4252 4253 4254void FullCodeGenerator::LoadContextField(Register dst, int context_index) { 4255 __ movq(dst, ContextOperand(rsi, context_index)); 4256} 4257 4258 4259void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { 4260 Scope* declaration_scope = scope()->DeclarationScope(); 4261 if (declaration_scope->is_global_scope()) { 4262 // Contexts nested in the global context have a canonical empty function 4263 // as their closure, not the anonymous closure containing the global 4264 // code. Pass a smi sentinel and let the runtime look up the empty 4265 // function. 4266 __ Push(Smi::FromInt(0)); 4267 } else if (declaration_scope->is_eval_scope()) { 4268 // Contexts created by a call to eval have the same closure as the 4269 // context calling eval, not the anonymous closure containing the eval 4270 // code. Fetch it from the context. 4271 __ push(ContextOperand(rsi, Context::CLOSURE_INDEX)); 4272 } else { 4273 ASSERT(declaration_scope->is_function_scope()); 4274 __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 4275 } 4276} 4277 4278 4279// ---------------------------------------------------------------------------- 4280// Non-local control flow support. 4281 4282 4283void FullCodeGenerator::EnterFinallyBlock() { 4284 ASSERT(!result_register().is(rdx)); 4285 ASSERT(!result_register().is(rcx)); 4286 // Cook return address on top of stack (smi encoded Code* delta) 4287 __ pop(rdx); 4288 __ Move(rcx, masm_->CodeObject()); 4289 __ subq(rdx, rcx); 4290 __ Integer32ToSmi(rdx, rdx); 4291 __ push(rdx); 4292 // Store result register while executing finally block. 4293 __ push(result_register()); 4294} 4295 4296 4297void FullCodeGenerator::ExitFinallyBlock() { 4298 ASSERT(!result_register().is(rdx)); 4299 ASSERT(!result_register().is(rcx)); 4300 __ pop(result_register()); 4301 // Uncook return address. 4302 __ pop(rdx); 4303 __ SmiToInteger32(rdx, rdx); 4304 __ Move(rcx, masm_->CodeObject()); 4305 __ addq(rdx, rcx); 4306 __ jmp(rdx); 4307} 4308 4309 4310#undef __ 4311 4312#define __ ACCESS_MASM(masm()) 4313 4314FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit( 4315 int* stack_depth, 4316 int* context_length) { 4317 // The macros used here must preserve the result register. 4318 4319 // Because the handler block contains the context of the finally 4320 // code, we can restore it directly from there for the finally code 4321 // rather than iteratively unwinding contexts via their previous 4322 // links. 4323 __ Drop(*stack_depth); // Down to the handler block. 4324 if (*context_length > 0) { 4325 // Restore the context to its dedicated register and the stack. 4326 __ movq(rsi, Operand(rsp, StackHandlerConstants::kContextOffset)); 4327 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); 4328 } 4329 __ PopTryHandler(); 4330 __ call(finally_entry_); 4331 4332 *stack_depth = 0; 4333 *context_length = 0; 4334 return previous_; 4335} 4336 4337 4338#undef __ 4339 4340} } // namespace v8::internal 4341 4342#endif // V8_TARGET_ARCH_X64 4343