1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #if V8_TARGET_ARCH_ARM64 6 7 #include "src/ast/scopes.h" 8 #include "src/code-factory.h" 9 #include "src/code-stubs.h" 10 #include "src/codegen.h" 11 #include "src/debug/debug.h" 12 #include "src/full-codegen/full-codegen.h" 13 #include "src/ic/ic.h" 14 #include "src/parsing/parser.h" 15 16 #include "src/arm64/code-stubs-arm64.h" 17 #include "src/arm64/frames-arm64.h" 18 #include "src/arm64/macro-assembler-arm64.h" 19 20 namespace v8 { 21 namespace internal { 22 23 #define __ ACCESS_MASM(masm()) 24 25 class JumpPatchSite BASE_EMBEDDED { 26 public: 27 explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm), reg_(NoReg) { 28 #ifdef DEBUG 29 info_emitted_ = false; 30 #endif 31 } 32 33 ~JumpPatchSite() { 34 if (patch_site_.is_bound()) { 35 DCHECK(info_emitted_); 36 } else { 37 DCHECK(reg_.IsNone()); 38 } 39 } 40 41 void EmitJumpIfNotSmi(Register reg, Label* target) { 42 // This code will be patched by PatchInlinedSmiCode, in ic-arm64.cc. 43 InstructionAccurateScope scope(masm_, 1); 44 DCHECK(!info_emitted_); 45 DCHECK(reg.Is64Bits()); 46 DCHECK(!reg.Is(csp)); 47 reg_ = reg; 48 __ bind(&patch_site_); 49 __ tbz(xzr, 0, target); // Always taken before patched. 50 } 51 52 void EmitJumpIfSmi(Register reg, Label* target) { 53 // This code will be patched by PatchInlinedSmiCode, in ic-arm64.cc. 54 InstructionAccurateScope scope(masm_, 1); 55 DCHECK(!info_emitted_); 56 DCHECK(reg.Is64Bits()); 57 DCHECK(!reg.Is(csp)); 58 reg_ = reg; 59 __ bind(&patch_site_); 60 __ tbnz(xzr, 0, target); // Never taken before patched. 61 } 62 63 void EmitJumpIfEitherNotSmi(Register reg1, Register reg2, Label* target) { 64 UseScratchRegisterScope temps(masm_); 65 Register temp = temps.AcquireX(); 66 __ Orr(temp, reg1, reg2); 67 EmitJumpIfNotSmi(temp, target); 68 } 69 70 void EmitPatchInfo() { 71 Assembler::BlockPoolsScope scope(masm_); 72 InlineSmiCheckInfo::Emit(masm_, reg_, &patch_site_); 73 #ifdef DEBUG 74 info_emitted_ = true; 75 #endif 76 } 77 78 private: 79 MacroAssembler* masm() { return masm_; } 80 MacroAssembler* masm_; 81 Label patch_site_; 82 Register reg_; 83 #ifdef DEBUG 84 bool info_emitted_; 85 #endif 86 }; 87 88 89 // Generate code for a JS function. On entry to the function the receiver 90 // and arguments have been pushed on the stack left to right. The actual 91 // argument count matches the formal parameter count expected by the 92 // function. 93 // 94 // The live registers are: 95 // - x1: the JS function object being called (i.e. ourselves). 96 // - x3: the new target value 97 // - cp: our context. 98 // - fp: our caller's frame pointer. 99 // - jssp: stack pointer. 100 // - lr: return address. 101 // 102 // The function builds a JS frame. See JavaScriptFrameConstants in 103 // frames-arm.h for its layout. 104 void FullCodeGenerator::Generate() { 105 CompilationInfo* info = info_; 106 profiling_counter_ = isolate()->factory()->NewCell( 107 Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate())); 108 SetFunctionPosition(literal()); 109 Comment cmnt(masm_, "[ Function compiled by full code generator"); 110 111 ProfileEntryHookStub::MaybeCallEntryHook(masm_); 112 113 if (FLAG_debug_code && info->ExpectsJSReceiverAsReceiver()) { 114 int receiver_offset = info->scope()->num_parameters() * kXRegSize; 115 __ Peek(x10, receiver_offset); 116 __ AssertNotSmi(x10); 117 __ CompareObjectType(x10, x10, x11, FIRST_JS_RECEIVER_TYPE); 118 __ Assert(ge, kSloppyFunctionExpectsJSReceiverReceiver); 119 } 120 121 // Open a frame scope to indicate that there is a frame on the stack. 122 // The MANUAL indicates that the scope shouldn't actually generate code 123 // to set up the frame because we do it manually below. 124 FrameScope frame_scope(masm_, StackFrame::MANUAL); 125 126 // This call emits the following sequence in a way that can be patched for 127 // code ageing support: 128 // Push(lr, fp, cp, x1); 129 // Add(fp, jssp, 2 * kPointerSize); 130 info->set_prologue_offset(masm_->pc_offset()); 131 __ Prologue(info->GeneratePreagedPrologue()); 132 133 // Reserve space on the stack for locals. 134 { Comment cmnt(masm_, "[ Allocate locals"); 135 int locals_count = info->scope()->num_stack_slots(); 136 // Generators allocate locals, if any, in context slots. 137 DCHECK(!IsGeneratorFunction(info->literal()->kind()) || locals_count == 0); 138 OperandStackDepthIncrement(locals_count); 139 if (locals_count > 0) { 140 if (locals_count >= 128) { 141 Label ok; 142 DCHECK(jssp.Is(__ StackPointer())); 143 __ Sub(x10, jssp, locals_count * kPointerSize); 144 __ CompareRoot(x10, Heap::kRealStackLimitRootIndex); 145 __ B(hs, &ok); 146 __ CallRuntime(Runtime::kThrowStackOverflow); 147 __ Bind(&ok); 148 } 149 __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); 150 if (FLAG_optimize_for_size) { 151 __ PushMultipleTimes(x10 , locals_count); 152 } else { 153 const int kMaxPushes = 32; 154 if (locals_count >= kMaxPushes) { 155 int loop_iterations = locals_count / kMaxPushes; 156 __ Mov(x2, loop_iterations); 157 Label loop_header; 158 __ Bind(&loop_header); 159 // Do pushes. 160 __ PushMultipleTimes(x10 , kMaxPushes); 161 __ Subs(x2, x2, 1); 162 __ B(ne, &loop_header); 163 } 164 int remaining = locals_count % kMaxPushes; 165 // Emit the remaining pushes. 166 __ PushMultipleTimes(x10 , remaining); 167 } 168 } 169 } 170 171 bool function_in_register_x1 = true; 172 173 if (info->scope()->num_heap_slots() > 0) { 174 // Argument to NewContext is the function, which is still in x1. 175 Comment cmnt(masm_, "[ Allocate context"); 176 bool need_write_barrier = true; 177 int slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; 178 if (info->scope()->is_script_scope()) { 179 __ Mov(x10, Operand(info->scope()->GetScopeInfo(info->isolate()))); 180 __ Push(x1, x10); 181 __ CallRuntime(Runtime::kNewScriptContext); 182 PrepareForBailoutForId(BailoutId::ScriptContext(), 183 BailoutState::TOS_REGISTER); 184 // The new target value is not used, clobbering is safe. 185 DCHECK_NULL(info->scope()->new_target_var()); 186 } else { 187 if (info->scope()->new_target_var() != nullptr) { 188 __ Push(x3); // Preserve new target. 189 } 190 if (slots <= FastNewContextStub::kMaximumSlots) { 191 FastNewContextStub stub(isolate(), slots); 192 __ CallStub(&stub); 193 // Result of FastNewContextStub is always in new space. 194 need_write_barrier = false; 195 } else { 196 __ Push(x1); 197 __ CallRuntime(Runtime::kNewFunctionContext); 198 } 199 if (info->scope()->new_target_var() != nullptr) { 200 __ Pop(x3); // Restore new target. 201 } 202 } 203 function_in_register_x1 = false; 204 // Context is returned in x0. It replaces the context passed to us. 205 // It's saved in the stack and kept live in cp. 206 __ Mov(cp, x0); 207 __ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset)); 208 // Copy any necessary parameters into the context. 209 int num_parameters = info->scope()->num_parameters(); 210 int first_parameter = info->scope()->has_this_declaration() ? -1 : 0; 211 for (int i = first_parameter; i < num_parameters; i++) { 212 Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i); 213 if (var->IsContextSlot()) { 214 int parameter_offset = StandardFrameConstants::kCallerSPOffset + 215 (num_parameters - 1 - i) * kPointerSize; 216 // Load parameter from stack. 217 __ Ldr(x10, MemOperand(fp, parameter_offset)); 218 // Store it in the context. 219 MemOperand target = ContextMemOperand(cp, var->index()); 220 __ Str(x10, target); 221 222 // Update the write barrier. 223 if (need_write_barrier) { 224 __ RecordWriteContextSlot(cp, static_cast<int>(target.offset()), x10, 225 x11, kLRHasBeenSaved, kDontSaveFPRegs); 226 } else if (FLAG_debug_code) { 227 Label done; 228 __ JumpIfInNewSpace(cp, &done); 229 __ Abort(kExpectedNewSpaceObject); 230 __ bind(&done); 231 } 232 } 233 } 234 } 235 236 // Register holding this function and new target are both trashed in case we 237 // bailout here. But since that can happen only when new target is not used 238 // and we allocate a context, the value of |function_in_register| is correct. 239 PrepareForBailoutForId(BailoutId::FunctionContext(), 240 BailoutState::NO_REGISTERS); 241 242 // Possibly set up a local binding to the this function which is used in 243 // derived constructors with super calls. 244 Variable* this_function_var = scope()->this_function_var(); 245 if (this_function_var != nullptr) { 246 Comment cmnt(masm_, "[ This function"); 247 if (!function_in_register_x1) { 248 __ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 249 // The write barrier clobbers register again, keep it marked as such. 250 } 251 SetVar(this_function_var, x1, x0, x2); 252 } 253 254 // Possibly set up a local binding to the new target value. 255 Variable* new_target_var = scope()->new_target_var(); 256 if (new_target_var != nullptr) { 257 Comment cmnt(masm_, "[ new.target"); 258 SetVar(new_target_var, x3, x0, x2); 259 } 260 261 // Possibly allocate RestParameters 262 int rest_index; 263 Variable* rest_param = scope()->rest_parameter(&rest_index); 264 if (rest_param) { 265 Comment cmnt(masm_, "[ Allocate rest parameter array"); 266 if (!function_in_register_x1) { 267 __ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 268 } 269 FastNewRestParameterStub stub(isolate()); 270 __ CallStub(&stub); 271 function_in_register_x1 = false; 272 SetVar(rest_param, x0, x1, x2); 273 } 274 275 Variable* arguments = scope()->arguments(); 276 if (arguments != NULL) { 277 // Function uses arguments object. 278 Comment cmnt(masm_, "[ Allocate arguments object"); 279 if (!function_in_register_x1) { 280 // Load this again, if it's used by the local context below. 281 __ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 282 } 283 if (is_strict(language_mode()) || !has_simple_parameters()) { 284 FastNewStrictArgumentsStub stub(isolate()); 285 __ CallStub(&stub); 286 } else if (literal()->has_duplicate_parameters()) { 287 __ Push(x1); 288 __ CallRuntime(Runtime::kNewSloppyArguments_Generic); 289 } else { 290 FastNewSloppyArgumentsStub stub(isolate()); 291 __ CallStub(&stub); 292 } 293 294 SetVar(arguments, x0, x1, x2); 295 } 296 297 if (FLAG_trace) { 298 __ CallRuntime(Runtime::kTraceEnter); 299 } 300 301 // Visit the declarations and body. 302 PrepareForBailoutForId(BailoutId::FunctionEntry(), 303 BailoutState::NO_REGISTERS); 304 { 305 Comment cmnt(masm_, "[ Declarations"); 306 VisitDeclarations(scope()->declarations()); 307 } 308 309 // Assert that the declarations do not use ICs. Otherwise the debugger 310 // won't be able to redirect a PC at an IC to the correct IC in newly 311 // recompiled code. 312 DCHECK_EQ(0, ic_total_count_); 313 314 { 315 Comment cmnt(masm_, "[ Stack check"); 316 PrepareForBailoutForId(BailoutId::Declarations(), 317 BailoutState::NO_REGISTERS); 318 Label ok; 319 DCHECK(jssp.Is(__ StackPointer())); 320 __ CompareRoot(jssp, Heap::kStackLimitRootIndex); 321 __ B(hs, &ok); 322 PredictableCodeSizeScope predictable(masm_, 323 Assembler::kCallSizeWithRelocation); 324 __ Call(isolate()->builtins()->StackCheck(), RelocInfo::CODE_TARGET); 325 __ Bind(&ok); 326 } 327 328 { 329 Comment cmnt(masm_, "[ Body"); 330 DCHECK(loop_depth() == 0); 331 VisitStatements(literal()->body()); 332 DCHECK(loop_depth() == 0); 333 } 334 335 // Always emit a 'return undefined' in case control fell off the end of 336 // the body. 337 { Comment cmnt(masm_, "[ return <undefined>;"); 338 __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); 339 } 340 EmitReturnSequence(); 341 342 // Force emission of the pools, so they don't get emitted in the middle 343 // of the back edge table. 344 masm()->CheckVeneerPool(true, false); 345 masm()->CheckConstPool(true, false); 346 } 347 348 349 void FullCodeGenerator::ClearAccumulator() { 350 __ Mov(x0, Smi::FromInt(0)); 351 } 352 353 354 void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) { 355 __ Mov(x2, Operand(profiling_counter_)); 356 __ Ldr(x3, FieldMemOperand(x2, Cell::kValueOffset)); 357 __ Subs(x3, x3, Smi::FromInt(delta)); 358 __ Str(x3, FieldMemOperand(x2, Cell::kValueOffset)); 359 } 360 361 362 void FullCodeGenerator::EmitProfilingCounterReset() { 363 int reset_value = FLAG_interrupt_budget; 364 __ Mov(x2, Operand(profiling_counter_)); 365 __ Mov(x3, Smi::FromInt(reset_value)); 366 __ Str(x3, FieldMemOperand(x2, Cell::kValueOffset)); 367 } 368 369 370 void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt, 371 Label* back_edge_target) { 372 DCHECK(jssp.Is(__ StackPointer())); 373 Comment cmnt(masm_, "[ Back edge bookkeeping"); 374 // Block literal pools whilst emitting back edge code. 375 Assembler::BlockPoolsScope block_const_pool(masm_); 376 Label ok; 377 378 DCHECK(back_edge_target->is_bound()); 379 // We want to do a round rather than a floor of distance/kCodeSizeMultiplier 380 // to reduce the absolute error due to the integer division. To do that, 381 // we add kCodeSizeMultiplier/2 to the distance (equivalent to adding 0.5 to 382 // the result). 383 int distance = 384 static_cast<int>(masm_->SizeOfCodeGeneratedSince(back_edge_target) + 385 kCodeSizeMultiplier / 2); 386 int weight = Min(kMaxBackEdgeWeight, 387 Max(1, distance / kCodeSizeMultiplier)); 388 EmitProfilingCounterDecrement(weight); 389 __ B(pl, &ok); 390 __ Call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); 391 392 // Record a mapping of this PC offset to the OSR id. This is used to find 393 // the AST id from the unoptimized code in order to use it as a key into 394 // the deoptimization input data found in the optimized code. 395 RecordBackEdge(stmt->OsrEntryId()); 396 397 EmitProfilingCounterReset(); 398 399 __ Bind(&ok); 400 PrepareForBailoutForId(stmt->EntryId(), BailoutState::NO_REGISTERS); 401 // Record a mapping of the OSR id to this PC. This is used if the OSR 402 // entry becomes the target of a bailout. We don't expect it to be, but 403 // we want it to work if it is. 404 PrepareForBailoutForId(stmt->OsrEntryId(), BailoutState::NO_REGISTERS); 405 } 406 407 void FullCodeGenerator::EmitProfilingCounterHandlingForReturnSequence( 408 bool is_tail_call) { 409 // Pretend that the exit is a backwards jump to the entry. 410 int weight = 1; 411 if (info_->ShouldSelfOptimize()) { 412 weight = FLAG_interrupt_budget / FLAG_self_opt_count; 413 } else { 414 int distance = masm_->pc_offset() + kCodeSizeMultiplier / 2; 415 weight = Min(kMaxBackEdgeWeight, Max(1, distance / kCodeSizeMultiplier)); 416 } 417 EmitProfilingCounterDecrement(weight); 418 Label ok; 419 __ B(pl, &ok); 420 // Don't need to save result register if we are going to do a tail call. 421 if (!is_tail_call) { 422 __ Push(x0); 423 } 424 __ Call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); 425 if (!is_tail_call) { 426 __ Pop(x0); 427 } 428 EmitProfilingCounterReset(); 429 __ Bind(&ok); 430 } 431 432 void FullCodeGenerator::EmitReturnSequence() { 433 Comment cmnt(masm_, "[ Return sequence"); 434 435 if (return_label_.is_bound()) { 436 __ B(&return_label_); 437 438 } else { 439 __ Bind(&return_label_); 440 if (FLAG_trace) { 441 // Push the return value on the stack as the parameter. 442 // Runtime::TraceExit returns its parameter in x0. 443 __ Push(result_register()); 444 __ CallRuntime(Runtime::kTraceExit); 445 DCHECK(x0.Is(result_register())); 446 } 447 EmitProfilingCounterHandlingForReturnSequence(false); 448 449 SetReturnPosition(literal()); 450 const Register& current_sp = __ StackPointer(); 451 // Nothing ensures 16 bytes alignment here. 452 DCHECK(!current_sp.Is(csp)); 453 __ Mov(current_sp, fp); 454 __ Ldp(fp, lr, MemOperand(current_sp, 2 * kXRegSize, PostIndex)); 455 // Drop the arguments and receiver and return. 456 // TODO(all): This implementation is overkill as it supports 2**31+1 457 // arguments, consider how to improve it without creating a security 458 // hole. 459 __ ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2); 460 __ Add(current_sp, current_sp, ip0); 461 __ Ret(); 462 int32_t arg_count = info_->scope()->num_parameters() + 1; 463 __ dc64(kXRegSize * arg_count); 464 } 465 } 466 467 void FullCodeGenerator::RestoreContext() { 468 __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); 469 } 470 471 void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { 472 DCHECK(var->IsStackAllocated() || var->IsContextSlot()); 473 codegen()->GetVar(result_register(), var); 474 codegen()->PushOperand(result_register()); 475 } 476 477 478 void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const { 479 // Root values have no side effects. 480 } 481 482 483 void FullCodeGenerator::AccumulatorValueContext::Plug( 484 Heap::RootListIndex index) const { 485 __ LoadRoot(result_register(), index); 486 } 487 488 489 void FullCodeGenerator::StackValueContext::Plug( 490 Heap::RootListIndex index) const { 491 __ LoadRoot(result_register(), index); 492 codegen()->PushOperand(result_register()); 493 } 494 495 496 void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { 497 codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, 498 false_label_); 499 if (index == Heap::kUndefinedValueRootIndex || 500 index == Heap::kNullValueRootIndex || 501 index == Heap::kFalseValueRootIndex) { 502 if (false_label_ != fall_through_) __ B(false_label_); 503 } else if (index == Heap::kTrueValueRootIndex) { 504 if (true_label_ != fall_through_) __ B(true_label_); 505 } else { 506 __ LoadRoot(result_register(), index); 507 codegen()->DoTest(this); 508 } 509 } 510 511 512 void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const { 513 } 514 515 516 void FullCodeGenerator::AccumulatorValueContext::Plug( 517 Handle<Object> lit) const { 518 __ Mov(result_register(), Operand(lit)); 519 } 520 521 522 void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { 523 // Immediates cannot be pushed directly. 524 __ Mov(result_register(), Operand(lit)); 525 codegen()->PushOperand(result_register()); 526 } 527 528 529 void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { 530 codegen()->PrepareForBailoutBeforeSplit(condition(), 531 true, 532 true_label_, 533 false_label_); 534 DCHECK(lit->IsNull(isolate()) || lit->IsUndefined(isolate()) || 535 !lit->IsUndetectable()); 536 if (lit->IsUndefined(isolate()) || lit->IsNull(isolate()) || 537 lit->IsFalse(isolate())) { 538 if (false_label_ != fall_through_) __ B(false_label_); 539 } else if (lit->IsTrue(isolate()) || lit->IsJSObject()) { 540 if (true_label_ != fall_through_) __ B(true_label_); 541 } else if (lit->IsString()) { 542 if (String::cast(*lit)->length() == 0) { 543 if (false_label_ != fall_through_) __ B(false_label_); 544 } else { 545 if (true_label_ != fall_through_) __ B(true_label_); 546 } 547 } else if (lit->IsSmi()) { 548 if (Smi::cast(*lit)->value() == 0) { 549 if (false_label_ != fall_through_) __ B(false_label_); 550 } else { 551 if (true_label_ != fall_through_) __ B(true_label_); 552 } 553 } else { 554 // For simplicity we always test the accumulator register. 555 __ Mov(result_register(), Operand(lit)); 556 codegen()->DoTest(this); 557 } 558 } 559 560 561 void FullCodeGenerator::StackValueContext::DropAndPlug(int count, 562 Register reg) const { 563 DCHECK(count > 0); 564 if (count > 1) codegen()->DropOperands(count - 1); 565 __ Poke(reg, 0); 566 } 567 568 569 void FullCodeGenerator::EffectContext::Plug(Label* materialize_true, 570 Label* materialize_false) const { 571 DCHECK(materialize_true == materialize_false); 572 __ Bind(materialize_true); 573 } 574 575 576 void FullCodeGenerator::AccumulatorValueContext::Plug( 577 Label* materialize_true, 578 Label* materialize_false) const { 579 Label done; 580 __ Bind(materialize_true); 581 __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); 582 __ B(&done); 583 __ Bind(materialize_false); 584 __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); 585 __ Bind(&done); 586 } 587 588 589 void FullCodeGenerator::StackValueContext::Plug( 590 Label* materialize_true, 591 Label* materialize_false) const { 592 Label done; 593 __ Bind(materialize_true); 594 __ LoadRoot(x10, Heap::kTrueValueRootIndex); 595 __ B(&done); 596 __ Bind(materialize_false); 597 __ LoadRoot(x10, Heap::kFalseValueRootIndex); 598 __ Bind(&done); 599 codegen()->PushOperand(x10); 600 } 601 602 603 void FullCodeGenerator::TestContext::Plug(Label* materialize_true, 604 Label* materialize_false) const { 605 DCHECK(materialize_true == true_label_); 606 DCHECK(materialize_false == false_label_); 607 } 608 609 610 void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const { 611 Heap::RootListIndex value_root_index = 612 flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; 613 __ LoadRoot(result_register(), value_root_index); 614 } 615 616 617 void FullCodeGenerator::StackValueContext::Plug(bool flag) const { 618 Heap::RootListIndex value_root_index = 619 flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; 620 __ LoadRoot(x10, value_root_index); 621 codegen()->PushOperand(x10); 622 } 623 624 625 void FullCodeGenerator::TestContext::Plug(bool flag) const { 626 codegen()->PrepareForBailoutBeforeSplit(condition(), 627 true, 628 true_label_, 629 false_label_); 630 if (flag) { 631 if (true_label_ != fall_through_) { 632 __ B(true_label_); 633 } 634 } else { 635 if (false_label_ != fall_through_) { 636 __ B(false_label_); 637 } 638 } 639 } 640 641 642 void FullCodeGenerator::DoTest(Expression* condition, 643 Label* if_true, 644 Label* if_false, 645 Label* fall_through) { 646 Handle<Code> ic = ToBooleanICStub::GetUninitialized(isolate()); 647 CallIC(ic, condition->test_id()); 648 __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); 649 Split(eq, if_true, if_false, fall_through); 650 } 651 652 653 // If (cond), branch to if_true. 654 // If (!cond), branch to if_false. 655 // fall_through is used as an optimization in cases where only one branch 656 // instruction is necessary. 657 void FullCodeGenerator::Split(Condition cond, 658 Label* if_true, 659 Label* if_false, 660 Label* fall_through) { 661 if (if_false == fall_through) { 662 __ B(cond, if_true); 663 } else if (if_true == fall_through) { 664 DCHECK(if_false != fall_through); 665 __ B(NegateCondition(cond), if_false); 666 } else { 667 __ B(cond, if_true); 668 __ B(if_false); 669 } 670 } 671 672 673 MemOperand FullCodeGenerator::StackOperand(Variable* var) { 674 // Offset is negative because higher indexes are at lower addresses. 675 int offset = -var->index() * kXRegSize; 676 // Adjust by a (parameter or local) base offset. 677 if (var->IsParameter()) { 678 offset += (info_->scope()->num_parameters() + 1) * kPointerSize; 679 } else { 680 offset += JavaScriptFrameConstants::kLocal0Offset; 681 } 682 return MemOperand(fp, offset); 683 } 684 685 686 MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { 687 DCHECK(var->IsContextSlot() || var->IsStackAllocated()); 688 if (var->IsContextSlot()) { 689 int context_chain_length = scope()->ContextChainLength(var->scope()); 690 __ LoadContext(scratch, context_chain_length); 691 return ContextMemOperand(scratch, var->index()); 692 } else { 693 return StackOperand(var); 694 } 695 } 696 697 698 void FullCodeGenerator::GetVar(Register dest, Variable* var) { 699 // Use destination as scratch. 700 MemOperand location = VarOperand(var, dest); 701 __ Ldr(dest, location); 702 } 703 704 705 void FullCodeGenerator::SetVar(Variable* var, 706 Register src, 707 Register scratch0, 708 Register scratch1) { 709 DCHECK(var->IsContextSlot() || var->IsStackAllocated()); 710 DCHECK(!AreAliased(src, scratch0, scratch1)); 711 MemOperand location = VarOperand(var, scratch0); 712 __ Str(src, location); 713 714 // Emit the write barrier code if the location is in the heap. 715 if (var->IsContextSlot()) { 716 // scratch0 contains the correct context. 717 __ RecordWriteContextSlot(scratch0, static_cast<int>(location.offset()), 718 src, scratch1, kLRHasBeenSaved, kDontSaveFPRegs); 719 } 720 } 721 722 723 void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, 724 bool should_normalize, 725 Label* if_true, 726 Label* if_false) { 727 // Only prepare for bailouts before splits if we're in a test 728 // context. Otherwise, we let the Visit function deal with the 729 // preparation to avoid preparing with the same AST id twice. 730 if (!context()->IsTest()) return; 731 732 // TODO(all): Investigate to see if there is something to work on here. 733 Label skip; 734 if (should_normalize) { 735 __ B(&skip); 736 } 737 PrepareForBailout(expr, BailoutState::TOS_REGISTER); 738 if (should_normalize) { 739 __ CompareRoot(x0, Heap::kTrueValueRootIndex); 740 Split(eq, if_true, if_false, NULL); 741 __ Bind(&skip); 742 } 743 } 744 745 746 void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) { 747 // The variable in the declaration always resides in the current function 748 // context. 749 DCHECK_EQ(0, scope()->ContextChainLength(variable->scope())); 750 if (FLAG_debug_code) { 751 // Check that we're not inside a with or catch context. 752 __ Ldr(x1, FieldMemOperand(cp, HeapObject::kMapOffset)); 753 __ CompareRoot(x1, Heap::kWithContextMapRootIndex); 754 __ Check(ne, kDeclarationInWithContext); 755 __ CompareRoot(x1, Heap::kCatchContextMapRootIndex); 756 __ Check(ne, kDeclarationInCatchContext); 757 } 758 } 759 760 761 void FullCodeGenerator::VisitVariableDeclaration( 762 VariableDeclaration* declaration) { 763 // If it was not possible to allocate the variable at compile time, we 764 // need to "declare" it at runtime to make sure it actually exists in the 765 // local context. 766 VariableProxy* proxy = declaration->proxy(); 767 VariableMode mode = declaration->mode(); 768 Variable* variable = proxy->var(); 769 bool hole_init = mode == LET || mode == CONST; 770 771 switch (variable->location()) { 772 case VariableLocation::GLOBAL: 773 case VariableLocation::UNALLOCATED: 774 DCHECK(!variable->binding_needs_init()); 775 globals_->Add(variable->name(), zone()); 776 globals_->Add(isolate()->factory()->undefined_value(), zone()); 777 break; 778 779 case VariableLocation::PARAMETER: 780 case VariableLocation::LOCAL: 781 if (hole_init) { 782 Comment cmnt(masm_, "[ VariableDeclaration"); 783 __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); 784 __ Str(x10, StackOperand(variable)); 785 } 786 break; 787 788 case VariableLocation::CONTEXT: 789 if (hole_init) { 790 Comment cmnt(masm_, "[ VariableDeclaration"); 791 EmitDebugCheckDeclarationContext(variable); 792 __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); 793 __ Str(x10, ContextMemOperand(cp, variable->index())); 794 // No write barrier since the_hole_value is in old space. 795 PrepareForBailoutForId(proxy->id(), BailoutState::NO_REGISTERS); 796 } 797 break; 798 799 case VariableLocation::LOOKUP: { 800 Comment cmnt(masm_, "[ VariableDeclaration"); 801 DCHECK_EQ(VAR, mode); 802 DCHECK(!hole_init); 803 __ Mov(x2, Operand(variable->name())); 804 __ Push(x2); 805 __ CallRuntime(Runtime::kDeclareEvalVar); 806 PrepareForBailoutForId(proxy->id(), BailoutState::NO_REGISTERS); 807 break; 808 } 809 } 810 } 811 812 813 void FullCodeGenerator::VisitFunctionDeclaration( 814 FunctionDeclaration* declaration) { 815 VariableProxy* proxy = declaration->proxy(); 816 Variable* variable = proxy->var(); 817 switch (variable->location()) { 818 case VariableLocation::GLOBAL: 819 case VariableLocation::UNALLOCATED: { 820 globals_->Add(variable->name(), zone()); 821 Handle<SharedFunctionInfo> function = 822 Compiler::GetSharedFunctionInfo(declaration->fun(), script(), info_); 823 // Check for stack overflow exception. 824 if (function.is_null()) return SetStackOverflow(); 825 globals_->Add(function, zone()); 826 break; 827 } 828 829 case VariableLocation::PARAMETER: 830 case VariableLocation::LOCAL: { 831 Comment cmnt(masm_, "[ Function Declaration"); 832 VisitForAccumulatorValue(declaration->fun()); 833 __ Str(result_register(), StackOperand(variable)); 834 break; 835 } 836 837 case VariableLocation::CONTEXT: { 838 Comment cmnt(masm_, "[ Function Declaration"); 839 EmitDebugCheckDeclarationContext(variable); 840 VisitForAccumulatorValue(declaration->fun()); 841 __ Str(result_register(), ContextMemOperand(cp, variable->index())); 842 int offset = Context::SlotOffset(variable->index()); 843 // We know that we have written a function, which is not a smi. 844 __ RecordWriteContextSlot(cp, 845 offset, 846 result_register(), 847 x2, 848 kLRHasBeenSaved, 849 kDontSaveFPRegs, 850 EMIT_REMEMBERED_SET, 851 OMIT_SMI_CHECK); 852 PrepareForBailoutForId(proxy->id(), BailoutState::NO_REGISTERS); 853 break; 854 } 855 856 case VariableLocation::LOOKUP: { 857 Comment cmnt(masm_, "[ Function Declaration"); 858 __ Mov(x2, Operand(variable->name())); 859 PushOperand(x2); 860 // Push initial value for function declaration. 861 VisitForStackValue(declaration->fun()); 862 CallRuntimeWithOperands(Runtime::kDeclareEvalFunction); 863 PrepareForBailoutForId(proxy->id(), BailoutState::NO_REGISTERS); 864 break; 865 } 866 } 867 } 868 869 870 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { 871 // Call the runtime to declare the globals. 872 __ Mov(x11, Operand(pairs)); 873 Register flags = xzr; 874 if (Smi::FromInt(DeclareGlobalsFlags())) { 875 flags = x10; 876 __ Mov(flags, Smi::FromInt(DeclareGlobalsFlags())); 877 } 878 __ Push(x11, flags); 879 __ CallRuntime(Runtime::kDeclareGlobals); 880 // Return value is ignored. 881 } 882 883 884 void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) { 885 // Call the runtime to declare the modules. 886 __ Push(descriptions); 887 __ CallRuntime(Runtime::kDeclareModules); 888 // Return value is ignored. 889 } 890 891 892 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { 893 ASM_LOCATION("FullCodeGenerator::VisitSwitchStatement"); 894 Comment cmnt(masm_, "[ SwitchStatement"); 895 Breakable nested_statement(this, stmt); 896 SetStatementPosition(stmt); 897 898 // Keep the switch value on the stack until a case matches. 899 VisitForStackValue(stmt->tag()); 900 PrepareForBailoutForId(stmt->EntryId(), BailoutState::NO_REGISTERS); 901 902 ZoneList<CaseClause*>* clauses = stmt->cases(); 903 CaseClause* default_clause = NULL; // Can occur anywhere in the list. 904 905 Label next_test; // Recycled for each test. 906 // Compile all the tests with branches to their bodies. 907 for (int i = 0; i < clauses->length(); i++) { 908 CaseClause* clause = clauses->at(i); 909 clause->body_target()->Unuse(); 910 911 // The default is not a test, but remember it as final fall through. 912 if (clause->is_default()) { 913 default_clause = clause; 914 continue; 915 } 916 917 Comment cmnt(masm_, "[ Case comparison"); 918 __ Bind(&next_test); 919 next_test.Unuse(); 920 921 // Compile the label expression. 922 VisitForAccumulatorValue(clause->label()); 923 924 // Perform the comparison as if via '==='. 925 __ Peek(x1, 0); // Switch value. 926 927 JumpPatchSite patch_site(masm_); 928 if (ShouldInlineSmiCase(Token::EQ_STRICT)) { 929 Label slow_case; 930 patch_site.EmitJumpIfEitherNotSmi(x0, x1, &slow_case); 931 __ Cmp(x1, x0); 932 __ B(ne, &next_test); 933 __ Drop(1); // Switch value is no longer needed. 934 __ B(clause->body_target()); 935 __ Bind(&slow_case); 936 } 937 938 // Record position before stub call for type feedback. 939 SetExpressionPosition(clause); 940 Handle<Code> ic = 941 CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code(); 942 CallIC(ic, clause->CompareId()); 943 patch_site.EmitPatchInfo(); 944 945 Label skip; 946 __ B(&skip); 947 PrepareForBailout(clause, BailoutState::TOS_REGISTER); 948 __ JumpIfNotRoot(x0, Heap::kTrueValueRootIndex, &next_test); 949 __ Drop(1); 950 __ B(clause->body_target()); 951 __ Bind(&skip); 952 953 __ Cbnz(x0, &next_test); 954 __ Drop(1); // Switch value is no longer needed. 955 __ B(clause->body_target()); 956 } 957 958 // Discard the test value and jump to the default if present, otherwise to 959 // the end of the statement. 960 __ Bind(&next_test); 961 DropOperands(1); // Switch value is no longer needed. 962 if (default_clause == NULL) { 963 __ B(nested_statement.break_label()); 964 } else { 965 __ B(default_clause->body_target()); 966 } 967 968 // Compile all the case bodies. 969 for (int i = 0; i < clauses->length(); i++) { 970 Comment cmnt(masm_, "[ Case body"); 971 CaseClause* clause = clauses->at(i); 972 __ Bind(clause->body_target()); 973 PrepareForBailoutForId(clause->EntryId(), BailoutState::NO_REGISTERS); 974 VisitStatements(clause->statements()); 975 } 976 977 __ Bind(nested_statement.break_label()); 978 PrepareForBailoutForId(stmt->ExitId(), BailoutState::NO_REGISTERS); 979 } 980 981 982 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { 983 ASM_LOCATION("FullCodeGenerator::VisitForInStatement"); 984 Comment cmnt(masm_, "[ ForInStatement"); 985 SetStatementPosition(stmt, SKIP_BREAK); 986 987 FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); 988 989 // TODO(all): This visitor probably needs better comments and a revisit. 990 991 // Get the object to enumerate over. 992 SetExpressionAsStatementPosition(stmt->enumerable()); 993 VisitForAccumulatorValue(stmt->enumerable()); 994 OperandStackDepthIncrement(5); 995 996 Label loop, exit; 997 Iteration loop_statement(this, stmt); 998 increment_loop_depth(); 999 1000 // If the object is null or undefined, skip over the loop, otherwise convert 1001 // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. 1002 Label convert, done_convert; 1003 __ JumpIfSmi(x0, &convert); 1004 __ JumpIfObjectType(x0, x10, x11, FIRST_JS_RECEIVER_TYPE, &done_convert, ge); 1005 __ JumpIfRoot(x0, Heap::kNullValueRootIndex, &exit); 1006 __ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, &exit); 1007 __ Bind(&convert); 1008 ToObjectStub stub(isolate()); 1009 __ CallStub(&stub); 1010 __ Bind(&done_convert); 1011 PrepareForBailoutForId(stmt->ToObjectId(), BailoutState::TOS_REGISTER); 1012 __ Push(x0); 1013 1014 // Check cache validity in generated code. If we cannot guarantee cache 1015 // validity, call the runtime system to check cache validity or get the 1016 // property names in a fixed array. Note: Proxies never have an enum cache, 1017 // so will always take the slow path. 1018 Label call_runtime; 1019 __ CheckEnumCache(x0, x15, x10, x11, x12, x13, &call_runtime); 1020 1021 // The enum cache is valid. Load the map of the object being 1022 // iterated over and use the cache for the iteration. 1023 Label use_cache; 1024 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 1025 __ B(&use_cache); 1026 1027 // Get the set of properties to enumerate. 1028 __ Bind(&call_runtime); 1029 __ Push(x0); // Duplicate the enumerable object on the stack. 1030 __ CallRuntime(Runtime::kForInEnumerate); 1031 PrepareForBailoutForId(stmt->EnumId(), BailoutState::TOS_REGISTER); 1032 1033 // If we got a map from the runtime call, we can do a fast 1034 // modification check. Otherwise, we got a fixed array, and we have 1035 // to do a slow check. 1036 Label fixed_array, no_descriptors; 1037 __ Ldr(x2, FieldMemOperand(x0, HeapObject::kMapOffset)); 1038 __ JumpIfNotRoot(x2, Heap::kMetaMapRootIndex, &fixed_array); 1039 1040 // We got a map in register x0. Get the enumeration cache from it. 1041 __ Bind(&use_cache); 1042 1043 __ EnumLengthUntagged(x1, x0); 1044 __ Cbz(x1, &no_descriptors); 1045 1046 __ LoadInstanceDescriptors(x0, x2); 1047 __ Ldr(x2, FieldMemOperand(x2, DescriptorArray::kEnumCacheOffset)); 1048 __ Ldr(x2, 1049 FieldMemOperand(x2, DescriptorArray::kEnumCacheBridgeCacheOffset)); 1050 1051 // Set up the four remaining stack slots. 1052 __ SmiTag(x1); 1053 // Map, enumeration cache, enum cache length, zero (both last as smis). 1054 __ Push(x0, x2, x1, xzr); 1055 __ B(&loop); 1056 1057 __ Bind(&no_descriptors); 1058 __ Drop(1); 1059 __ B(&exit); 1060 1061 // We got a fixed array in register x0. Iterate through that. 1062 __ Bind(&fixed_array); 1063 1064 __ Mov(x1, Smi::FromInt(1)); // Smi(1) indicates slow check. 1065 __ Ldr(x2, FieldMemOperand(x0, FixedArray::kLengthOffset)); 1066 __ Push(x1, x0, x2); // Smi and array, fixed array length (as smi). 1067 PrepareForBailoutForId(stmt->PrepareId(), BailoutState::NO_REGISTERS); 1068 __ Push(xzr); // Initial index. 1069 1070 // Generate code for doing the condition check. 1071 __ Bind(&loop); 1072 SetExpressionAsStatementPosition(stmt->each()); 1073 1074 // Load the current count to x0, load the length to x1. 1075 __ PeekPair(x0, x1, 0); 1076 __ Cmp(x0, x1); // Compare to the array length. 1077 __ B(hs, loop_statement.break_label()); 1078 1079 // Get the current entry of the array into register r3. 1080 __ Peek(x10, 2 * kXRegSize); 1081 __ Add(x10, x10, Operand::UntagSmiAndScale(x0, kPointerSizeLog2)); 1082 __ Ldr(x3, MemOperand(x10, FixedArray::kHeaderSize - kHeapObjectTag)); 1083 1084 // Get the expected map from the stack or a smi in the 1085 // permanent slow case into register x10. 1086 __ Peek(x2, 3 * kXRegSize); 1087 1088 // Check if the expected map still matches that of the enumerable. 1089 // If not, we may have to filter the key. 1090 Label update_each; 1091 __ Peek(x1, 4 * kXRegSize); 1092 __ Ldr(x11, FieldMemOperand(x1, HeapObject::kMapOffset)); 1093 __ Cmp(x11, x2); 1094 __ B(eq, &update_each); 1095 1096 // We need to filter the key, record slow-path here. 1097 int const vector_index = SmiFromSlot(slot)->value(); 1098 __ EmitLoadTypeFeedbackVector(x0); 1099 __ Mov(x10, Operand(TypeFeedbackVector::MegamorphicSentinel(isolate()))); 1100 __ Str(x10, FieldMemOperand(x0, FixedArray::OffsetOfElementAt(vector_index))); 1101 1102 // Convert the entry to a string or (smi) 0 if it isn't a property 1103 // any more. If the property has been removed while iterating, we 1104 // just skip it. 1105 __ Push(x1, x3); 1106 __ CallRuntime(Runtime::kForInFilter); 1107 PrepareForBailoutForId(stmt->FilterId(), BailoutState::TOS_REGISTER); 1108 __ Mov(x3, x0); 1109 __ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, 1110 loop_statement.continue_label()); 1111 1112 // Update the 'each' property or variable from the possibly filtered 1113 // entry in register x3. 1114 __ Bind(&update_each); 1115 __ Mov(result_register(), x3); 1116 // Perform the assignment as if via '='. 1117 { EffectContext context(this); 1118 EmitAssignment(stmt->each(), stmt->EachFeedbackSlot()); 1119 PrepareForBailoutForId(stmt->AssignmentId(), BailoutState::NO_REGISTERS); 1120 } 1121 1122 // Both Crankshaft and Turbofan expect BodyId to be right before stmt->body(). 1123 PrepareForBailoutForId(stmt->BodyId(), BailoutState::NO_REGISTERS); 1124 // Generate code for the body of the loop. 1125 Visit(stmt->body()); 1126 1127 // Generate code for going to the next element by incrementing 1128 // the index (smi) stored on top of the stack. 1129 __ Bind(loop_statement.continue_label()); 1130 // TODO(all): We could use a callee saved register to avoid popping. 1131 __ Pop(x0); 1132 __ Add(x0, x0, Smi::FromInt(1)); 1133 __ Push(x0); 1134 1135 EmitBackEdgeBookkeeping(stmt, &loop); 1136 __ B(&loop); 1137 1138 // Remove the pointers stored on the stack. 1139 __ Bind(loop_statement.break_label()); 1140 DropOperands(5); 1141 1142 // Exit and decrement the loop depth. 1143 PrepareForBailoutForId(stmt->ExitId(), BailoutState::NO_REGISTERS); 1144 __ Bind(&exit); 1145 decrement_loop_depth(); 1146 } 1147 1148 1149 void FullCodeGenerator::EmitSetHomeObject(Expression* initializer, int offset, 1150 FeedbackVectorSlot slot) { 1151 DCHECK(NeedsHomeObject(initializer)); 1152 __ Peek(StoreDescriptor::ReceiverRegister(), 0); 1153 __ Mov(StoreDescriptor::NameRegister(), 1154 Operand(isolate()->factory()->home_object_symbol())); 1155 __ Peek(StoreDescriptor::ValueRegister(), offset * kPointerSize); 1156 EmitLoadStoreICSlot(slot); 1157 CallStoreIC(); 1158 } 1159 1160 1161 void FullCodeGenerator::EmitSetHomeObjectAccumulator(Expression* initializer, 1162 int offset, 1163 FeedbackVectorSlot slot) { 1164 DCHECK(NeedsHomeObject(initializer)); 1165 __ Move(StoreDescriptor::ReceiverRegister(), x0); 1166 __ Mov(StoreDescriptor::NameRegister(), 1167 Operand(isolate()->factory()->home_object_symbol())); 1168 __ Peek(StoreDescriptor::ValueRegister(), offset * kPointerSize); 1169 EmitLoadStoreICSlot(slot); 1170 CallStoreIC(); 1171 } 1172 1173 1174 void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy, 1175 TypeofMode typeof_mode, 1176 Label* slow) { 1177 Register current = cp; 1178 Register next = x10; 1179 Register temp = x11; 1180 1181 Scope* s = scope(); 1182 while (s != NULL) { 1183 if (s->num_heap_slots() > 0) { 1184 if (s->calls_sloppy_eval()) { 1185 // Check that extension is "the hole". 1186 __ Ldr(temp, ContextMemOperand(current, Context::EXTENSION_INDEX)); 1187 __ JumpIfNotRoot(temp, Heap::kTheHoleValueRootIndex, slow); 1188 } 1189 // Load next context in chain. 1190 __ Ldr(next, ContextMemOperand(current, Context::PREVIOUS_INDEX)); 1191 // Walk the rest of the chain without clobbering cp. 1192 current = next; 1193 } 1194 // If no outer scope calls eval, we do not need to check more 1195 // context extensions. 1196 if (!s->outer_scope_calls_sloppy_eval() || s->is_eval_scope()) break; 1197 s = s->outer_scope(); 1198 } 1199 1200 if (s->is_eval_scope()) { 1201 Label loop, fast; 1202 __ Mov(next, current); 1203 1204 __ Bind(&loop); 1205 // Terminate at native context. 1206 __ Ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset)); 1207 __ JumpIfRoot(temp, Heap::kNativeContextMapRootIndex, &fast); 1208 // Check that extension is "the hole". 1209 __ Ldr(temp, ContextMemOperand(next, Context::EXTENSION_INDEX)); 1210 __ JumpIfNotRoot(temp, Heap::kTheHoleValueRootIndex, slow); 1211 // Load next context in chain. 1212 __ Ldr(next, ContextMemOperand(next, Context::PREVIOUS_INDEX)); 1213 __ B(&loop); 1214 __ Bind(&fast); 1215 } 1216 1217 // All extension objects were empty and it is safe to use a normal global 1218 // load machinery. 1219 EmitGlobalVariableLoad(proxy, typeof_mode); 1220 } 1221 1222 1223 MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, 1224 Label* slow) { 1225 DCHECK(var->IsContextSlot()); 1226 Register context = cp; 1227 Register next = x10; 1228 Register temp = x11; 1229 1230 for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { 1231 if (s->num_heap_slots() > 0) { 1232 if (s->calls_sloppy_eval()) { 1233 // Check that extension is "the hole". 1234 __ Ldr(temp, ContextMemOperand(context, Context::EXTENSION_INDEX)); 1235 __ JumpIfNotRoot(temp, Heap::kTheHoleValueRootIndex, slow); 1236 } 1237 __ Ldr(next, ContextMemOperand(context, Context::PREVIOUS_INDEX)); 1238 // Walk the rest of the chain without clobbering cp. 1239 context = next; 1240 } 1241 } 1242 // Check that last extension is "the hole". 1243 __ Ldr(temp, ContextMemOperand(context, Context::EXTENSION_INDEX)); 1244 __ JumpIfNotRoot(temp, Heap::kTheHoleValueRootIndex, slow); 1245 1246 // This function is used only for loads, not stores, so it's safe to 1247 // return an cp-based operand (the write barrier cannot be allowed to 1248 // destroy the cp register). 1249 return ContextMemOperand(context, var->index()); 1250 } 1251 1252 1253 void FullCodeGenerator::EmitDynamicLookupFastCase(VariableProxy* proxy, 1254 TypeofMode typeof_mode, 1255 Label* slow, Label* done) { 1256 // Generate fast-case code for variables that might be shadowed by 1257 // eval-introduced variables. Eval is used a lot without 1258 // introducing variables. In those cases, we do not want to 1259 // perform a runtime call for all variables in the scope 1260 // containing the eval. 1261 Variable* var = proxy->var(); 1262 if (var->mode() == DYNAMIC_GLOBAL) { 1263 EmitLoadGlobalCheckExtensions(proxy, typeof_mode, slow); 1264 __ B(done); 1265 } else if (var->mode() == DYNAMIC_LOCAL) { 1266 Variable* local = var->local_if_not_shadowed(); 1267 __ Ldr(x0, ContextSlotOperandCheckExtensions(local, slow)); 1268 if (local->mode() == LET || local->mode() == CONST) { 1269 __ JumpIfNotRoot(x0, Heap::kTheHoleValueRootIndex, done); 1270 __ Mov(x0, Operand(var->name())); 1271 __ Push(x0); 1272 __ CallRuntime(Runtime::kThrowReferenceError); 1273 } 1274 __ B(done); 1275 } 1276 } 1277 1278 1279 void FullCodeGenerator::EmitGlobalVariableLoad(VariableProxy* proxy, 1280 TypeofMode typeof_mode) { 1281 #ifdef DEBUG 1282 Variable* var = proxy->var(); 1283 DCHECK(var->IsUnallocatedOrGlobalSlot() || 1284 (var->IsLookupSlot() && var->mode() == DYNAMIC_GLOBAL)); 1285 #endif 1286 __ Mov(LoadGlobalDescriptor::SlotRegister(), 1287 SmiFromSlot(proxy->VariableFeedbackSlot())); 1288 CallLoadGlobalIC(typeof_mode); 1289 } 1290 1291 1292 void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy, 1293 TypeofMode typeof_mode) { 1294 // Record position before possible IC call. 1295 SetExpressionPosition(proxy); 1296 PrepareForBailoutForId(proxy->BeforeId(), BailoutState::NO_REGISTERS); 1297 Variable* var = proxy->var(); 1298 1299 // Three cases: global variables, lookup variables, and all other types of 1300 // variables. 1301 switch (var->location()) { 1302 case VariableLocation::GLOBAL: 1303 case VariableLocation::UNALLOCATED: { 1304 Comment cmnt(masm_, "Global variable"); 1305 EmitGlobalVariableLoad(proxy, typeof_mode); 1306 context()->Plug(x0); 1307 break; 1308 } 1309 1310 case VariableLocation::PARAMETER: 1311 case VariableLocation::LOCAL: 1312 case VariableLocation::CONTEXT: { 1313 DCHECK_EQ(NOT_INSIDE_TYPEOF, typeof_mode); 1314 Comment cmnt(masm_, var->IsContextSlot() 1315 ? "Context variable" 1316 : "Stack variable"); 1317 if (NeedsHoleCheckForLoad(proxy)) { 1318 // Let and const need a read barrier. 1319 GetVar(x0, var); 1320 Label done; 1321 __ JumpIfNotRoot(x0, Heap::kTheHoleValueRootIndex, &done); 1322 if (var->mode() == LET || var->mode() == CONST) { 1323 // Throw a reference error when using an uninitialized let/const 1324 // binding in harmony mode. 1325 __ Mov(x0, Operand(var->name())); 1326 __ Push(x0); 1327 __ CallRuntime(Runtime::kThrowReferenceError); 1328 __ Bind(&done); 1329 } 1330 context()->Plug(x0); 1331 break; 1332 } 1333 context()->Plug(var); 1334 break; 1335 } 1336 1337 case VariableLocation::LOOKUP: { 1338 Label done, slow; 1339 // Generate code for loading from variables potentially shadowed by 1340 // eval-introduced variables. 1341 EmitDynamicLookupFastCase(proxy, typeof_mode, &slow, &done); 1342 __ Bind(&slow); 1343 Comment cmnt(masm_, "Lookup variable"); 1344 __ Push(var->name()); 1345 Runtime::FunctionId function_id = 1346 typeof_mode == NOT_INSIDE_TYPEOF 1347 ? Runtime::kLoadLookupSlot 1348 : Runtime::kLoadLookupSlotInsideTypeof; 1349 __ CallRuntime(function_id); 1350 __ Bind(&done); 1351 context()->Plug(x0); 1352 break; 1353 } 1354 } 1355 } 1356 1357 1358 void FullCodeGenerator::EmitAccessor(ObjectLiteralProperty* property) { 1359 Expression* expression = (property == NULL) ? NULL : property->value(); 1360 if (expression == NULL) { 1361 __ LoadRoot(x10, Heap::kNullValueRootIndex); 1362 PushOperand(x10); 1363 } else { 1364 VisitForStackValue(expression); 1365 if (NeedsHomeObject(expression)) { 1366 DCHECK(property->kind() == ObjectLiteral::Property::GETTER || 1367 property->kind() == ObjectLiteral::Property::SETTER); 1368 int offset = property->kind() == ObjectLiteral::Property::GETTER ? 2 : 3; 1369 EmitSetHomeObject(expression, offset, property->GetSlot()); 1370 } 1371 } 1372 } 1373 1374 1375 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { 1376 Comment cmnt(masm_, "[ ObjectLiteral"); 1377 1378 Handle<FixedArray> constant_properties = expr->constant_properties(); 1379 __ Ldr(x3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 1380 __ Mov(x2, Smi::FromInt(expr->literal_index())); 1381 __ Mov(x1, Operand(constant_properties)); 1382 int flags = expr->ComputeFlags(); 1383 __ Mov(x0, Smi::FromInt(flags)); 1384 if (MustCreateObjectLiteralWithRuntime(expr)) { 1385 __ Push(x3, x2, x1, x0); 1386 __ CallRuntime(Runtime::kCreateObjectLiteral); 1387 } else { 1388 FastCloneShallowObjectStub stub(isolate(), expr->properties_count()); 1389 __ CallStub(&stub); 1390 RestoreContext(); 1391 } 1392 PrepareForBailoutForId(expr->CreateLiteralId(), BailoutState::TOS_REGISTER); 1393 1394 // If result_saved is true the result is on top of the stack. If 1395 // result_saved is false the result is in x0. 1396 bool result_saved = false; 1397 1398 AccessorTable accessor_table(zone()); 1399 int property_index = 0; 1400 for (; property_index < expr->properties()->length(); property_index++) { 1401 ObjectLiteral::Property* property = expr->properties()->at(property_index); 1402 if (property->is_computed_name()) break; 1403 if (property->IsCompileTimeValue()) continue; 1404 1405 Literal* key = property->key()->AsLiteral(); 1406 Expression* value = property->value(); 1407 if (!result_saved) { 1408 PushOperand(x0); // Save result on stack 1409 result_saved = true; 1410 } 1411 switch (property->kind()) { 1412 case ObjectLiteral::Property::CONSTANT: 1413 UNREACHABLE(); 1414 case ObjectLiteral::Property::MATERIALIZED_LITERAL: 1415 DCHECK(!CompileTimeValue::IsCompileTimeValue(property->value())); 1416 // Fall through. 1417 case ObjectLiteral::Property::COMPUTED: 1418 // It is safe to use [[Put]] here because the boilerplate already 1419 // contains computed properties with an uninitialized value. 1420 if (key->value()->IsInternalizedString()) { 1421 if (property->emit_store()) { 1422 VisitForAccumulatorValue(value); 1423 DCHECK(StoreDescriptor::ValueRegister().is(x0)); 1424 __ Mov(StoreDescriptor::NameRegister(), Operand(key->value())); 1425 __ Peek(StoreDescriptor::ReceiverRegister(), 0); 1426 EmitLoadStoreICSlot(property->GetSlot(0)); 1427 CallStoreIC(); 1428 PrepareForBailoutForId(key->id(), BailoutState::NO_REGISTERS); 1429 1430 if (NeedsHomeObject(value)) { 1431 EmitSetHomeObjectAccumulator(value, 0, property->GetSlot(1)); 1432 } 1433 } else { 1434 VisitForEffect(value); 1435 } 1436 break; 1437 } 1438 __ Peek(x0, 0); 1439 PushOperand(x0); 1440 VisitForStackValue(key); 1441 VisitForStackValue(value); 1442 if (property->emit_store()) { 1443 if (NeedsHomeObject(value)) { 1444 EmitSetHomeObject(value, 2, property->GetSlot()); 1445 } 1446 __ Mov(x0, Smi::FromInt(SLOPPY)); // Language mode 1447 PushOperand(x0); 1448 CallRuntimeWithOperands(Runtime::kSetProperty); 1449 } else { 1450 DropOperands(3); 1451 } 1452 break; 1453 case ObjectLiteral::Property::PROTOTYPE: 1454 DCHECK(property->emit_store()); 1455 // Duplicate receiver on stack. 1456 __ Peek(x0, 0); 1457 PushOperand(x0); 1458 VisitForStackValue(value); 1459 CallRuntimeWithOperands(Runtime::kInternalSetPrototype); 1460 PrepareForBailoutForId(expr->GetIdForPropertySet(property_index), 1461 BailoutState::NO_REGISTERS); 1462 break; 1463 case ObjectLiteral::Property::GETTER: 1464 if (property->emit_store()) { 1465 AccessorTable::Iterator it = accessor_table.lookup(key); 1466 it->second->bailout_id = expr->GetIdForPropertySet(property_index); 1467 it->second->getter = property; 1468 } 1469 break; 1470 case ObjectLiteral::Property::SETTER: 1471 if (property->emit_store()) { 1472 AccessorTable::Iterator it = accessor_table.lookup(key); 1473 it->second->bailout_id = expr->GetIdForPropertySet(property_index); 1474 it->second->setter = property; 1475 } 1476 break; 1477 } 1478 } 1479 1480 // Emit code to define accessors, using only a single call to the runtime for 1481 // each pair of corresponding getters and setters. 1482 for (AccessorTable::Iterator it = accessor_table.begin(); 1483 it != accessor_table.end(); 1484 ++it) { 1485 __ Peek(x10, 0); // Duplicate receiver. 1486 PushOperand(x10); 1487 VisitForStackValue(it->first); 1488 EmitAccessor(it->second->getter); 1489 EmitAccessor(it->second->setter); 1490 __ Mov(x10, Smi::FromInt(NONE)); 1491 PushOperand(x10); 1492 CallRuntimeWithOperands(Runtime::kDefineAccessorPropertyUnchecked); 1493 PrepareForBailoutForId(it->second->bailout_id, BailoutState::NO_REGISTERS); 1494 } 1495 1496 // Object literals have two parts. The "static" part on the left contains no 1497 // computed property names, and so we can compute its map ahead of time; see 1498 // runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part 1499 // starts with the first computed property name, and continues with all 1500 // properties to its right. All the code from above initializes the static 1501 // component of the object literal, and arranges for the map of the result to 1502 // reflect the static order in which the keys appear. For the dynamic 1503 // properties, we compile them into a series of "SetOwnProperty" runtime 1504 // calls. This will preserve insertion order. 1505 for (; property_index < expr->properties()->length(); property_index++) { 1506 ObjectLiteral::Property* property = expr->properties()->at(property_index); 1507 1508 Expression* value = property->value(); 1509 if (!result_saved) { 1510 PushOperand(x0); // Save result on stack 1511 result_saved = true; 1512 } 1513 1514 __ Peek(x10, 0); // Duplicate receiver. 1515 PushOperand(x10); 1516 1517 if (property->kind() == ObjectLiteral::Property::PROTOTYPE) { 1518 DCHECK(!property->is_computed_name()); 1519 VisitForStackValue(value); 1520 DCHECK(property->emit_store()); 1521 CallRuntimeWithOperands(Runtime::kInternalSetPrototype); 1522 PrepareForBailoutForId(expr->GetIdForPropertySet(property_index), 1523 BailoutState::NO_REGISTERS); 1524 } else { 1525 EmitPropertyKey(property, expr->GetIdForPropertyName(property_index)); 1526 VisitForStackValue(value); 1527 if (NeedsHomeObject(value)) { 1528 EmitSetHomeObject(value, 2, property->GetSlot()); 1529 } 1530 1531 switch (property->kind()) { 1532 case ObjectLiteral::Property::CONSTANT: 1533 case ObjectLiteral::Property::MATERIALIZED_LITERAL: 1534 case ObjectLiteral::Property::COMPUTED: 1535 if (property->emit_store()) { 1536 PushOperand(Smi::FromInt(NONE)); 1537 PushOperand(Smi::FromInt(property->NeedsSetFunctionName())); 1538 CallRuntimeWithOperands(Runtime::kDefineDataPropertyInLiteral); 1539 PrepareForBailoutForId(expr->GetIdForPropertySet(property_index), 1540 BailoutState::NO_REGISTERS); 1541 } else { 1542 DropOperands(3); 1543 } 1544 break; 1545 1546 case ObjectLiteral::Property::PROTOTYPE: 1547 UNREACHABLE(); 1548 break; 1549 1550 case ObjectLiteral::Property::GETTER: 1551 PushOperand(Smi::FromInt(NONE)); 1552 CallRuntimeWithOperands(Runtime::kDefineGetterPropertyUnchecked); 1553 break; 1554 1555 case ObjectLiteral::Property::SETTER: 1556 PushOperand(Smi::FromInt(NONE)); 1557 CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); 1558 break; 1559 } 1560 } 1561 } 1562 1563 if (result_saved) { 1564 context()->PlugTOS(); 1565 } else { 1566 context()->Plug(x0); 1567 } 1568 } 1569 1570 1571 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { 1572 Comment cmnt(masm_, "[ ArrayLiteral"); 1573 1574 Handle<FixedArray> constant_elements = expr->constant_elements(); 1575 bool has_fast_elements = 1576 IsFastObjectElementsKind(expr->constant_elements_kind()); 1577 1578 AllocationSiteMode allocation_site_mode = TRACK_ALLOCATION_SITE; 1579 if (has_fast_elements && !FLAG_allocation_site_pretenuring) { 1580 // If the only customer of allocation sites is transitioning, then 1581 // we can turn it off if we don't have anywhere else to transition to. 1582 allocation_site_mode = DONT_TRACK_ALLOCATION_SITE; 1583 } 1584 1585 __ Ldr(x3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 1586 __ Mov(x2, Smi::FromInt(expr->literal_index())); 1587 __ Mov(x1, Operand(constant_elements)); 1588 if (MustCreateArrayLiteralWithRuntime(expr)) { 1589 __ Mov(x0, Smi::FromInt(expr->ComputeFlags())); 1590 __ Push(x3, x2, x1, x0); 1591 __ CallRuntime(Runtime::kCreateArrayLiteral); 1592 } else { 1593 FastCloneShallowArrayStub stub(isolate(), allocation_site_mode); 1594 __ CallStub(&stub); 1595 } 1596 PrepareForBailoutForId(expr->CreateLiteralId(), BailoutState::TOS_REGISTER); 1597 1598 bool result_saved = false; // Is the result saved to the stack? 1599 ZoneList<Expression*>* subexprs = expr->values(); 1600 int length = subexprs->length(); 1601 1602 // Emit code to evaluate all the non-constant subexpressions and to store 1603 // them into the newly cloned array. 1604 int array_index = 0; 1605 for (; array_index < length; array_index++) { 1606 Expression* subexpr = subexprs->at(array_index); 1607 DCHECK(!subexpr->IsSpread()); 1608 1609 // If the subexpression is a literal or a simple materialized literal it 1610 // is already set in the cloned array. 1611 if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; 1612 1613 if (!result_saved) { 1614 PushOperand(x0); 1615 result_saved = true; 1616 } 1617 VisitForAccumulatorValue(subexpr); 1618 1619 __ Mov(StoreDescriptor::NameRegister(), Smi::FromInt(array_index)); 1620 __ Peek(StoreDescriptor::ReceiverRegister(), 0); 1621 EmitLoadStoreICSlot(expr->LiteralFeedbackSlot()); 1622 Handle<Code> ic = 1623 CodeFactory::KeyedStoreIC(isolate(), language_mode()).code(); 1624 CallIC(ic); 1625 1626 PrepareForBailoutForId(expr->GetIdForElement(array_index), 1627 BailoutState::NO_REGISTERS); 1628 } 1629 1630 // In case the array literal contains spread expressions it has two parts. The 1631 // first part is the "static" array which has a literal index is handled 1632 // above. The second part is the part after the first spread expression 1633 // (inclusive) and these elements gets appended to the array. Note that the 1634 // number elements an iterable produces is unknown ahead of time. 1635 if (array_index < length && result_saved) { 1636 PopOperand(x0); 1637 result_saved = false; 1638 } 1639 for (; array_index < length; array_index++) { 1640 Expression* subexpr = subexprs->at(array_index); 1641 1642 PushOperand(x0); 1643 DCHECK(!subexpr->IsSpread()); 1644 VisitForStackValue(subexpr); 1645 CallRuntimeWithOperands(Runtime::kAppendElement); 1646 1647 PrepareForBailoutForId(expr->GetIdForElement(array_index), 1648 BailoutState::NO_REGISTERS); 1649 } 1650 1651 if (result_saved) { 1652 context()->PlugTOS(); 1653 } else { 1654 context()->Plug(x0); 1655 } 1656 } 1657 1658 1659 void FullCodeGenerator::VisitAssignment(Assignment* expr) { 1660 DCHECK(expr->target()->IsValidReferenceExpressionOrThis()); 1661 1662 Comment cmnt(masm_, "[ Assignment"); 1663 1664 Property* property = expr->target()->AsProperty(); 1665 LhsKind assign_type = Property::GetAssignType(property); 1666 1667 // Evaluate LHS expression. 1668 switch (assign_type) { 1669 case VARIABLE: 1670 // Nothing to do here. 1671 break; 1672 case NAMED_PROPERTY: 1673 if (expr->is_compound()) { 1674 // We need the receiver both on the stack and in the register. 1675 VisitForStackValue(property->obj()); 1676 __ Peek(LoadDescriptor::ReceiverRegister(), 0); 1677 } else { 1678 VisitForStackValue(property->obj()); 1679 } 1680 break; 1681 case NAMED_SUPER_PROPERTY: 1682 VisitForStackValue( 1683 property->obj()->AsSuperPropertyReference()->this_var()); 1684 VisitForAccumulatorValue( 1685 property->obj()->AsSuperPropertyReference()->home_object()); 1686 PushOperand(result_register()); 1687 if (expr->is_compound()) { 1688 const Register scratch = x10; 1689 __ Peek(scratch, kPointerSize); 1690 PushOperands(scratch, result_register()); 1691 } 1692 break; 1693 case KEYED_SUPER_PROPERTY: 1694 VisitForStackValue( 1695 property->obj()->AsSuperPropertyReference()->this_var()); 1696 VisitForStackValue( 1697 property->obj()->AsSuperPropertyReference()->home_object()); 1698 VisitForAccumulatorValue(property->key()); 1699 PushOperand(result_register()); 1700 if (expr->is_compound()) { 1701 const Register scratch1 = x10; 1702 const Register scratch2 = x11; 1703 __ Peek(scratch1, 2 * kPointerSize); 1704 __ Peek(scratch2, kPointerSize); 1705 PushOperands(scratch1, scratch2, result_register()); 1706 } 1707 break; 1708 case KEYED_PROPERTY: 1709 if (expr->is_compound()) { 1710 VisitForStackValue(property->obj()); 1711 VisitForStackValue(property->key()); 1712 __ Peek(LoadDescriptor::ReceiverRegister(), 1 * kPointerSize); 1713 __ Peek(LoadDescriptor::NameRegister(), 0); 1714 } else { 1715 VisitForStackValue(property->obj()); 1716 VisitForStackValue(property->key()); 1717 } 1718 break; 1719 } 1720 1721 // For compound assignments we need another deoptimization point after the 1722 // variable/property load. 1723 if (expr->is_compound()) { 1724 { AccumulatorValueContext context(this); 1725 switch (assign_type) { 1726 case VARIABLE: 1727 EmitVariableLoad(expr->target()->AsVariableProxy()); 1728 PrepareForBailout(expr->target(), BailoutState::TOS_REGISTER); 1729 break; 1730 case NAMED_PROPERTY: 1731 EmitNamedPropertyLoad(property); 1732 PrepareForBailoutForId(property->LoadId(), 1733 BailoutState::TOS_REGISTER); 1734 break; 1735 case NAMED_SUPER_PROPERTY: 1736 EmitNamedSuperPropertyLoad(property); 1737 PrepareForBailoutForId(property->LoadId(), 1738 BailoutState::TOS_REGISTER); 1739 break; 1740 case KEYED_SUPER_PROPERTY: 1741 EmitKeyedSuperPropertyLoad(property); 1742 PrepareForBailoutForId(property->LoadId(), 1743 BailoutState::TOS_REGISTER); 1744 break; 1745 case KEYED_PROPERTY: 1746 EmitKeyedPropertyLoad(property); 1747 PrepareForBailoutForId(property->LoadId(), 1748 BailoutState::TOS_REGISTER); 1749 break; 1750 } 1751 } 1752 1753 Token::Value op = expr->binary_op(); 1754 PushOperand(x0); // Left operand goes on the stack. 1755 VisitForAccumulatorValue(expr->value()); 1756 1757 AccumulatorValueContext context(this); 1758 if (ShouldInlineSmiCase(op)) { 1759 EmitInlineSmiBinaryOp(expr->binary_operation(), 1760 op, 1761 expr->target(), 1762 expr->value()); 1763 } else { 1764 EmitBinaryOp(expr->binary_operation(), op); 1765 } 1766 1767 // Deoptimization point in case the binary operation may have side effects. 1768 PrepareForBailout(expr->binary_operation(), BailoutState::TOS_REGISTER); 1769 } else { 1770 VisitForAccumulatorValue(expr->value()); 1771 } 1772 1773 SetExpressionPosition(expr); 1774 1775 // Store the value. 1776 switch (assign_type) { 1777 case VARIABLE: 1778 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), 1779 expr->op(), expr->AssignmentSlot()); 1780 PrepareForBailoutForId(expr->AssignmentId(), BailoutState::TOS_REGISTER); 1781 context()->Plug(x0); 1782 break; 1783 case NAMED_PROPERTY: 1784 EmitNamedPropertyAssignment(expr); 1785 break; 1786 case NAMED_SUPER_PROPERTY: 1787 EmitNamedSuperPropertyStore(property); 1788 context()->Plug(x0); 1789 break; 1790 case KEYED_SUPER_PROPERTY: 1791 EmitKeyedSuperPropertyStore(property); 1792 context()->Plug(x0); 1793 break; 1794 case KEYED_PROPERTY: 1795 EmitKeyedPropertyAssignment(expr); 1796 break; 1797 } 1798 } 1799 1800 1801 void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, 1802 Token::Value op, 1803 Expression* left_expr, 1804 Expression* right_expr) { 1805 Label done, both_smis, stub_call; 1806 1807 // Get the arguments. 1808 Register left = x1; 1809 Register right = x0; 1810 Register result = x0; 1811 PopOperand(left); 1812 1813 // Perform combined smi check on both operands. 1814 __ Orr(x10, left, right); 1815 JumpPatchSite patch_site(masm_); 1816 patch_site.EmitJumpIfSmi(x10, &both_smis); 1817 1818 __ Bind(&stub_call); 1819 1820 Handle<Code> code = CodeFactory::BinaryOpIC(isolate(), op).code(); 1821 { 1822 Assembler::BlockPoolsScope scope(masm_); 1823 CallIC(code, expr->BinaryOperationFeedbackId()); 1824 patch_site.EmitPatchInfo(); 1825 } 1826 __ B(&done); 1827 1828 __ Bind(&both_smis); 1829 // Smi case. This code works in the same way as the smi-smi case in the type 1830 // recording binary operation stub, see 1831 // BinaryOpStub::GenerateSmiSmiOperation for comments. 1832 // TODO(all): That doesn't exist any more. Where are the comments? 1833 // 1834 // The set of operations that needs to be supported here is controlled by 1835 // FullCodeGenerator::ShouldInlineSmiCase(). 1836 switch (op) { 1837 case Token::SAR: 1838 __ Ubfx(right, right, kSmiShift, 5); 1839 __ Asr(result, left, right); 1840 __ Bic(result, result, kSmiShiftMask); 1841 break; 1842 case Token::SHL: 1843 __ Ubfx(right, right, kSmiShift, 5); 1844 __ Lsl(result, left, right); 1845 break; 1846 case Token::SHR: 1847 // If `left >>> right` >= 0x80000000, the result is not representable in a 1848 // signed 32-bit smi. 1849 __ Ubfx(right, right, kSmiShift, 5); 1850 __ Lsr(x10, left, right); 1851 __ Tbnz(x10, kXSignBit, &stub_call); 1852 __ Bic(result, x10, kSmiShiftMask); 1853 break; 1854 case Token::ADD: 1855 __ Adds(x10, left, right); 1856 __ B(vs, &stub_call); 1857 __ Mov(result, x10); 1858 break; 1859 case Token::SUB: 1860 __ Subs(x10, left, right); 1861 __ B(vs, &stub_call); 1862 __ Mov(result, x10); 1863 break; 1864 case Token::MUL: { 1865 Label not_minus_zero, done; 1866 STATIC_ASSERT(static_cast<unsigned>(kSmiShift) == (kXRegSizeInBits / 2)); 1867 STATIC_ASSERT(kSmiTag == 0); 1868 __ Smulh(x10, left, right); 1869 __ Cbnz(x10, ¬_minus_zero); 1870 __ Eor(x11, left, right); 1871 __ Tbnz(x11, kXSignBit, &stub_call); 1872 __ Mov(result, x10); 1873 __ B(&done); 1874 __ Bind(¬_minus_zero); 1875 __ Cls(x11, x10); 1876 __ Cmp(x11, kXRegSizeInBits - kSmiShift); 1877 __ B(lt, &stub_call); 1878 __ SmiTag(result, x10); 1879 __ Bind(&done); 1880 break; 1881 } 1882 case Token::BIT_OR: 1883 __ Orr(result, left, right); 1884 break; 1885 case Token::BIT_AND: 1886 __ And(result, left, right); 1887 break; 1888 case Token::BIT_XOR: 1889 __ Eor(result, left, right); 1890 break; 1891 default: 1892 UNREACHABLE(); 1893 } 1894 1895 __ Bind(&done); 1896 context()->Plug(x0); 1897 } 1898 1899 1900 void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op) { 1901 PopOperand(x1); 1902 Handle<Code> code = CodeFactory::BinaryOpIC(isolate(), op).code(); 1903 JumpPatchSite patch_site(masm_); // Unbound, signals no inlined smi code. 1904 { 1905 Assembler::BlockPoolsScope scope(masm_); 1906 CallIC(code, expr->BinaryOperationFeedbackId()); 1907 patch_site.EmitPatchInfo(); 1908 } 1909 context()->Plug(x0); 1910 } 1911 1912 1913 void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { 1914 for (int i = 0; i < lit->properties()->length(); i++) { 1915 ObjectLiteral::Property* property = lit->properties()->at(i); 1916 Expression* value = property->value(); 1917 1918 Register scratch = x1; 1919 if (property->is_static()) { 1920 __ Peek(scratch, kPointerSize); // constructor 1921 } else { 1922 __ Peek(scratch, 0); // prototype 1923 } 1924 PushOperand(scratch); 1925 EmitPropertyKey(property, lit->GetIdForProperty(i)); 1926 1927 // The static prototype property is read only. We handle the non computed 1928 // property name case in the parser. Since this is the only case where we 1929 // need to check for an own read only property we special case this so we do 1930 // not need to do this for every property. 1931 if (property->is_static() && property->is_computed_name()) { 1932 __ CallRuntime(Runtime::kThrowIfStaticPrototype); 1933 __ Push(x0); 1934 } 1935 1936 VisitForStackValue(value); 1937 if (NeedsHomeObject(value)) { 1938 EmitSetHomeObject(value, 2, property->GetSlot()); 1939 } 1940 1941 switch (property->kind()) { 1942 case ObjectLiteral::Property::CONSTANT: 1943 case ObjectLiteral::Property::MATERIALIZED_LITERAL: 1944 case ObjectLiteral::Property::PROTOTYPE: 1945 UNREACHABLE(); 1946 case ObjectLiteral::Property::COMPUTED: 1947 PushOperand(Smi::FromInt(DONT_ENUM)); 1948 PushOperand(Smi::FromInt(property->NeedsSetFunctionName())); 1949 CallRuntimeWithOperands(Runtime::kDefineDataPropertyInLiteral); 1950 break; 1951 1952 case ObjectLiteral::Property::GETTER: 1953 PushOperand(Smi::FromInt(DONT_ENUM)); 1954 CallRuntimeWithOperands(Runtime::kDefineGetterPropertyUnchecked); 1955 break; 1956 1957 case ObjectLiteral::Property::SETTER: 1958 PushOperand(Smi::FromInt(DONT_ENUM)); 1959 CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); 1960 break; 1961 1962 default: 1963 UNREACHABLE(); 1964 } 1965 } 1966 } 1967 1968 1969 void FullCodeGenerator::EmitAssignment(Expression* expr, 1970 FeedbackVectorSlot slot) { 1971 DCHECK(expr->IsValidReferenceExpressionOrThis()); 1972 1973 Property* prop = expr->AsProperty(); 1974 LhsKind assign_type = Property::GetAssignType(prop); 1975 1976 switch (assign_type) { 1977 case VARIABLE: { 1978 Variable* var = expr->AsVariableProxy()->var(); 1979 EffectContext context(this); 1980 EmitVariableAssignment(var, Token::ASSIGN, slot); 1981 break; 1982 } 1983 case NAMED_PROPERTY: { 1984 PushOperand(x0); // Preserve value. 1985 VisitForAccumulatorValue(prop->obj()); 1986 // TODO(all): We could introduce a VisitForRegValue(reg, expr) to avoid 1987 // this copy. 1988 __ Mov(StoreDescriptor::ReceiverRegister(), x0); 1989 PopOperand(StoreDescriptor::ValueRegister()); // Restore value. 1990 __ Mov(StoreDescriptor::NameRegister(), 1991 Operand(prop->key()->AsLiteral()->value())); 1992 EmitLoadStoreICSlot(slot); 1993 CallStoreIC(); 1994 break; 1995 } 1996 case NAMED_SUPER_PROPERTY: { 1997 PushOperand(x0); 1998 VisitForStackValue(prop->obj()->AsSuperPropertyReference()->this_var()); 1999 VisitForAccumulatorValue( 2000 prop->obj()->AsSuperPropertyReference()->home_object()); 2001 // stack: value, this; x0: home_object 2002 Register scratch = x10; 2003 Register scratch2 = x11; 2004 __ mov(scratch, result_register()); // home_object 2005 __ Peek(x0, kPointerSize); // value 2006 __ Peek(scratch2, 0); // this 2007 __ Poke(scratch2, kPointerSize); // this 2008 __ Poke(scratch, 0); // home_object 2009 // stack: this, home_object; x0: value 2010 EmitNamedSuperPropertyStore(prop); 2011 break; 2012 } 2013 case KEYED_SUPER_PROPERTY: { 2014 PushOperand(x0); 2015 VisitForStackValue(prop->obj()->AsSuperPropertyReference()->this_var()); 2016 VisitForStackValue( 2017 prop->obj()->AsSuperPropertyReference()->home_object()); 2018 VisitForAccumulatorValue(prop->key()); 2019 Register scratch = x10; 2020 Register scratch2 = x11; 2021 __ Peek(scratch2, 2 * kPointerSize); // value 2022 // stack: value, this, home_object; x0: key, x11: value 2023 __ Peek(scratch, kPointerSize); // this 2024 __ Poke(scratch, 2 * kPointerSize); 2025 __ Peek(scratch, 0); // home_object 2026 __ Poke(scratch, kPointerSize); 2027 __ Poke(x0, 0); 2028 __ Move(x0, scratch2); 2029 // stack: this, home_object, key; x0: value. 2030 EmitKeyedSuperPropertyStore(prop); 2031 break; 2032 } 2033 case KEYED_PROPERTY: { 2034 PushOperand(x0); // Preserve value. 2035 VisitForStackValue(prop->obj()); 2036 VisitForAccumulatorValue(prop->key()); 2037 __ Mov(StoreDescriptor::NameRegister(), x0); 2038 PopOperands(StoreDescriptor::ReceiverRegister(), 2039 StoreDescriptor::ValueRegister()); 2040 EmitLoadStoreICSlot(slot); 2041 Handle<Code> ic = 2042 CodeFactory::KeyedStoreIC(isolate(), language_mode()).code(); 2043 CallIC(ic); 2044 break; 2045 } 2046 } 2047 context()->Plug(x0); 2048 } 2049 2050 2051 void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( 2052 Variable* var, MemOperand location) { 2053 __ Str(result_register(), location); 2054 if (var->IsContextSlot()) { 2055 // RecordWrite may destroy all its register arguments. 2056 __ Mov(x10, result_register()); 2057 int offset = Context::SlotOffset(var->index()); 2058 __ RecordWriteContextSlot( 2059 x1, offset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs); 2060 } 2061 } 2062 2063 2064 void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op, 2065 FeedbackVectorSlot slot) { 2066 ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment"); 2067 if (var->IsUnallocated()) { 2068 // Global var, const, or let. 2069 __ Mov(StoreDescriptor::NameRegister(), Operand(var->name())); 2070 __ LoadGlobalObject(StoreDescriptor::ReceiverRegister()); 2071 EmitLoadStoreICSlot(slot); 2072 CallStoreIC(); 2073 2074 } else if (var->mode() == LET && op != Token::INIT) { 2075 // Non-initializing assignment to let variable needs a write barrier. 2076 DCHECK(!var->IsLookupSlot()); 2077 DCHECK(var->IsStackAllocated() || var->IsContextSlot()); 2078 Label assign; 2079 MemOperand location = VarOperand(var, x1); 2080 __ Ldr(x10, location); 2081 __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign); 2082 __ Mov(x10, Operand(var->name())); 2083 __ Push(x10); 2084 __ CallRuntime(Runtime::kThrowReferenceError); 2085 // Perform the assignment. 2086 __ Bind(&assign); 2087 EmitStoreToStackLocalOrContextSlot(var, location); 2088 2089 } else if (var->mode() == CONST && op != Token::INIT) { 2090 // Assignment to const variable needs a write barrier. 2091 DCHECK(!var->IsLookupSlot()); 2092 DCHECK(var->IsStackAllocated() || var->IsContextSlot()); 2093 Label const_error; 2094 MemOperand location = VarOperand(var, x1); 2095 __ Ldr(x10, location); 2096 __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &const_error); 2097 __ Mov(x10, Operand(var->name())); 2098 __ Push(x10); 2099 __ CallRuntime(Runtime::kThrowReferenceError); 2100 __ Bind(&const_error); 2101 __ CallRuntime(Runtime::kThrowConstAssignError); 2102 2103 } else if (var->is_this() && var->mode() == CONST && op == Token::INIT) { 2104 // Initializing assignment to const {this} needs a write barrier. 2105 DCHECK(var->IsStackAllocated() || var->IsContextSlot()); 2106 Label uninitialized_this; 2107 MemOperand location = VarOperand(var, x1); 2108 __ Ldr(x10, location); 2109 __ JumpIfRoot(x10, Heap::kTheHoleValueRootIndex, &uninitialized_this); 2110 __ Mov(x0, Operand(var->name())); 2111 __ Push(x0); 2112 __ CallRuntime(Runtime::kThrowReferenceError); 2113 __ bind(&uninitialized_this); 2114 EmitStoreToStackLocalOrContextSlot(var, location); 2115 2116 } else if (!var->is_const_mode() || op == Token::INIT) { 2117 if (var->IsLookupSlot()) { 2118 // Assignment to var. 2119 __ Push(var->name()); 2120 __ Push(x0); 2121 __ CallRuntime(is_strict(language_mode()) 2122 ? Runtime::kStoreLookupSlot_Strict 2123 : Runtime::kStoreLookupSlot_Sloppy); 2124 } else { 2125 // Assignment to var or initializing assignment to let/const in harmony 2126 // mode. 2127 DCHECK(var->IsStackAllocated() || var->IsContextSlot()); 2128 MemOperand location = VarOperand(var, x1); 2129 if (FLAG_debug_code && var->mode() == LET && op == Token::INIT) { 2130 __ Ldr(x10, location); 2131 __ CompareRoot(x10, Heap::kTheHoleValueRootIndex); 2132 __ Check(eq, kLetBindingReInitialization); 2133 } 2134 EmitStoreToStackLocalOrContextSlot(var, location); 2135 } 2136 2137 } else { 2138 DCHECK(var->mode() == CONST_LEGACY && op != Token::INIT); 2139 if (is_strict(language_mode())) { 2140 __ CallRuntime(Runtime::kThrowConstAssignError); 2141 } 2142 // Silently ignore store in sloppy mode. 2143 } 2144 } 2145 2146 2147 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { 2148 ASM_LOCATION("FullCodeGenerator::EmitNamedPropertyAssignment"); 2149 // Assignment to a property, using a named store IC. 2150 Property* prop = expr->target()->AsProperty(); 2151 DCHECK(prop != NULL); 2152 DCHECK(prop->key()->IsLiteral()); 2153 2154 __ Mov(StoreDescriptor::NameRegister(), 2155 Operand(prop->key()->AsLiteral()->value())); 2156 PopOperand(StoreDescriptor::ReceiverRegister()); 2157 EmitLoadStoreICSlot(expr->AssignmentSlot()); 2158 CallStoreIC(); 2159 2160 PrepareForBailoutForId(expr->AssignmentId(), BailoutState::TOS_REGISTER); 2161 context()->Plug(x0); 2162 } 2163 2164 2165 void FullCodeGenerator::EmitNamedSuperPropertyStore(Property* prop) { 2166 // Assignment to named property of super. 2167 // x0 : value 2168 // stack : receiver ('this'), home_object 2169 DCHECK(prop != NULL); 2170 Literal* key = prop->key()->AsLiteral(); 2171 DCHECK(key != NULL); 2172 2173 PushOperand(key->value()); 2174 PushOperand(x0); 2175 CallRuntimeWithOperands(is_strict(language_mode()) 2176 ? Runtime::kStoreToSuper_Strict 2177 : Runtime::kStoreToSuper_Sloppy); 2178 } 2179 2180 2181 void FullCodeGenerator::EmitKeyedSuperPropertyStore(Property* prop) { 2182 // Assignment to named property of super. 2183 // x0 : value 2184 // stack : receiver ('this'), home_object, key 2185 DCHECK(prop != NULL); 2186 2187 PushOperand(x0); 2188 CallRuntimeWithOperands(is_strict(language_mode()) 2189 ? Runtime::kStoreKeyedToSuper_Strict 2190 : Runtime::kStoreKeyedToSuper_Sloppy); 2191 } 2192 2193 2194 void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { 2195 ASM_LOCATION("FullCodeGenerator::EmitKeyedPropertyAssignment"); 2196 // Assignment to a property, using a keyed store IC. 2197 2198 // TODO(all): Could we pass this in registers rather than on the stack? 2199 PopOperands(StoreDescriptor::NameRegister(), 2200 StoreDescriptor::ReceiverRegister()); 2201 DCHECK(StoreDescriptor::ValueRegister().is(x0)); 2202 2203 Handle<Code> ic = 2204 CodeFactory::KeyedStoreIC(isolate(), language_mode()).code(); 2205 EmitLoadStoreICSlot(expr->AssignmentSlot()); 2206 CallIC(ic); 2207 2208 PrepareForBailoutForId(expr->AssignmentId(), BailoutState::TOS_REGISTER); 2209 context()->Plug(x0); 2210 } 2211 2212 2213 void FullCodeGenerator::CallIC(Handle<Code> code, 2214 TypeFeedbackId ast_id) { 2215 ic_total_count_++; 2216 // All calls must have a predictable size in full-codegen code to ensure that 2217 // the debugger can patch them correctly. 2218 __ Call(code, RelocInfo::CODE_TARGET, ast_id); 2219 } 2220 2221 2222 // Code common for calls using the IC. 2223 void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { 2224 ASM_LOCATION("FullCodeGenerator::EmitCallWithLoadIC"); 2225 Expression* callee = expr->expression(); 2226 2227 // Get the target function. 2228 ConvertReceiverMode convert_mode; 2229 if (callee->IsVariableProxy()) { 2230 { StackValueContext context(this); 2231 EmitVariableLoad(callee->AsVariableProxy()); 2232 PrepareForBailout(callee, BailoutState::NO_REGISTERS); 2233 } 2234 // Push undefined as receiver. This is patched in the method prologue if it 2235 // is a sloppy mode method. 2236 { 2237 UseScratchRegisterScope temps(masm_); 2238 Register temp = temps.AcquireX(); 2239 __ LoadRoot(temp, Heap::kUndefinedValueRootIndex); 2240 PushOperand(temp); 2241 } 2242 convert_mode = ConvertReceiverMode::kNullOrUndefined; 2243 } else { 2244 // Load the function from the receiver. 2245 DCHECK(callee->IsProperty()); 2246 DCHECK(!callee->AsProperty()->IsSuperAccess()); 2247 __ Peek(LoadDescriptor::ReceiverRegister(), 0); 2248 EmitNamedPropertyLoad(callee->AsProperty()); 2249 PrepareForBailoutForId(callee->AsProperty()->LoadId(), 2250 BailoutState::TOS_REGISTER); 2251 // Push the target function under the receiver. 2252 PopOperand(x10); 2253 PushOperands(x0, x10); 2254 convert_mode = ConvertReceiverMode::kNotNullOrUndefined; 2255 } 2256 2257 EmitCall(expr, convert_mode); 2258 } 2259 2260 2261 void FullCodeGenerator::EmitSuperCallWithLoadIC(Call* expr) { 2262 ASM_LOCATION("FullCodeGenerator::EmitSuperCallWithLoadIC"); 2263 Expression* callee = expr->expression(); 2264 DCHECK(callee->IsProperty()); 2265 Property* prop = callee->AsProperty(); 2266 DCHECK(prop->IsSuperAccess()); 2267 SetExpressionPosition(prop); 2268 2269 Literal* key = prop->key()->AsLiteral(); 2270 DCHECK(!key->value()->IsSmi()); 2271 2272 // Load the function from the receiver. 2273 const Register scratch = x10; 2274 SuperPropertyReference* super_ref = 2275 callee->AsProperty()->obj()->AsSuperPropertyReference(); 2276 VisitForStackValue(super_ref->home_object()); 2277 VisitForAccumulatorValue(super_ref->this_var()); 2278 PushOperand(x0); 2279 __ Peek(scratch, kPointerSize); 2280 PushOperands(x0, scratch); 2281 PushOperand(key->value()); 2282 2283 // Stack here: 2284 // - home_object 2285 // - this (receiver) 2286 // - this (receiver) <-- LoadFromSuper will pop here and below. 2287 // - home_object 2288 // - key 2289 CallRuntimeWithOperands(Runtime::kLoadFromSuper); 2290 PrepareForBailoutForId(prop->LoadId(), BailoutState::TOS_REGISTER); 2291 2292 // Replace home_object with target function. 2293 __ Poke(x0, kPointerSize); 2294 2295 // Stack here: 2296 // - target function 2297 // - this (receiver) 2298 EmitCall(expr); 2299 } 2300 2301 2302 // Code common for calls using the IC. 2303 void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, 2304 Expression* key) { 2305 ASM_LOCATION("FullCodeGenerator::EmitKeyedCallWithLoadIC"); 2306 // Load the key. 2307 VisitForAccumulatorValue(key); 2308 2309 Expression* callee = expr->expression(); 2310 2311 // Load the function from the receiver. 2312 DCHECK(callee->IsProperty()); 2313 __ Peek(LoadDescriptor::ReceiverRegister(), 0); 2314 __ Move(LoadDescriptor::NameRegister(), x0); 2315 EmitKeyedPropertyLoad(callee->AsProperty()); 2316 PrepareForBailoutForId(callee->AsProperty()->LoadId(), 2317 BailoutState::TOS_REGISTER); 2318 2319 // Push the target function under the receiver. 2320 PopOperand(x10); 2321 PushOperands(x0, x10); 2322 2323 EmitCall(expr, ConvertReceiverMode::kNotNullOrUndefined); 2324 } 2325 2326 2327 void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) { 2328 ASM_LOCATION("FullCodeGenerator::EmitKeyedSuperCallWithLoadIC"); 2329 Expression* callee = expr->expression(); 2330 DCHECK(callee->IsProperty()); 2331 Property* prop = callee->AsProperty(); 2332 DCHECK(prop->IsSuperAccess()); 2333 SetExpressionPosition(prop); 2334 2335 // Load the function from the receiver. 2336 const Register scratch = x10; 2337 SuperPropertyReference* super_ref = 2338 callee->AsProperty()->obj()->AsSuperPropertyReference(); 2339 VisitForStackValue(super_ref->home_object()); 2340 VisitForAccumulatorValue(super_ref->this_var()); 2341 PushOperand(x0); 2342 __ Peek(scratch, kPointerSize); 2343 PushOperands(x0, scratch); 2344 VisitForStackValue(prop->key()); 2345 2346 // Stack here: 2347 // - home_object 2348 // - this (receiver) 2349 // - this (receiver) <-- LoadKeyedFromSuper will pop here and below. 2350 // - home_object 2351 // - key 2352 CallRuntimeWithOperands(Runtime::kLoadKeyedFromSuper); 2353 PrepareForBailoutForId(prop->LoadId(), BailoutState::TOS_REGISTER); 2354 2355 // Replace home_object with target function. 2356 __ Poke(x0, kPointerSize); 2357 2358 // Stack here: 2359 // - target function 2360 // - this (receiver) 2361 EmitCall(expr); 2362 } 2363 2364 2365 void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) { 2366 ASM_LOCATION("FullCodeGenerator::EmitCall"); 2367 // Load the arguments. 2368 ZoneList<Expression*>* args = expr->arguments(); 2369 int arg_count = args->length(); 2370 for (int i = 0; i < arg_count; i++) { 2371 VisitForStackValue(args->at(i)); 2372 } 2373 2374 PrepareForBailoutForId(expr->CallId(), BailoutState::NO_REGISTERS); 2375 SetCallPosition(expr, expr->tail_call_mode()); 2376 if (expr->tail_call_mode() == TailCallMode::kAllow) { 2377 if (FLAG_trace) { 2378 __ CallRuntime(Runtime::kTraceTailCall); 2379 } 2380 // Update profiling counters before the tail call since we will 2381 // not return to this function. 2382 EmitProfilingCounterHandlingForReturnSequence(true); 2383 } 2384 Handle<Code> ic = 2385 CodeFactory::CallIC(isolate(), arg_count, mode, expr->tail_call_mode()) 2386 .code(); 2387 __ Mov(x3, SmiFromSlot(expr->CallFeedbackICSlot())); 2388 __ Peek(x1, (arg_count + 1) * kXRegSize); 2389 // Don't assign a type feedback id to the IC, since type feedback is provided 2390 // by the vector above. 2391 CallIC(ic); 2392 OperandStackDepthDecrement(arg_count + 1); 2393 2394 RecordJSReturnSite(expr); 2395 RestoreContext(); 2396 context()->DropAndPlug(1, x0); 2397 } 2398 2399 void FullCodeGenerator::EmitResolvePossiblyDirectEval(Call* expr) { 2400 int arg_count = expr->arguments()->length(); 2401 ASM_LOCATION("FullCodeGenerator::EmitResolvePossiblyDirectEval"); 2402 // Prepare to push a copy of the first argument or undefined if it doesn't 2403 // exist. 2404 if (arg_count > 0) { 2405 __ Peek(x9, arg_count * kXRegSize); 2406 } else { 2407 __ LoadRoot(x9, Heap::kUndefinedValueRootIndex); 2408 } 2409 2410 __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 2411 2412 // Prepare to push the language mode. 2413 __ Mov(x11, Smi::FromInt(language_mode())); 2414 // Prepare to push the start position of the scope the calls resides in. 2415 __ Mov(x12, Smi::FromInt(scope()->start_position())); 2416 // Prepare to push the source position of the eval call. 2417 __ Mov(x13, Smi::FromInt(expr->position())); 2418 2419 // Push. 2420 __ Push(x9, x10, x11, x12, x13); 2421 2422 // Do the runtime call. 2423 __ CallRuntime(Runtime::kResolvePossiblyDirectEval); 2424 } 2425 2426 2427 // See http://www.ecma-international.org/ecma-262/6.0/#sec-function-calls. 2428 void FullCodeGenerator::PushCalleeAndWithBaseObject(Call* expr) { 2429 VariableProxy* callee = expr->expression()->AsVariableProxy(); 2430 if (callee->var()->IsLookupSlot()) { 2431 Label slow, done; 2432 SetExpressionPosition(callee); 2433 // Generate code for loading from variables potentially shadowed 2434 // by eval-introduced variables. 2435 EmitDynamicLookupFastCase(callee, NOT_INSIDE_TYPEOF, &slow, &done); 2436 2437 __ Bind(&slow); 2438 // Call the runtime to find the function to call (returned in x0) 2439 // and the object holding it (returned in x1). 2440 __ Push(callee->name()); 2441 __ CallRuntime(Runtime::kLoadLookupSlotForCall); 2442 PushOperands(x0, x1); // Receiver, function. 2443 PrepareForBailoutForId(expr->LookupId(), BailoutState::NO_REGISTERS); 2444 2445 // If fast case code has been generated, emit code to push the 2446 // function and receiver and have the slow path jump around this 2447 // code. 2448 if (done.is_linked()) { 2449 Label call; 2450 __ B(&call); 2451 __ Bind(&done); 2452 // Push function. 2453 // The receiver is implicitly the global receiver. Indicate this 2454 // by passing the undefined to the call function stub. 2455 __ LoadRoot(x1, Heap::kUndefinedValueRootIndex); 2456 __ Push(x0, x1); 2457 __ Bind(&call); 2458 } 2459 } else { 2460 VisitForStackValue(callee); 2461 // refEnv.WithBaseObject() 2462 __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); 2463 PushOperand(x10); // Reserved receiver slot. 2464 } 2465 } 2466 2467 2468 void FullCodeGenerator::EmitPossiblyEvalCall(Call* expr) { 2469 ASM_LOCATION("FullCodeGenerator::EmitPossiblyEvalCall"); 2470 // In a call to eval, we first call Runtime_ResolvePossiblyDirectEval 2471 // to resolve the function we need to call. Then we call the resolved 2472 // function using the given arguments. 2473 ZoneList<Expression*>* args = expr->arguments(); 2474 int arg_count = args->length(); 2475 2476 PushCalleeAndWithBaseObject(expr); 2477 2478 // Push the arguments. 2479 for (int i = 0; i < arg_count; i++) { 2480 VisitForStackValue(args->at(i)); 2481 } 2482 2483 // Push a copy of the function (found below the arguments) and 2484 // resolve eval. 2485 __ Peek(x10, (arg_count + 1) * kPointerSize); 2486 __ Push(x10); 2487 EmitResolvePossiblyDirectEval(expr); 2488 2489 // Touch up the stack with the resolved function. 2490 __ Poke(x0, (arg_count + 1) * kPointerSize); 2491 2492 PrepareForBailoutForId(expr->EvalId(), BailoutState::NO_REGISTERS); 2493 2494 // Record source position for debugger. 2495 SetCallPosition(expr); 2496 2497 // Call the evaluated function. 2498 __ Peek(x1, (arg_count + 1) * kXRegSize); 2499 __ Mov(x0, arg_count); 2500 __ Call(isolate()->builtins()->Call(ConvertReceiverMode::kAny, 2501 expr->tail_call_mode()), 2502 RelocInfo::CODE_TARGET); 2503 OperandStackDepthDecrement(arg_count + 1); 2504 RecordJSReturnSite(expr); 2505 RestoreContext(); 2506 context()->DropAndPlug(1, x0); 2507 } 2508 2509 2510 void FullCodeGenerator::VisitCallNew(CallNew* expr) { 2511 Comment cmnt(masm_, "[ CallNew"); 2512 // According to ECMA-262, section 11.2.2, page 44, the function 2513 // expression in new calls must be evaluated before the 2514 // arguments. 2515 2516 // Push constructor on the stack. If it's not a function it's used as 2517 // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is 2518 // ignored. 2519 DCHECK(!expr->expression()->IsSuperPropertyReference()); 2520 VisitForStackValue(expr->expression()); 2521 2522 // Push the arguments ("left-to-right") on the stack. 2523 ZoneList<Expression*>* args = expr->arguments(); 2524 int arg_count = args->length(); 2525 for (int i = 0; i < arg_count; i++) { 2526 VisitForStackValue(args->at(i)); 2527 } 2528 2529 // Call the construct call builtin that handles allocation and 2530 // constructor invocation. 2531 SetConstructCallPosition(expr); 2532 2533 // Load function and argument count into x1 and x0. 2534 __ Mov(x0, arg_count); 2535 __ Peek(x1, arg_count * kXRegSize); 2536 2537 // Record call targets in unoptimized code. 2538 __ EmitLoadTypeFeedbackVector(x2); 2539 __ Mov(x3, SmiFromSlot(expr->CallNewFeedbackSlot())); 2540 2541 CallConstructStub stub(isolate()); 2542 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET); 2543 OperandStackDepthDecrement(arg_count + 1); 2544 PrepareForBailoutForId(expr->ReturnId(), BailoutState::TOS_REGISTER); 2545 RestoreContext(); 2546 context()->Plug(x0); 2547 } 2548 2549 2550 void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { 2551 ASM_LOCATION("FullCodeGenerator::EmitSuperConstructorCall"); 2552 SuperCallReference* super_call_ref = 2553 expr->expression()->AsSuperCallReference(); 2554 DCHECK_NOT_NULL(super_call_ref); 2555 2556 // Push the super constructor target on the stack (may be null, 2557 // but the Construct builtin can deal with that properly). 2558 VisitForAccumulatorValue(super_call_ref->this_function_var()); 2559 __ AssertFunction(result_register()); 2560 __ Ldr(result_register(), 2561 FieldMemOperand(result_register(), HeapObject::kMapOffset)); 2562 __ Ldr(result_register(), 2563 FieldMemOperand(result_register(), Map::kPrototypeOffset)); 2564 PushOperand(result_register()); 2565 2566 // Push the arguments ("left-to-right") on the stack. 2567 ZoneList<Expression*>* args = expr->arguments(); 2568 int arg_count = args->length(); 2569 for (int i = 0; i < arg_count; i++) { 2570 VisitForStackValue(args->at(i)); 2571 } 2572 2573 // Call the construct call builtin that handles allocation and 2574 // constructor invocation. 2575 SetConstructCallPosition(expr); 2576 2577 // Load new target into x3. 2578 VisitForAccumulatorValue(super_call_ref->new_target_var()); 2579 __ Mov(x3, result_register()); 2580 2581 // Load function and argument count into x1 and x0. 2582 __ Mov(x0, arg_count); 2583 __ Peek(x1, arg_count * kXRegSize); 2584 2585 __ Call(isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); 2586 OperandStackDepthDecrement(arg_count + 1); 2587 2588 RecordJSReturnSite(expr); 2589 RestoreContext(); 2590 context()->Plug(x0); 2591 } 2592 2593 2594 void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { 2595 ZoneList<Expression*>* args = expr->arguments(); 2596 DCHECK(args->length() == 1); 2597 2598 VisitForAccumulatorValue(args->at(0)); 2599 2600 Label materialize_true, materialize_false; 2601 Label* if_true = NULL; 2602 Label* if_false = NULL; 2603 Label* fall_through = NULL; 2604 context()->PrepareTest(&materialize_true, &materialize_false, 2605 &if_true, &if_false, &fall_through); 2606 2607 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2608 __ TestAndSplit(x0, kSmiTagMask, if_true, if_false, fall_through); 2609 2610 context()->Plug(if_true, if_false); 2611 } 2612 2613 2614 void FullCodeGenerator::EmitIsJSReceiver(CallRuntime* expr) { 2615 ZoneList<Expression*>* args = expr->arguments(); 2616 DCHECK(args->length() == 1); 2617 2618 VisitForAccumulatorValue(args->at(0)); 2619 2620 Label materialize_true, materialize_false; 2621 Label* if_true = NULL; 2622 Label* if_false = NULL; 2623 Label* fall_through = NULL; 2624 context()->PrepareTest(&materialize_true, &materialize_false, 2625 &if_true, &if_false, &fall_through); 2626 2627 __ JumpIfSmi(x0, if_false); 2628 __ CompareObjectType(x0, x10, x11, FIRST_JS_RECEIVER_TYPE); 2629 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2630 Split(ge, if_true, if_false, fall_through); 2631 2632 context()->Plug(if_true, if_false); 2633 } 2634 2635 2636 void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { 2637 ZoneList<Expression*>* args = expr->arguments(); 2638 DCHECK(args->length() == 1); 2639 2640 VisitForAccumulatorValue(args->at(0)); 2641 2642 Label materialize_true, materialize_false; 2643 Label* if_true = NULL; 2644 Label* if_false = NULL; 2645 Label* fall_through = NULL; 2646 context()->PrepareTest(&materialize_true, &materialize_false, 2647 &if_true, &if_false, &fall_through); 2648 2649 __ JumpIfSmi(x0, if_false); 2650 __ CompareObjectType(x0, x10, x11, JS_ARRAY_TYPE); 2651 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2652 Split(eq, if_true, if_false, fall_through); 2653 2654 context()->Plug(if_true, if_false); 2655 } 2656 2657 2658 void FullCodeGenerator::EmitIsTypedArray(CallRuntime* expr) { 2659 ZoneList<Expression*>* args = expr->arguments(); 2660 DCHECK(args->length() == 1); 2661 2662 VisitForAccumulatorValue(args->at(0)); 2663 2664 Label materialize_true, materialize_false; 2665 Label* if_true = NULL; 2666 Label* if_false = NULL; 2667 Label* fall_through = NULL; 2668 context()->PrepareTest(&materialize_true, &materialize_false, &if_true, 2669 &if_false, &fall_through); 2670 2671 __ JumpIfSmi(x0, if_false); 2672 __ CompareObjectType(x0, x10, x11, JS_TYPED_ARRAY_TYPE); 2673 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2674 Split(eq, if_true, if_false, fall_through); 2675 2676 context()->Plug(if_true, if_false); 2677 } 2678 2679 2680 void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { 2681 ZoneList<Expression*>* args = expr->arguments(); 2682 DCHECK(args->length() == 1); 2683 2684 VisitForAccumulatorValue(args->at(0)); 2685 2686 Label materialize_true, materialize_false; 2687 Label* if_true = NULL; 2688 Label* if_false = NULL; 2689 Label* fall_through = NULL; 2690 context()->PrepareTest(&materialize_true, &materialize_false, 2691 &if_true, &if_false, &fall_through); 2692 2693 __ JumpIfSmi(x0, if_false); 2694 __ CompareObjectType(x0, x10, x11, JS_REGEXP_TYPE); 2695 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2696 Split(eq, if_true, if_false, fall_through); 2697 2698 context()->Plug(if_true, if_false); 2699 } 2700 2701 2702 void FullCodeGenerator::EmitIsJSProxy(CallRuntime* expr) { 2703 ZoneList<Expression*>* args = expr->arguments(); 2704 DCHECK(args->length() == 1); 2705 2706 VisitForAccumulatorValue(args->at(0)); 2707 2708 Label materialize_true, materialize_false; 2709 Label* if_true = NULL; 2710 Label* if_false = NULL; 2711 Label* fall_through = NULL; 2712 context()->PrepareTest(&materialize_true, &materialize_false, &if_true, 2713 &if_false, &fall_through); 2714 2715 __ JumpIfSmi(x0, if_false); 2716 __ CompareObjectType(x0, x10, x11, JS_PROXY_TYPE); 2717 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2718 Split(eq, if_true, if_false, fall_through); 2719 2720 context()->Plug(if_true, if_false); 2721 } 2722 2723 2724 void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { 2725 ASM_LOCATION("FullCodeGenerator::EmitClassOf"); 2726 ZoneList<Expression*>* args = expr->arguments(); 2727 DCHECK(args->length() == 1); 2728 Label done, null, function, non_function_constructor; 2729 2730 VisitForAccumulatorValue(args->at(0)); 2731 2732 // If the object is not a JSReceiver, we return null. 2733 __ JumpIfSmi(x0, &null); 2734 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 2735 __ CompareObjectType(x0, x10, x11, FIRST_JS_RECEIVER_TYPE); 2736 // x10: object's map. 2737 // x11: object's type. 2738 __ B(lt, &null); 2739 2740 // Return 'Function' for JSFunction objects. 2741 __ Cmp(x11, FIRST_FUNCTION_TYPE); 2742 STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE); 2743 __ B(hs, &function); 2744 2745 // Check if the constructor in the map is a JS function. 2746 Register instance_type = x14; 2747 __ GetMapConstructor(x12, x10, x13, instance_type); 2748 __ Cmp(instance_type, JS_FUNCTION_TYPE); 2749 __ B(ne, &non_function_constructor); 2750 2751 // x12 now contains the constructor function. Grab the 2752 // instance class name from there. 2753 __ Ldr(x13, FieldMemOperand(x12, JSFunction::kSharedFunctionInfoOffset)); 2754 __ Ldr(x0, 2755 FieldMemOperand(x13, SharedFunctionInfo::kInstanceClassNameOffset)); 2756 __ B(&done); 2757 2758 // Functions have class 'Function'. 2759 __ Bind(&function); 2760 __ LoadRoot(x0, Heap::kFunction_stringRootIndex); 2761 __ B(&done); 2762 2763 // Objects with a non-function constructor have class 'Object'. 2764 __ Bind(&non_function_constructor); 2765 __ LoadRoot(x0, Heap::kObject_stringRootIndex); 2766 __ B(&done); 2767 2768 // Non-JS objects have class null. 2769 __ Bind(&null); 2770 __ LoadRoot(x0, Heap::kNullValueRootIndex); 2771 2772 // All done. 2773 __ Bind(&done); 2774 2775 context()->Plug(x0); 2776 } 2777 2778 2779 void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { 2780 ASM_LOCATION("FullCodeGenerator::EmitValueOf"); 2781 ZoneList<Expression*>* args = expr->arguments(); 2782 DCHECK(args->length() == 1); 2783 VisitForAccumulatorValue(args->at(0)); // Load the object. 2784 2785 Label done; 2786 // If the object is a smi return the object. 2787 __ JumpIfSmi(x0, &done); 2788 // If the object is not a value type, return the object. 2789 __ JumpIfNotObjectType(x0, x10, x11, JS_VALUE_TYPE, &done); 2790 __ Ldr(x0, FieldMemOperand(x0, JSValue::kValueOffset)); 2791 2792 __ Bind(&done); 2793 context()->Plug(x0); 2794 } 2795 2796 2797 void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { 2798 ZoneList<Expression*>* args = expr->arguments(); 2799 DCHECK(args->length() == 1); 2800 2801 VisitForAccumulatorValue(args->at(0)); 2802 2803 Label done; 2804 Register code = x0; 2805 Register result = x1; 2806 2807 StringCharFromCodeGenerator generator(code, result); 2808 generator.GenerateFast(masm_); 2809 __ B(&done); 2810 2811 NopRuntimeCallHelper call_helper; 2812 generator.GenerateSlow(masm_, call_helper); 2813 2814 __ Bind(&done); 2815 context()->Plug(result); 2816 } 2817 2818 2819 void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { 2820 ZoneList<Expression*>* args = expr->arguments(); 2821 DCHECK(args->length() == 2); 2822 2823 VisitForStackValue(args->at(0)); 2824 VisitForAccumulatorValue(args->at(1)); 2825 2826 Register object = x1; 2827 Register index = x0; 2828 Register result = x3; 2829 2830 PopOperand(object); 2831 2832 Label need_conversion; 2833 Label index_out_of_range; 2834 Label done; 2835 StringCharCodeAtGenerator generator(object, index, result, &need_conversion, 2836 &need_conversion, &index_out_of_range); 2837 generator.GenerateFast(masm_); 2838 __ B(&done); 2839 2840 __ Bind(&index_out_of_range); 2841 // When the index is out of range, the spec requires us to return NaN. 2842 __ LoadRoot(result, Heap::kNanValueRootIndex); 2843 __ B(&done); 2844 2845 __ Bind(&need_conversion); 2846 // Load the undefined value into the result register, which will 2847 // trigger conversion. 2848 __ LoadRoot(result, Heap::kUndefinedValueRootIndex); 2849 __ B(&done); 2850 2851 NopRuntimeCallHelper call_helper; 2852 generator.GenerateSlow(masm_, NOT_PART_OF_IC_HANDLER, call_helper); 2853 2854 __ Bind(&done); 2855 context()->Plug(result); 2856 } 2857 2858 2859 void FullCodeGenerator::EmitCall(CallRuntime* expr) { 2860 ASM_LOCATION("FullCodeGenerator::EmitCall"); 2861 ZoneList<Expression*>* args = expr->arguments(); 2862 DCHECK_LE(2, args->length()); 2863 // Push target, receiver and arguments onto the stack. 2864 for (Expression* const arg : *args) { 2865 VisitForStackValue(arg); 2866 } 2867 PrepareForBailoutForId(expr->CallId(), BailoutState::NO_REGISTERS); 2868 // Move target to x1. 2869 int const argc = args->length() - 2; 2870 __ Peek(x1, (argc + 1) * kXRegSize); 2871 // Call the target. 2872 __ Mov(x0, argc); 2873 __ Call(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); 2874 OperandStackDepthDecrement(argc + 1); 2875 RestoreContext(); 2876 // Discard the function left on TOS. 2877 context()->DropAndPlug(1, x0); 2878 } 2879 2880 2881 void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { 2882 ZoneList<Expression*>* args = expr->arguments(); 2883 VisitForAccumulatorValue(args->at(0)); 2884 2885 Label materialize_true, materialize_false; 2886 Label* if_true = NULL; 2887 Label* if_false = NULL; 2888 Label* fall_through = NULL; 2889 context()->PrepareTest(&materialize_true, &materialize_false, 2890 &if_true, &if_false, &fall_through); 2891 2892 __ Ldr(x10, FieldMemOperand(x0, String::kHashFieldOffset)); 2893 __ Tst(x10, String::kContainsCachedArrayIndexMask); 2894 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 2895 Split(eq, if_true, if_false, fall_through); 2896 2897 context()->Plug(if_true, if_false); 2898 } 2899 2900 2901 void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { 2902 ZoneList<Expression*>* args = expr->arguments(); 2903 DCHECK(args->length() == 1); 2904 VisitForAccumulatorValue(args->at(0)); 2905 2906 __ AssertString(x0); 2907 2908 __ Ldr(x10, FieldMemOperand(x0, String::kHashFieldOffset)); 2909 __ IndexFromHash(x10, x0); 2910 2911 context()->Plug(x0); 2912 } 2913 2914 2915 void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) { 2916 ZoneList<Expression*>* args = expr->arguments(); 2917 DCHECK_EQ(1, args->length()); 2918 VisitForAccumulatorValue(args->at(0)); 2919 __ AssertFunction(x0); 2920 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 2921 __ Ldr(x0, FieldMemOperand(x0, Map::kPrototypeOffset)); 2922 context()->Plug(x0); 2923 } 2924 2925 void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) { 2926 DCHECK(expr->arguments()->length() == 0); 2927 ExternalReference debug_is_active = 2928 ExternalReference::debug_is_active_address(isolate()); 2929 __ Mov(x10, debug_is_active); 2930 __ Ldrb(x0, MemOperand(x10)); 2931 __ SmiTag(x0); 2932 context()->Plug(x0); 2933 } 2934 2935 2936 void FullCodeGenerator::EmitCreateIterResultObject(CallRuntime* expr) { 2937 ZoneList<Expression*>* args = expr->arguments(); 2938 DCHECK_EQ(2, args->length()); 2939 VisitForStackValue(args->at(0)); 2940 VisitForStackValue(args->at(1)); 2941 2942 Label runtime, done; 2943 2944 Register result = x0; 2945 __ Allocate(JSIteratorResult::kSize, result, x10, x11, &runtime, 2946 NO_ALLOCATION_FLAGS); 2947 Register map_reg = x1; 2948 Register result_value = x2; 2949 Register boolean_done = x3; 2950 Register empty_fixed_array = x4; 2951 Register untagged_result = x5; 2952 __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, map_reg); 2953 __ Pop(boolean_done); 2954 __ Pop(result_value); 2955 __ LoadRoot(empty_fixed_array, Heap::kEmptyFixedArrayRootIndex); 2956 STATIC_ASSERT(JSObject::kPropertiesOffset + kPointerSize == 2957 JSObject::kElementsOffset); 2958 STATIC_ASSERT(JSIteratorResult::kValueOffset + kPointerSize == 2959 JSIteratorResult::kDoneOffset); 2960 __ ObjectUntag(untagged_result, result); 2961 __ Str(map_reg, MemOperand(untagged_result, HeapObject::kMapOffset)); 2962 __ Stp(empty_fixed_array, empty_fixed_array, 2963 MemOperand(untagged_result, JSObject::kPropertiesOffset)); 2964 __ Stp(result_value, boolean_done, 2965 MemOperand(untagged_result, JSIteratorResult::kValueOffset)); 2966 STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); 2967 __ B(&done); 2968 2969 __ Bind(&runtime); 2970 CallRuntimeWithOperands(Runtime::kCreateIterResultObject); 2971 2972 __ Bind(&done); 2973 context()->Plug(x0); 2974 } 2975 2976 2977 void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) { 2978 // Push function. 2979 __ LoadNativeContextSlot(expr->context_index(), x0); 2980 PushOperand(x0); 2981 2982 // Push undefined as the receiver. 2983 __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); 2984 PushOperand(x0); 2985 } 2986 2987 2988 void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) { 2989 ZoneList<Expression*>* args = expr->arguments(); 2990 int arg_count = args->length(); 2991 2992 SetCallPosition(expr); 2993 __ Peek(x1, (arg_count + 1) * kPointerSize); 2994 __ Mov(x0, arg_count); 2995 __ Call(isolate()->builtins()->Call(ConvertReceiverMode::kNullOrUndefined), 2996 RelocInfo::CODE_TARGET); 2997 OperandStackDepthDecrement(arg_count + 1); 2998 RestoreContext(); 2999 } 3000 3001 3002 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 3003 switch (expr->op()) { 3004 case Token::DELETE: { 3005 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); 3006 Property* property = expr->expression()->AsProperty(); 3007 VariableProxy* proxy = expr->expression()->AsVariableProxy(); 3008 3009 if (property != NULL) { 3010 VisitForStackValue(property->obj()); 3011 VisitForStackValue(property->key()); 3012 CallRuntimeWithOperands(is_strict(language_mode()) 3013 ? Runtime::kDeleteProperty_Strict 3014 : Runtime::kDeleteProperty_Sloppy); 3015 context()->Plug(x0); 3016 } else if (proxy != NULL) { 3017 Variable* var = proxy->var(); 3018 // Delete of an unqualified identifier is disallowed in strict mode but 3019 // "delete this" is allowed. 3020 bool is_this = var->HasThisName(isolate()); 3021 DCHECK(is_sloppy(language_mode()) || is_this); 3022 if (var->IsUnallocatedOrGlobalSlot()) { 3023 __ LoadGlobalObject(x12); 3024 __ Mov(x11, Operand(var->name())); 3025 __ Push(x12, x11); 3026 __ CallRuntime(Runtime::kDeleteProperty_Sloppy); 3027 context()->Plug(x0); 3028 } else if (var->IsStackAllocated() || var->IsContextSlot()) { 3029 // Result of deleting non-global, non-dynamic variables is false. 3030 // The subexpression does not have side effects. 3031 context()->Plug(is_this); 3032 } else { 3033 // Non-global variable. Call the runtime to try to delete from the 3034 // context where the variable was introduced. 3035 __ Push(var->name()); 3036 __ CallRuntime(Runtime::kDeleteLookupSlot); 3037 context()->Plug(x0); 3038 } 3039 } else { 3040 // Result of deleting non-property, non-variable reference is true. 3041 // The subexpression may have side effects. 3042 VisitForEffect(expr->expression()); 3043 context()->Plug(true); 3044 } 3045 break; 3046 break; 3047 } 3048 case Token::VOID: { 3049 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); 3050 VisitForEffect(expr->expression()); 3051 context()->Plug(Heap::kUndefinedValueRootIndex); 3052 break; 3053 } 3054 case Token::NOT: { 3055 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); 3056 if (context()->IsEffect()) { 3057 // Unary NOT has no side effects so it's only necessary to visit the 3058 // subexpression. Match the optimizing compiler by not branching. 3059 VisitForEffect(expr->expression()); 3060 } else if (context()->IsTest()) { 3061 const TestContext* test = TestContext::cast(context()); 3062 // The labels are swapped for the recursive call. 3063 VisitForControl(expr->expression(), 3064 test->false_label(), 3065 test->true_label(), 3066 test->fall_through()); 3067 context()->Plug(test->true_label(), test->false_label()); 3068 } else { 3069 DCHECK(context()->IsAccumulatorValue() || context()->IsStackValue()); 3070 // TODO(jbramley): This could be much more efficient using (for 3071 // example) the CSEL instruction. 3072 Label materialize_true, materialize_false, done; 3073 VisitForControl(expr->expression(), 3074 &materialize_false, 3075 &materialize_true, 3076 &materialize_true); 3077 if (!context()->IsAccumulatorValue()) OperandStackDepthIncrement(1); 3078 3079 __ Bind(&materialize_true); 3080 PrepareForBailoutForId(expr->MaterializeTrueId(), 3081 BailoutState::NO_REGISTERS); 3082 __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); 3083 __ B(&done); 3084 3085 __ Bind(&materialize_false); 3086 PrepareForBailoutForId(expr->MaterializeFalseId(), 3087 BailoutState::NO_REGISTERS); 3088 __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); 3089 __ B(&done); 3090 3091 __ Bind(&done); 3092 if (context()->IsStackValue()) { 3093 __ Push(result_register()); 3094 } 3095 } 3096 break; 3097 } 3098 case Token::TYPEOF: { 3099 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); 3100 { 3101 AccumulatorValueContext context(this); 3102 VisitForTypeofValue(expr->expression()); 3103 } 3104 __ Mov(x3, x0); 3105 TypeofStub typeof_stub(isolate()); 3106 __ CallStub(&typeof_stub); 3107 context()->Plug(x0); 3108 break; 3109 } 3110 default: 3111 UNREACHABLE(); 3112 } 3113 } 3114 3115 3116 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { 3117 DCHECK(expr->expression()->IsValidReferenceExpressionOrThis()); 3118 3119 Comment cmnt(masm_, "[ CountOperation"); 3120 3121 Property* prop = expr->expression()->AsProperty(); 3122 LhsKind assign_type = Property::GetAssignType(prop); 3123 3124 // Evaluate expression and get value. 3125 if (assign_type == VARIABLE) { 3126 DCHECK(expr->expression()->AsVariableProxy()->var() != NULL); 3127 AccumulatorValueContext context(this); 3128 EmitVariableLoad(expr->expression()->AsVariableProxy()); 3129 } else { 3130 // Reserve space for result of postfix operation. 3131 if (expr->is_postfix() && !context()->IsEffect()) { 3132 PushOperand(xzr); 3133 } 3134 switch (assign_type) { 3135 case NAMED_PROPERTY: { 3136 // Put the object both on the stack and in the register. 3137 VisitForStackValue(prop->obj()); 3138 __ Peek(LoadDescriptor::ReceiverRegister(), 0); 3139 EmitNamedPropertyLoad(prop); 3140 break; 3141 } 3142 3143 case NAMED_SUPER_PROPERTY: { 3144 VisitForStackValue(prop->obj()->AsSuperPropertyReference()->this_var()); 3145 VisitForAccumulatorValue( 3146 prop->obj()->AsSuperPropertyReference()->home_object()); 3147 PushOperand(result_register()); 3148 const Register scratch = x10; 3149 __ Peek(scratch, kPointerSize); 3150 PushOperands(scratch, result_register()); 3151 EmitNamedSuperPropertyLoad(prop); 3152 break; 3153 } 3154 3155 case KEYED_SUPER_PROPERTY: { 3156 VisitForStackValue(prop->obj()->AsSuperPropertyReference()->this_var()); 3157 VisitForStackValue( 3158 prop->obj()->AsSuperPropertyReference()->home_object()); 3159 VisitForAccumulatorValue(prop->key()); 3160 PushOperand(result_register()); 3161 const Register scratch1 = x10; 3162 const Register scratch2 = x11; 3163 __ Peek(scratch1, 2 * kPointerSize); 3164 __ Peek(scratch2, kPointerSize); 3165 PushOperands(scratch1, scratch2, result_register()); 3166 EmitKeyedSuperPropertyLoad(prop); 3167 break; 3168 } 3169 3170 case KEYED_PROPERTY: { 3171 VisitForStackValue(prop->obj()); 3172 VisitForStackValue(prop->key()); 3173 __ Peek(LoadDescriptor::ReceiverRegister(), 1 * kPointerSize); 3174 __ Peek(LoadDescriptor::NameRegister(), 0); 3175 EmitKeyedPropertyLoad(prop); 3176 break; 3177 } 3178 3179 case VARIABLE: 3180 UNREACHABLE(); 3181 } 3182 } 3183 3184 // We need a second deoptimization point after loading the value 3185 // in case evaluating the property load my have a side effect. 3186 if (assign_type == VARIABLE) { 3187 PrepareForBailout(expr->expression(), BailoutState::TOS_REGISTER); 3188 } else { 3189 PrepareForBailoutForId(prop->LoadId(), BailoutState::TOS_REGISTER); 3190 } 3191 3192 // Inline smi case if we are in a loop. 3193 Label stub_call, done; 3194 JumpPatchSite patch_site(masm_); 3195 3196 int count_value = expr->op() == Token::INC ? 1 : -1; 3197 if (ShouldInlineSmiCase(expr->op())) { 3198 Label slow; 3199 patch_site.EmitJumpIfNotSmi(x0, &slow); 3200 3201 // Save result for postfix expressions. 3202 if (expr->is_postfix()) { 3203 if (!context()->IsEffect()) { 3204 // Save the result on the stack. If we have a named or keyed property we 3205 // store the result under the receiver that is currently on top of the 3206 // stack. 3207 switch (assign_type) { 3208 case VARIABLE: 3209 __ Push(x0); 3210 break; 3211 case NAMED_PROPERTY: 3212 __ Poke(x0, kPointerSize); 3213 break; 3214 case NAMED_SUPER_PROPERTY: 3215 __ Poke(x0, kPointerSize * 2); 3216 break; 3217 case KEYED_PROPERTY: 3218 __ Poke(x0, kPointerSize * 2); 3219 break; 3220 case KEYED_SUPER_PROPERTY: 3221 __ Poke(x0, kPointerSize * 3); 3222 break; 3223 } 3224 } 3225 } 3226 3227 __ Adds(x0, x0, Smi::FromInt(count_value)); 3228 __ B(vc, &done); 3229 // Call stub. Undo operation first. 3230 __ Sub(x0, x0, Smi::FromInt(count_value)); 3231 __ B(&stub_call); 3232 __ Bind(&slow); 3233 } 3234 3235 // Convert old value into a number. 3236 __ Call(isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); 3237 PrepareForBailoutForId(expr->ToNumberId(), BailoutState::TOS_REGISTER); 3238 3239 // Save result for postfix expressions. 3240 if (expr->is_postfix()) { 3241 if (!context()->IsEffect()) { 3242 // Save the result on the stack. If we have a named or keyed property 3243 // we store the result under the receiver that is currently on top 3244 // of the stack. 3245 switch (assign_type) { 3246 case VARIABLE: 3247 PushOperand(x0); 3248 break; 3249 case NAMED_PROPERTY: 3250 __ Poke(x0, kXRegSize); 3251 break; 3252 case NAMED_SUPER_PROPERTY: 3253 __ Poke(x0, 2 * kXRegSize); 3254 break; 3255 case KEYED_PROPERTY: 3256 __ Poke(x0, 2 * kXRegSize); 3257 break; 3258 case KEYED_SUPER_PROPERTY: 3259 __ Poke(x0, 3 * kXRegSize); 3260 break; 3261 } 3262 } 3263 } 3264 3265 __ Bind(&stub_call); 3266 __ Mov(x1, x0); 3267 __ Mov(x0, Smi::FromInt(count_value)); 3268 3269 SetExpressionPosition(expr); 3270 3271 { 3272 Assembler::BlockPoolsScope scope(masm_); 3273 Handle<Code> code = CodeFactory::BinaryOpIC(isolate(), Token::ADD).code(); 3274 CallIC(code, expr->CountBinOpFeedbackId()); 3275 patch_site.EmitPatchInfo(); 3276 } 3277 __ Bind(&done); 3278 3279 // Store the value returned in x0. 3280 switch (assign_type) { 3281 case VARIABLE: 3282 if (expr->is_postfix()) { 3283 { EffectContext context(this); 3284 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), 3285 Token::ASSIGN, expr->CountSlot()); 3286 PrepareForBailoutForId(expr->AssignmentId(), 3287 BailoutState::TOS_REGISTER); 3288 context.Plug(x0); 3289 } 3290 // For all contexts except EffectConstant We have the result on 3291 // top of the stack. 3292 if (!context()->IsEffect()) { 3293 context()->PlugTOS(); 3294 } 3295 } else { 3296 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), 3297 Token::ASSIGN, expr->CountSlot()); 3298 PrepareForBailoutForId(expr->AssignmentId(), 3299 BailoutState::TOS_REGISTER); 3300 context()->Plug(x0); 3301 } 3302 break; 3303 case NAMED_PROPERTY: { 3304 __ Mov(StoreDescriptor::NameRegister(), 3305 Operand(prop->key()->AsLiteral()->value())); 3306 PopOperand(StoreDescriptor::ReceiverRegister()); 3307 EmitLoadStoreICSlot(expr->CountSlot()); 3308 CallStoreIC(); 3309 PrepareForBailoutForId(expr->AssignmentId(), BailoutState::TOS_REGISTER); 3310 if (expr->is_postfix()) { 3311 if (!context()->IsEffect()) { 3312 context()->PlugTOS(); 3313 } 3314 } else { 3315 context()->Plug(x0); 3316 } 3317 break; 3318 } 3319 case NAMED_SUPER_PROPERTY: { 3320 EmitNamedSuperPropertyStore(prop); 3321 if (expr->is_postfix()) { 3322 if (!context()->IsEffect()) { 3323 context()->PlugTOS(); 3324 } 3325 } else { 3326 context()->Plug(x0); 3327 } 3328 break; 3329 } 3330 case KEYED_SUPER_PROPERTY: { 3331 EmitKeyedSuperPropertyStore(prop); 3332 if (expr->is_postfix()) { 3333 if (!context()->IsEffect()) { 3334 context()->PlugTOS(); 3335 } 3336 } else { 3337 context()->Plug(x0); 3338 } 3339 break; 3340 } 3341 case KEYED_PROPERTY: { 3342 PopOperand(StoreDescriptor::NameRegister()); 3343 PopOperand(StoreDescriptor::ReceiverRegister()); 3344 Handle<Code> ic = 3345 CodeFactory::KeyedStoreIC(isolate(), language_mode()).code(); 3346 EmitLoadStoreICSlot(expr->CountSlot()); 3347 CallIC(ic); 3348 PrepareForBailoutForId(expr->AssignmentId(), BailoutState::TOS_REGISTER); 3349 if (expr->is_postfix()) { 3350 if (!context()->IsEffect()) { 3351 context()->PlugTOS(); 3352 } 3353 } else { 3354 context()->Plug(x0); 3355 } 3356 break; 3357 } 3358 } 3359 } 3360 3361 3362 void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, 3363 Expression* sub_expr, 3364 Handle<String> check) { 3365 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof"); 3366 Comment cmnt(masm_, "[ EmitLiteralCompareTypeof"); 3367 Label materialize_true, materialize_false; 3368 Label* if_true = NULL; 3369 Label* if_false = NULL; 3370 Label* fall_through = NULL; 3371 context()->PrepareTest(&materialize_true, &materialize_false, 3372 &if_true, &if_false, &fall_through); 3373 3374 { AccumulatorValueContext context(this); 3375 VisitForTypeofValue(sub_expr); 3376 } 3377 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 3378 3379 Factory* factory = isolate()->factory(); 3380 if (String::Equals(check, factory->number_string())) { 3381 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof number_string"); 3382 __ JumpIfSmi(x0, if_true); 3383 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 3384 __ CompareRoot(x0, Heap::kHeapNumberMapRootIndex); 3385 Split(eq, if_true, if_false, fall_through); 3386 } else if (String::Equals(check, factory->string_string())) { 3387 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof string_string"); 3388 __ JumpIfSmi(x0, if_false); 3389 __ CompareObjectType(x0, x0, x1, FIRST_NONSTRING_TYPE); 3390 Split(lt, if_true, if_false, fall_through); 3391 } else if (String::Equals(check, factory->symbol_string())) { 3392 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof symbol_string"); 3393 __ JumpIfSmi(x0, if_false); 3394 __ CompareObjectType(x0, x0, x1, SYMBOL_TYPE); 3395 Split(eq, if_true, if_false, fall_through); 3396 } else if (String::Equals(check, factory->boolean_string())) { 3397 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof boolean_string"); 3398 __ JumpIfRoot(x0, Heap::kTrueValueRootIndex, if_true); 3399 __ CompareRoot(x0, Heap::kFalseValueRootIndex); 3400 Split(eq, if_true, if_false, fall_through); 3401 } else if (String::Equals(check, factory->undefined_string())) { 3402 ASM_LOCATION( 3403 "FullCodeGenerator::EmitLiteralCompareTypeof undefined_string"); 3404 __ JumpIfRoot(x0, Heap::kNullValueRootIndex, if_false); 3405 __ JumpIfSmi(x0, if_false); 3406 // Check for undetectable objects => true. 3407 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 3408 __ Ldrb(x1, FieldMemOperand(x0, Map::kBitFieldOffset)); 3409 __ TestAndSplit(x1, 1 << Map::kIsUndetectable, if_false, if_true, 3410 fall_through); 3411 } else if (String::Equals(check, factory->function_string())) { 3412 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof function_string"); 3413 __ JumpIfSmi(x0, if_false); 3414 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 3415 __ Ldrb(x1, FieldMemOperand(x0, Map::kBitFieldOffset)); 3416 __ And(x1, x1, (1 << Map::kIsCallable) | (1 << Map::kIsUndetectable)); 3417 __ CompareAndSplit(x1, Operand(1 << Map::kIsCallable), eq, if_true, 3418 if_false, fall_through); 3419 } else if (String::Equals(check, factory->object_string())) { 3420 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof object_string"); 3421 __ JumpIfSmi(x0, if_false); 3422 __ JumpIfRoot(x0, Heap::kNullValueRootIndex, if_true); 3423 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 3424 __ JumpIfObjectType(x0, x10, x11, FIRST_JS_RECEIVER_TYPE, if_false, lt); 3425 // Check for callable or undetectable objects => false. 3426 __ Ldrb(x10, FieldMemOperand(x10, Map::kBitFieldOffset)); 3427 __ TestAndSplit(x10, (1 << Map::kIsCallable) | (1 << Map::kIsUndetectable), 3428 if_true, if_false, fall_through); 3429 // clang-format off 3430 #define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ 3431 } else if (String::Equals(check, factory->type##_string())) { \ 3432 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof " \ 3433 #type "_string"); \ 3434 __ JumpIfSmi(x0, if_true); \ 3435 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); \ 3436 __ CompareRoot(x0, Heap::k##Type##MapRootIndex); \ 3437 Split(eq, if_true, if_false, fall_through); 3438 SIMD128_TYPES(SIMD128_TYPE) 3439 #undef SIMD128_TYPE 3440 // clang-format on 3441 } else { 3442 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof other"); 3443 if (if_false != fall_through) __ B(if_false); 3444 } 3445 context()->Plug(if_true, if_false); 3446 } 3447 3448 3449 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { 3450 Comment cmnt(masm_, "[ CompareOperation"); 3451 3452 // Try to generate an optimized comparison with a literal value. 3453 // TODO(jbramley): This only checks common values like NaN or undefined. 3454 // Should it also handle ARM64 immediate operands? 3455 if (TryLiteralCompare(expr)) { 3456 return; 3457 } 3458 3459 // Assign labels according to context()->PrepareTest. 3460 Label materialize_true; 3461 Label materialize_false; 3462 Label* if_true = NULL; 3463 Label* if_false = NULL; 3464 Label* fall_through = NULL; 3465 context()->PrepareTest(&materialize_true, &materialize_false, 3466 &if_true, &if_false, &fall_through); 3467 3468 Token::Value op = expr->op(); 3469 VisitForStackValue(expr->left()); 3470 switch (op) { 3471 case Token::IN: 3472 VisitForStackValue(expr->right()); 3473 SetExpressionPosition(expr); 3474 EmitHasProperty(); 3475 PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); 3476 __ CompareRoot(x0, Heap::kTrueValueRootIndex); 3477 Split(eq, if_true, if_false, fall_through); 3478 break; 3479 3480 case Token::INSTANCEOF: { 3481 VisitForAccumulatorValue(expr->right()); 3482 SetExpressionPosition(expr); 3483 PopOperand(x1); 3484 InstanceOfStub stub(isolate()); 3485 __ CallStub(&stub); 3486 PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); 3487 __ CompareRoot(x0, Heap::kTrueValueRootIndex); 3488 Split(eq, if_true, if_false, fall_through); 3489 break; 3490 } 3491 3492 default: { 3493 VisitForAccumulatorValue(expr->right()); 3494 SetExpressionPosition(expr); 3495 Condition cond = CompareIC::ComputeCondition(op); 3496 3497 // Pop the stack value. 3498 PopOperand(x1); 3499 3500 JumpPatchSite patch_site(masm_); 3501 if (ShouldInlineSmiCase(op)) { 3502 Label slow_case; 3503 patch_site.EmitJumpIfEitherNotSmi(x0, x1, &slow_case); 3504 __ Cmp(x1, x0); 3505 Split(cond, if_true, if_false, NULL); 3506 __ Bind(&slow_case); 3507 } 3508 3509 Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code(); 3510 CallIC(ic, expr->CompareOperationFeedbackId()); 3511 patch_site.EmitPatchInfo(); 3512 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 3513 __ CompareAndSplit(x0, 0, cond, if_true, if_false, fall_through); 3514 } 3515 } 3516 3517 // Convert the result of the comparison into one expected for this 3518 // expression's context. 3519 context()->Plug(if_true, if_false); 3520 } 3521 3522 3523 void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, 3524 Expression* sub_expr, 3525 NilValue nil) { 3526 ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareNil"); 3527 Label materialize_true, materialize_false; 3528 Label* if_true = NULL; 3529 Label* if_false = NULL; 3530 Label* fall_through = NULL; 3531 context()->PrepareTest(&materialize_true, &materialize_false, 3532 &if_true, &if_false, &fall_through); 3533 3534 VisitForAccumulatorValue(sub_expr); 3535 PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); 3536 3537 if (expr->op() == Token::EQ_STRICT) { 3538 Heap::RootListIndex nil_value = nil == kNullValue ? 3539 Heap::kNullValueRootIndex : 3540 Heap::kUndefinedValueRootIndex; 3541 __ CompareRoot(x0, nil_value); 3542 Split(eq, if_true, if_false, fall_through); 3543 } else { 3544 __ JumpIfSmi(x0, if_false); 3545 __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); 3546 __ Ldrb(x1, FieldMemOperand(x0, Map::kBitFieldOffset)); 3547 __ TestAndSplit(x1, 1 << Map::kIsUndetectable, if_false, if_true, 3548 fall_through); 3549 } 3550 3551 context()->Plug(if_true, if_false); 3552 } 3553 3554 3555 void FullCodeGenerator::VisitYield(Yield* expr) { 3556 Comment cmnt(masm_, "[ Yield"); 3557 SetExpressionPosition(expr); 3558 3559 // Evaluate yielded value first; the initial iterator definition depends on 3560 // this. It stays on the stack while we update the iterator. 3561 VisitForStackValue(expr->expression()); 3562 3563 // TODO(jbramley): Tidy this up once the merge is done, using named registers 3564 // and suchlike. The implementation changes a little by bleeding_edge so I 3565 // don't want to spend too much time on it now. 3566 3567 Label suspend, continuation, post_runtime, resume, exception; 3568 3569 __ B(&suspend); 3570 // TODO(jbramley): This label is bound here because the following code 3571 // looks at its pos(). Is it possible to do something more efficient here, 3572 // perhaps using Adr? 3573 __ Bind(&continuation); 3574 // When we arrive here, x0 holds the generator object. 3575 __ RecordGeneratorContinuation(); 3576 __ Ldr(x1, FieldMemOperand(x0, JSGeneratorObject::kResumeModeOffset)); 3577 __ Ldr(x0, FieldMemOperand(x0, JSGeneratorObject::kInputOrDebugPosOffset)); 3578 STATIC_ASSERT(JSGeneratorObject::kNext < JSGeneratorObject::kReturn); 3579 STATIC_ASSERT(JSGeneratorObject::kThrow > JSGeneratorObject::kReturn); 3580 __ Cmp(x1, Operand(Smi::FromInt(JSGeneratorObject::kReturn))); 3581 __ B(lt, &resume); 3582 __ Push(result_register()); 3583 __ B(gt, &exception); 3584 EmitCreateIteratorResult(true); 3585 EmitUnwindAndReturn(); 3586 3587 __ Bind(&exception); 3588 __ CallRuntime(Runtime::kThrow); 3589 3590 __ Bind(&suspend); 3591 OperandStackDepthIncrement(1); // Not popped on this path. 3592 VisitForAccumulatorValue(expr->generator_object()); 3593 DCHECK((continuation.pos() > 0) && Smi::IsValid(continuation.pos())); 3594 __ Mov(x1, Smi::FromInt(continuation.pos())); 3595 __ Str(x1, FieldMemOperand(x0, JSGeneratorObject::kContinuationOffset)); 3596 __ Str(cp, FieldMemOperand(x0, JSGeneratorObject::kContextOffset)); 3597 __ Mov(x1, cp); 3598 __ RecordWriteField(x0, JSGeneratorObject::kContextOffset, x1, x2, 3599 kLRHasBeenSaved, kDontSaveFPRegs); 3600 __ Add(x1, fp, StandardFrameConstants::kExpressionsOffset); 3601 __ Cmp(__ StackPointer(), x1); 3602 __ B(eq, &post_runtime); 3603 __ Push(x0); // generator object 3604 __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1); 3605 RestoreContext(); 3606 __ Bind(&post_runtime); 3607 PopOperand(result_register()); 3608 EmitReturnSequence(); 3609 3610 __ Bind(&resume); 3611 context()->Plug(result_register()); 3612 } 3613 3614 void FullCodeGenerator::PushOperands(Register reg1, Register reg2) { 3615 OperandStackDepthIncrement(2); 3616 __ Push(reg1, reg2); 3617 } 3618 3619 void FullCodeGenerator::PushOperands(Register reg1, Register reg2, 3620 Register reg3) { 3621 OperandStackDepthIncrement(3); 3622 __ Push(reg1, reg2, reg3); 3623 } 3624 3625 void FullCodeGenerator::PopOperands(Register reg1, Register reg2) { 3626 OperandStackDepthDecrement(2); 3627 __ Pop(reg1, reg2); 3628 } 3629 3630 void FullCodeGenerator::EmitOperandStackDepthCheck() { 3631 if (FLAG_debug_code) { 3632 int expected_diff = StandardFrameConstants::kFixedFrameSizeFromFp + 3633 operand_stack_depth_ * kPointerSize; 3634 __ Sub(x0, fp, jssp); 3635 __ Cmp(x0, Operand(expected_diff)); 3636 __ Assert(eq, kUnexpectedStackDepth); 3637 } 3638 } 3639 3640 void FullCodeGenerator::EmitCreateIteratorResult(bool done) { 3641 Label allocate, done_allocate; 3642 3643 // Allocate and populate an object with this form: { value: VAL, done: DONE } 3644 3645 Register result = x0; 3646 __ Allocate(JSIteratorResult::kSize, result, x10, x11, &allocate, 3647 NO_ALLOCATION_FLAGS); 3648 __ B(&done_allocate); 3649 3650 __ Bind(&allocate); 3651 __ Push(Smi::FromInt(JSIteratorResult::kSize)); 3652 __ CallRuntime(Runtime::kAllocateInNewSpace); 3653 3654 __ Bind(&done_allocate); 3655 Register map_reg = x1; 3656 Register result_value = x2; 3657 Register boolean_done = x3; 3658 Register empty_fixed_array = x4; 3659 Register untagged_result = x5; 3660 __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, map_reg); 3661 PopOperand(result_value); 3662 __ LoadRoot(boolean_done, 3663 done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); 3664 __ LoadRoot(empty_fixed_array, Heap::kEmptyFixedArrayRootIndex); 3665 STATIC_ASSERT(JSObject::kPropertiesOffset + kPointerSize == 3666 JSObject::kElementsOffset); 3667 STATIC_ASSERT(JSIteratorResult::kValueOffset + kPointerSize == 3668 JSIteratorResult::kDoneOffset); 3669 __ ObjectUntag(untagged_result, result); 3670 __ Str(map_reg, MemOperand(untagged_result, HeapObject::kMapOffset)); 3671 __ Stp(empty_fixed_array, empty_fixed_array, 3672 MemOperand(untagged_result, JSObject::kPropertiesOffset)); 3673 __ Stp(result_value, boolean_done, 3674 MemOperand(untagged_result, JSIteratorResult::kValueOffset)); 3675 STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); 3676 } 3677 3678 3679 // TODO(all): I don't like this method. 3680 // It seems to me that in too many places x0 is used in place of this. 3681 // Also, this function is not suitable for all places where x0 should be 3682 // abstracted (eg. when used as an argument). But some places assume that the 3683 // first argument register is x0, and use this function instead. 3684 // Considering that most of the register allocation is hard-coded in the 3685 // FullCodeGen, that it is unlikely we will need to change it extensively, and 3686 // that abstracting the allocation through functions would not yield any 3687 // performance benefit, I think the existence of this function is debatable. 3688 Register FullCodeGenerator::result_register() { 3689 return x0; 3690 } 3691 3692 3693 Register FullCodeGenerator::context_register() { 3694 return cp; 3695 } 3696 3697 void FullCodeGenerator::LoadFromFrameField(int frame_offset, Register value) { 3698 DCHECK(POINTER_SIZE_ALIGN(frame_offset) == frame_offset); 3699 __ Ldr(value, MemOperand(fp, frame_offset)); 3700 } 3701 3702 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { 3703 DCHECK(POINTER_SIZE_ALIGN(frame_offset) == frame_offset); 3704 __ Str(value, MemOperand(fp, frame_offset)); 3705 } 3706 3707 3708 void FullCodeGenerator::LoadContextField(Register dst, int context_index) { 3709 __ Ldr(dst, ContextMemOperand(cp, context_index)); 3710 } 3711 3712 3713 void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { 3714 Scope* closure_scope = scope()->ClosureScope(); 3715 if (closure_scope->is_script_scope() || 3716 closure_scope->is_module_scope()) { 3717 // Contexts nested in the native context have a canonical empty function 3718 // as their closure, not the anonymous closure containing the global 3719 // code. 3720 DCHECK(kSmiTag == 0); 3721 __ LoadNativeContextSlot(Context::CLOSURE_INDEX, x10); 3722 } else if (closure_scope->is_eval_scope()) { 3723 // Contexts created by a call to eval have the same closure as the 3724 // context calling eval, not the anonymous closure containing the eval 3725 // code. Fetch it from the context. 3726 __ Ldr(x10, ContextMemOperand(cp, Context::CLOSURE_INDEX)); 3727 } else { 3728 DCHECK(closure_scope->is_function_scope()); 3729 __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 3730 } 3731 PushOperand(x10); 3732 } 3733 3734 3735 void FullCodeGenerator::EnterFinallyBlock() { 3736 ASM_LOCATION("FullCodeGenerator::EnterFinallyBlock"); 3737 DCHECK(!result_register().is(x10)); 3738 // Store pending message while executing finally block. 3739 ExternalReference pending_message_obj = 3740 ExternalReference::address_of_pending_message_obj(isolate()); 3741 __ Mov(x10, pending_message_obj); 3742 __ Ldr(x10, MemOperand(x10)); 3743 PushOperand(x10); 3744 3745 ClearPendingMessage(); 3746 } 3747 3748 3749 void FullCodeGenerator::ExitFinallyBlock() { 3750 ASM_LOCATION("FullCodeGenerator::ExitFinallyBlock"); 3751 DCHECK(!result_register().is(x10)); 3752 3753 // Restore pending message from stack. 3754 PopOperand(x10); 3755 ExternalReference pending_message_obj = 3756 ExternalReference::address_of_pending_message_obj(isolate()); 3757 __ Mov(x13, pending_message_obj); 3758 __ Str(x10, MemOperand(x13)); 3759 } 3760 3761 3762 void FullCodeGenerator::ClearPendingMessage() { 3763 DCHECK(!result_register().is(x10)); 3764 ExternalReference pending_message_obj = 3765 ExternalReference::address_of_pending_message_obj(isolate()); 3766 __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); 3767 __ Mov(x13, pending_message_obj); 3768 __ Str(x10, MemOperand(x13)); 3769 } 3770 3771 3772 void FullCodeGenerator::DeferredCommands::EmitCommands() { 3773 __ Pop(result_register(), x1); // Restore the accumulator and get the token. 3774 for (DeferredCommand cmd : commands_) { 3775 Label skip; 3776 __ Cmp(x1, Operand(Smi::FromInt(cmd.token))); 3777 __ B(ne, &skip); 3778 switch (cmd.command) { 3779 case kReturn: 3780 codegen_->EmitUnwindAndReturn(); 3781 break; 3782 case kThrow: 3783 __ Push(result_register()); 3784 __ CallRuntime(Runtime::kReThrow); 3785 break; 3786 case kContinue: 3787 codegen_->EmitContinue(cmd.target); 3788 break; 3789 case kBreak: 3790 codegen_->EmitBreak(cmd.target); 3791 break; 3792 } 3793 __ bind(&skip); 3794 } 3795 } 3796 3797 #undef __ 3798 3799 3800 void BackEdgeTable::PatchAt(Code* unoptimized_code, 3801 Address pc, 3802 BackEdgeState target_state, 3803 Code* replacement_code) { 3804 // Turn the jump into a nop. 3805 Address branch_address = pc - 3 * kInstructionSize; 3806 Isolate* isolate = unoptimized_code->GetIsolate(); 3807 PatchingAssembler patcher(isolate, branch_address, 1); 3808 3809 DCHECK(Instruction::Cast(branch_address) 3810 ->IsNop(Assembler::INTERRUPT_CODE_NOP) || 3811 (Instruction::Cast(branch_address)->IsCondBranchImm() && 3812 Instruction::Cast(branch_address)->ImmPCOffset() == 3813 6 * kInstructionSize)); 3814 3815 switch (target_state) { 3816 case INTERRUPT: 3817 // <decrement profiling counter> 3818 // .. .. .. .. b.pl ok 3819 // .. .. .. .. ldr x16, pc+<interrupt stub address> 3820 // .. .. .. .. blr x16 3821 // ... more instructions. 3822 // ok-label 3823 // Jump offset is 6 instructions. 3824 patcher.b(6, pl); 3825 break; 3826 case ON_STACK_REPLACEMENT: 3827 // <decrement profiling counter> 3828 // .. .. .. .. mov x0, x0 (NOP) 3829 // .. .. .. .. ldr x16, pc+<on-stack replacement address> 3830 // .. .. .. .. blr x16 3831 patcher.nop(Assembler::INTERRUPT_CODE_NOP); 3832 break; 3833 } 3834 3835 // Replace the call address. 3836 Instruction* load = Instruction::Cast(pc)->preceding(2); 3837 Address interrupt_address_pointer = 3838 reinterpret_cast<Address>(load) + load->ImmPCOffset(); 3839 DCHECK((Memory::uint64_at(interrupt_address_pointer) == 3840 reinterpret_cast<uint64_t>( 3841 isolate->builtins()->OnStackReplacement()->entry())) || 3842 (Memory::uint64_at(interrupt_address_pointer) == 3843 reinterpret_cast<uint64_t>( 3844 isolate->builtins()->InterruptCheck()->entry())) || 3845 (Memory::uint64_at(interrupt_address_pointer) == 3846 reinterpret_cast<uint64_t>( 3847 isolate->builtins()->OnStackReplacement()->entry()))); 3848 Memory::uint64_at(interrupt_address_pointer) = 3849 reinterpret_cast<uint64_t>(replacement_code->entry()); 3850 3851 unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( 3852 unoptimized_code, reinterpret_cast<Address>(load), replacement_code); 3853 } 3854 3855 3856 BackEdgeTable::BackEdgeState BackEdgeTable::GetBackEdgeState( 3857 Isolate* isolate, 3858 Code* unoptimized_code, 3859 Address pc) { 3860 // TODO(jbramley): There should be some extra assertions here (as in the ARM 3861 // back-end), but this function is gone in bleeding_edge so it might not 3862 // matter anyway. 3863 Instruction* jump_or_nop = Instruction::Cast(pc)->preceding(3); 3864 3865 if (jump_or_nop->IsNop(Assembler::INTERRUPT_CODE_NOP)) { 3866 Instruction* load = Instruction::Cast(pc)->preceding(2); 3867 uint64_t entry = Memory::uint64_at(reinterpret_cast<Address>(load) + 3868 load->ImmPCOffset()); 3869 if (entry == reinterpret_cast<uint64_t>( 3870 isolate->builtins()->OnStackReplacement()->entry())) { 3871 return ON_STACK_REPLACEMENT; 3872 } else { 3873 UNREACHABLE(); 3874 } 3875 } 3876 3877 return INTERRUPT; 3878 } 3879 3880 3881 } // namespace internal 3882 } // namespace v8 3883 3884 #endif // V8_TARGET_ARCH_ARM64 3885