1// Copyright 2014 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/compiler/code-generator.h" 6 7#include "src/arm64/macro-assembler-arm64.h" 8#include "src/compiler/code-generator-impl.h" 9#include "src/compiler/gap-resolver.h" 10#include "src/compiler/node-matchers.h" 11#include "src/compiler/node-properties-inl.h" 12#include "src/scopes.h" 13 14namespace v8 { 15namespace internal { 16namespace compiler { 17 18#define __ masm()-> 19 20 21// Adds Arm64-specific methods to convert InstructionOperands. 22class Arm64OperandConverter FINAL : public InstructionOperandConverter { 23 public: 24 Arm64OperandConverter(CodeGenerator* gen, Instruction* instr) 25 : InstructionOperandConverter(gen, instr) {} 26 27 Register InputRegister32(int index) { 28 return ToRegister(instr_->InputAt(index)).W(); 29 } 30 31 Register InputRegister64(int index) { return InputRegister(index); } 32 33 Operand InputImmediate(int index) { 34 return ToImmediate(instr_->InputAt(index)); 35 } 36 37 Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); } 38 39 Operand InputOperand64(int index) { return InputOperand(index); } 40 41 Operand InputOperand32(int index) { 42 return ToOperand32(instr_->InputAt(index)); 43 } 44 45 Register OutputRegister64() { return OutputRegister(); } 46 47 Register OutputRegister32() { return ToRegister(instr_->Output()).W(); } 48 49 MemOperand MemoryOperand(int* first_index) { 50 const int index = *first_index; 51 switch (AddressingModeField::decode(instr_->opcode())) { 52 case kMode_None: 53 break; 54 case kMode_MRI: 55 *first_index += 2; 56 return MemOperand(InputRegister(index + 0), InputInt32(index + 1)); 57 case kMode_MRR: 58 *first_index += 2; 59 return MemOperand(InputRegister(index + 0), InputRegister(index + 1), 60 SXTW); 61 } 62 UNREACHABLE(); 63 return MemOperand(no_reg); 64 } 65 66 MemOperand MemoryOperand() { 67 int index = 0; 68 return MemoryOperand(&index); 69 } 70 71 Operand ToOperand(InstructionOperand* op) { 72 if (op->IsRegister()) { 73 return Operand(ToRegister(op)); 74 } 75 return ToImmediate(op); 76 } 77 78 Operand ToOperand32(InstructionOperand* op) { 79 if (op->IsRegister()) { 80 return Operand(ToRegister(op).W()); 81 } 82 return ToImmediate(op); 83 } 84 85 Operand ToImmediate(InstructionOperand* operand) { 86 Constant constant = ToConstant(operand); 87 switch (constant.type()) { 88 case Constant::kInt32: 89 return Operand(constant.ToInt32()); 90 case Constant::kInt64: 91 return Operand(constant.ToInt64()); 92 case Constant::kFloat64: 93 return Operand( 94 isolate()->factory()->NewNumber(constant.ToFloat64(), TENURED)); 95 case Constant::kExternalReference: 96 return Operand(constant.ToExternalReference()); 97 case Constant::kHeapObject: 98 return Operand(constant.ToHeapObject()); 99 } 100 UNREACHABLE(); 101 return Operand(-1); 102 } 103 104 MemOperand ToMemOperand(InstructionOperand* op, MacroAssembler* masm) const { 105 DCHECK(op != NULL); 106 DCHECK(!op->IsRegister()); 107 DCHECK(!op->IsDoubleRegister()); 108 DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot()); 109 // The linkage computes where all spill slots are located. 110 FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), 0); 111 return MemOperand(offset.from_stack_pointer() ? masm->StackPointer() : fp, 112 offset.offset()); 113 } 114}; 115 116 117#define ASSEMBLE_SHIFT(asm_instr, width) \ 118 do { \ 119 if (instr->InputAt(1)->IsRegister()) { \ 120 __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0), \ 121 i.InputRegister##width(1)); \ 122 } else { \ 123 int64_t imm = i.InputOperand##width(1).immediate().value(); \ 124 __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0), imm); \ 125 } \ 126 } while (0); 127 128 129// Assembles an instruction after register allocation, producing machine code. 130void CodeGenerator::AssembleArchInstruction(Instruction* instr) { 131 Arm64OperandConverter i(this, instr); 132 InstructionCode opcode = instr->opcode(); 133 switch (ArchOpcodeField::decode(opcode)) { 134 case kArchCallCodeObject: { 135 EnsureSpaceForLazyDeopt(); 136 if (instr->InputAt(0)->IsImmediate()) { 137 __ Call(Handle<Code>::cast(i.InputHeapObject(0)), 138 RelocInfo::CODE_TARGET); 139 } else { 140 Register target = i.InputRegister(0); 141 __ Add(target, target, Code::kHeaderSize - kHeapObjectTag); 142 __ Call(target); 143 } 144 AddSafepointAndDeopt(instr); 145 break; 146 } 147 case kArchCallJSFunction: { 148 EnsureSpaceForLazyDeopt(); 149 Register func = i.InputRegister(0); 150 if (FLAG_debug_code) { 151 // Check the function's context matches the context argument. 152 UseScratchRegisterScope scope(masm()); 153 Register temp = scope.AcquireX(); 154 __ Ldr(temp, FieldMemOperand(func, JSFunction::kContextOffset)); 155 __ cmp(cp, temp); 156 __ Assert(eq, kWrongFunctionContext); 157 } 158 __ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset)); 159 __ Call(x10); 160 AddSafepointAndDeopt(instr); 161 break; 162 } 163 case kArchJmp: 164 __ B(code_->GetLabel(i.InputBlock(0))); 165 break; 166 case kArchNop: 167 // don't emit code for nops. 168 break; 169 case kArchRet: 170 AssembleReturn(); 171 break; 172 case kArchTruncateDoubleToI: 173 __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); 174 break; 175 case kArm64Add: 176 __ Add(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); 177 break; 178 case kArm64Add32: 179 if (FlagsModeField::decode(opcode) != kFlags_none) { 180 __ Adds(i.OutputRegister32(), i.InputRegister32(0), 181 i.InputOperand32(1)); 182 } else { 183 __ Add(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); 184 } 185 break; 186 case kArm64And: 187 __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); 188 break; 189 case kArm64And32: 190 __ And(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); 191 break; 192 case kArm64Mul: 193 __ Mul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); 194 break; 195 case kArm64Mul32: 196 __ Mul(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1)); 197 break; 198 case kArm64Idiv: 199 __ Sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); 200 break; 201 case kArm64Idiv32: 202 __ Sdiv(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1)); 203 break; 204 case kArm64Udiv: 205 __ Udiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); 206 break; 207 case kArm64Udiv32: 208 __ Udiv(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1)); 209 break; 210 case kArm64Imod: { 211 UseScratchRegisterScope scope(masm()); 212 Register temp = scope.AcquireX(); 213 __ Sdiv(temp, i.InputRegister(0), i.InputRegister(1)); 214 __ Msub(i.OutputRegister(), temp, i.InputRegister(1), i.InputRegister(0)); 215 break; 216 } 217 case kArm64Imod32: { 218 UseScratchRegisterScope scope(masm()); 219 Register temp = scope.AcquireW(); 220 __ Sdiv(temp, i.InputRegister32(0), i.InputRegister32(1)); 221 __ Msub(i.OutputRegister32(), temp, i.InputRegister32(1), 222 i.InputRegister32(0)); 223 break; 224 } 225 case kArm64Umod: { 226 UseScratchRegisterScope scope(masm()); 227 Register temp = scope.AcquireX(); 228 __ Udiv(temp, i.InputRegister(0), i.InputRegister(1)); 229 __ Msub(i.OutputRegister(), temp, i.InputRegister(1), i.InputRegister(0)); 230 break; 231 } 232 case kArm64Umod32: { 233 UseScratchRegisterScope scope(masm()); 234 Register temp = scope.AcquireW(); 235 __ Udiv(temp, i.InputRegister32(0), i.InputRegister32(1)); 236 __ Msub(i.OutputRegister32(), temp, i.InputRegister32(1), 237 i.InputRegister32(0)); 238 break; 239 } 240 // TODO(dcarney): use mvn instr?? 241 case kArm64Not: 242 __ Orn(i.OutputRegister(), xzr, i.InputOperand(0)); 243 break; 244 case kArm64Not32: 245 __ Orn(i.OutputRegister32(), wzr, i.InputOperand32(0)); 246 break; 247 case kArm64Neg: 248 __ Neg(i.OutputRegister(), i.InputOperand(0)); 249 break; 250 case kArm64Neg32: 251 __ Neg(i.OutputRegister32(), i.InputOperand32(0)); 252 break; 253 case kArm64Or: 254 __ Orr(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); 255 break; 256 case kArm64Or32: 257 __ Orr(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); 258 break; 259 case kArm64Xor: 260 __ Eor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); 261 break; 262 case kArm64Xor32: 263 __ Eor(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); 264 break; 265 case kArm64Sub: 266 __ Sub(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); 267 break; 268 case kArm64Sub32: 269 if (FlagsModeField::decode(opcode) != kFlags_none) { 270 __ Subs(i.OutputRegister32(), i.InputRegister32(0), 271 i.InputOperand32(1)); 272 } else { 273 __ Sub(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); 274 } 275 break; 276 case kArm64Shl: 277 ASSEMBLE_SHIFT(Lsl, 64); 278 break; 279 case kArm64Shl32: 280 ASSEMBLE_SHIFT(Lsl, 32); 281 break; 282 case kArm64Shr: 283 ASSEMBLE_SHIFT(Lsr, 64); 284 break; 285 case kArm64Shr32: 286 ASSEMBLE_SHIFT(Lsr, 32); 287 break; 288 case kArm64Sar: 289 ASSEMBLE_SHIFT(Asr, 64); 290 break; 291 case kArm64Sar32: 292 ASSEMBLE_SHIFT(Asr, 32); 293 break; 294 case kArm64Ror: 295 ASSEMBLE_SHIFT(Ror, 64); 296 break; 297 case kArm64Ror32: 298 ASSEMBLE_SHIFT(Ror, 32); 299 break; 300 case kArm64Mov32: 301 __ Mov(i.OutputRegister32(), i.InputRegister32(0)); 302 break; 303 case kArm64Sxtw: 304 __ Sxtw(i.OutputRegister(), i.InputRegister32(0)); 305 break; 306 case kArm64Claim: { 307 int words = MiscField::decode(instr->opcode()); 308 __ Claim(words); 309 break; 310 } 311 case kArm64Poke: { 312 int slot = MiscField::decode(instr->opcode()); 313 Operand operand(slot * kPointerSize); 314 __ Poke(i.InputRegister(0), operand); 315 break; 316 } 317 case kArm64PokePairZero: { 318 // TODO(dcarney): test slot offset and register order. 319 int slot = MiscField::decode(instr->opcode()) - 1; 320 __ PokePair(i.InputRegister(0), xzr, slot * kPointerSize); 321 break; 322 } 323 case kArm64PokePair: { 324 int slot = MiscField::decode(instr->opcode()) - 1; 325 __ PokePair(i.InputRegister(1), i.InputRegister(0), slot * kPointerSize); 326 break; 327 } 328 case kArm64Cmp: 329 __ Cmp(i.InputRegister(0), i.InputOperand(1)); 330 break; 331 case kArm64Cmp32: 332 __ Cmp(i.InputRegister32(0), i.InputOperand32(1)); 333 break; 334 case kArm64Cmn: 335 __ Cmn(i.InputRegister(0), i.InputOperand(1)); 336 break; 337 case kArm64Cmn32: 338 __ Cmn(i.InputRegister32(0), i.InputOperand32(1)); 339 break; 340 case kArm64Tst: 341 __ Tst(i.InputRegister(0), i.InputOperand(1)); 342 break; 343 case kArm64Tst32: 344 __ Tst(i.InputRegister32(0), i.InputOperand32(1)); 345 break; 346 case kArm64Float64Cmp: 347 __ Fcmp(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 348 break; 349 case kArm64Float64Add: 350 __ Fadd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 351 i.InputDoubleRegister(1)); 352 break; 353 case kArm64Float64Sub: 354 __ Fsub(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 355 i.InputDoubleRegister(1)); 356 break; 357 case kArm64Float64Mul: 358 __ Fmul(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 359 i.InputDoubleRegister(1)); 360 break; 361 case kArm64Float64Div: 362 __ Fdiv(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 363 i.InputDoubleRegister(1)); 364 break; 365 case kArm64Float64Mod: { 366 // TODO(dcarney): implement directly. See note in lithium-codegen-arm64.cc 367 FrameScope scope(masm(), StackFrame::MANUAL); 368 DCHECK(d0.is(i.InputDoubleRegister(0))); 369 DCHECK(d1.is(i.InputDoubleRegister(1))); 370 DCHECK(d0.is(i.OutputDoubleRegister())); 371 // TODO(dcarney): make sure this saves all relevant registers. 372 __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), 373 0, 2); 374 break; 375 } 376 case kArm64Float64Sqrt: 377 __ Fsqrt(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); 378 break; 379 case kArm64Float64ToInt32: 380 __ Fcvtzs(i.OutputRegister32(), i.InputDoubleRegister(0)); 381 break; 382 case kArm64Float64ToUint32: 383 __ Fcvtzu(i.OutputRegister32(), i.InputDoubleRegister(0)); 384 break; 385 case kArm64Int32ToFloat64: 386 __ Scvtf(i.OutputDoubleRegister(), i.InputRegister32(0)); 387 break; 388 case kArm64Uint32ToFloat64: 389 __ Ucvtf(i.OutputDoubleRegister(), i.InputRegister32(0)); 390 break; 391 case kArm64Ldrb: 392 __ Ldrb(i.OutputRegister(), i.MemoryOperand()); 393 break; 394 case kArm64Ldrsb: 395 __ Ldrsb(i.OutputRegister(), i.MemoryOperand()); 396 break; 397 case kArm64Strb: 398 __ Strb(i.InputRegister(2), i.MemoryOperand()); 399 break; 400 case kArm64Ldrh: 401 __ Ldrh(i.OutputRegister(), i.MemoryOperand()); 402 break; 403 case kArm64Ldrsh: 404 __ Ldrsh(i.OutputRegister(), i.MemoryOperand()); 405 break; 406 case kArm64Strh: 407 __ Strh(i.InputRegister(2), i.MemoryOperand()); 408 break; 409 case kArm64LdrW: 410 __ Ldr(i.OutputRegister32(), i.MemoryOperand()); 411 break; 412 case kArm64StrW: 413 __ Str(i.InputRegister32(2), i.MemoryOperand()); 414 break; 415 case kArm64Ldr: 416 __ Ldr(i.OutputRegister(), i.MemoryOperand()); 417 break; 418 case kArm64Str: 419 __ Str(i.InputRegister(2), i.MemoryOperand()); 420 break; 421 case kArm64LdrS: { 422 UseScratchRegisterScope scope(masm()); 423 FPRegister scratch = scope.AcquireS(); 424 __ Ldr(scratch, i.MemoryOperand()); 425 __ Fcvt(i.OutputDoubleRegister(), scratch); 426 break; 427 } 428 case kArm64StrS: { 429 UseScratchRegisterScope scope(masm()); 430 FPRegister scratch = scope.AcquireS(); 431 __ Fcvt(scratch, i.InputDoubleRegister(2)); 432 __ Str(scratch, i.MemoryOperand()); 433 break; 434 } 435 case kArm64LdrD: 436 __ Ldr(i.OutputDoubleRegister(), i.MemoryOperand()); 437 break; 438 case kArm64StrD: 439 __ Str(i.InputDoubleRegister(2), i.MemoryOperand()); 440 break; 441 case kArm64StoreWriteBarrier: { 442 Register object = i.InputRegister(0); 443 Register index = i.InputRegister(1); 444 Register value = i.InputRegister(2); 445 __ Add(index, object, Operand(index, SXTW)); 446 __ Str(value, MemOperand(index)); 447 SaveFPRegsMode mode = code_->frame()->DidAllocateDoubleRegisters() 448 ? kSaveFPRegs 449 : kDontSaveFPRegs; 450 // TODO(dcarney): we shouldn't test write barriers from c calls. 451 LinkRegisterStatus lr_status = kLRHasNotBeenSaved; 452 UseScratchRegisterScope scope(masm()); 453 Register temp = no_reg; 454 if (csp.is(masm()->StackPointer())) { 455 temp = scope.AcquireX(); 456 lr_status = kLRHasBeenSaved; 457 __ Push(lr, temp); // Need to push a pair 458 } 459 __ RecordWrite(object, index, value, lr_status, mode); 460 if (csp.is(masm()->StackPointer())) { 461 __ Pop(temp, lr); 462 } 463 break; 464 } 465 } 466} 467 468 469// Assemble branches after this instruction. 470void CodeGenerator::AssembleArchBranch(Instruction* instr, 471 FlagsCondition condition) { 472 Arm64OperandConverter i(this, instr); 473 Label done; 474 475 // Emit a branch. The true and false targets are always the last two inputs 476 // to the instruction. 477 BasicBlock* tblock = i.InputBlock(instr->InputCount() - 2); 478 BasicBlock* fblock = i.InputBlock(instr->InputCount() - 1); 479 bool fallthru = IsNextInAssemblyOrder(fblock); 480 Label* tlabel = code()->GetLabel(tblock); 481 Label* flabel = fallthru ? &done : code()->GetLabel(fblock); 482 switch (condition) { 483 case kUnorderedEqual: 484 __ B(vs, flabel); 485 // Fall through. 486 case kEqual: 487 __ B(eq, tlabel); 488 break; 489 case kUnorderedNotEqual: 490 __ B(vs, tlabel); 491 // Fall through. 492 case kNotEqual: 493 __ B(ne, tlabel); 494 break; 495 case kSignedLessThan: 496 __ B(lt, tlabel); 497 break; 498 case kSignedGreaterThanOrEqual: 499 __ B(ge, tlabel); 500 break; 501 case kSignedLessThanOrEqual: 502 __ B(le, tlabel); 503 break; 504 case kSignedGreaterThan: 505 __ B(gt, tlabel); 506 break; 507 case kUnorderedLessThan: 508 __ B(vs, flabel); 509 // Fall through. 510 case kUnsignedLessThan: 511 __ B(lo, tlabel); 512 break; 513 case kUnorderedGreaterThanOrEqual: 514 __ B(vs, tlabel); 515 // Fall through. 516 case kUnsignedGreaterThanOrEqual: 517 __ B(hs, tlabel); 518 break; 519 case kUnorderedLessThanOrEqual: 520 __ B(vs, flabel); 521 // Fall through. 522 case kUnsignedLessThanOrEqual: 523 __ B(ls, tlabel); 524 break; 525 case kUnorderedGreaterThan: 526 __ B(vs, tlabel); 527 // Fall through. 528 case kUnsignedGreaterThan: 529 __ B(hi, tlabel); 530 break; 531 case kOverflow: 532 __ B(vs, tlabel); 533 break; 534 case kNotOverflow: 535 __ B(vc, tlabel); 536 break; 537 } 538 if (!fallthru) __ B(flabel); // no fallthru to flabel. 539 __ Bind(&done); 540} 541 542 543// Assemble boolean materializations after this instruction. 544void CodeGenerator::AssembleArchBoolean(Instruction* instr, 545 FlagsCondition condition) { 546 Arm64OperandConverter i(this, instr); 547 Label done; 548 549 // Materialize a full 64-bit 1 or 0 value. The result register is always the 550 // last output of the instruction. 551 Label check; 552 DCHECK_NE(0, instr->OutputCount()); 553 Register reg = i.OutputRegister(instr->OutputCount() - 1); 554 Condition cc = nv; 555 switch (condition) { 556 case kUnorderedEqual: 557 __ B(vc, &check); 558 __ Mov(reg, 0); 559 __ B(&done); 560 // Fall through. 561 case kEqual: 562 cc = eq; 563 break; 564 case kUnorderedNotEqual: 565 __ B(vc, &check); 566 __ Mov(reg, 1); 567 __ B(&done); 568 // Fall through. 569 case kNotEqual: 570 cc = ne; 571 break; 572 case kSignedLessThan: 573 cc = lt; 574 break; 575 case kSignedGreaterThanOrEqual: 576 cc = ge; 577 break; 578 case kSignedLessThanOrEqual: 579 cc = le; 580 break; 581 case kSignedGreaterThan: 582 cc = gt; 583 break; 584 case kUnorderedLessThan: 585 __ B(vc, &check); 586 __ Mov(reg, 0); 587 __ B(&done); 588 // Fall through. 589 case kUnsignedLessThan: 590 cc = lo; 591 break; 592 case kUnorderedGreaterThanOrEqual: 593 __ B(vc, &check); 594 __ Mov(reg, 1); 595 __ B(&done); 596 // Fall through. 597 case kUnsignedGreaterThanOrEqual: 598 cc = hs; 599 break; 600 case kUnorderedLessThanOrEqual: 601 __ B(vc, &check); 602 __ Mov(reg, 0); 603 __ B(&done); 604 // Fall through. 605 case kUnsignedLessThanOrEqual: 606 cc = ls; 607 break; 608 case kUnorderedGreaterThan: 609 __ B(vc, &check); 610 __ Mov(reg, 1); 611 __ B(&done); 612 // Fall through. 613 case kUnsignedGreaterThan: 614 cc = hi; 615 break; 616 case kOverflow: 617 cc = vs; 618 break; 619 case kNotOverflow: 620 cc = vc; 621 break; 622 } 623 __ bind(&check); 624 __ Cset(reg, cc); 625 __ Bind(&done); 626} 627 628 629void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) { 630 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry( 631 isolate(), deoptimization_id, Deoptimizer::LAZY); 632 __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY); 633} 634 635 636// TODO(dcarney): increase stack slots in frame once before first use. 637static int AlignedStackSlots(int stack_slots) { 638 if (stack_slots & 1) stack_slots++; 639 return stack_slots; 640} 641 642 643void CodeGenerator::AssemblePrologue() { 644 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); 645 if (descriptor->kind() == CallDescriptor::kCallAddress) { 646 __ SetStackPointer(csp); 647 __ Push(lr, fp); 648 __ Mov(fp, csp); 649 // TODO(dcarney): correct callee saved registers. 650 __ PushCalleeSavedRegisters(); 651 frame()->SetRegisterSaveAreaSize(20 * kPointerSize); 652 } else if (descriptor->IsJSFunctionCall()) { 653 CompilationInfo* info = linkage()->info(); 654 __ SetStackPointer(jssp); 655 __ Prologue(info->IsCodePreAgingActive()); 656 frame()->SetRegisterSaveAreaSize( 657 StandardFrameConstants::kFixedFrameSizeFromFp); 658 659 // Sloppy mode functions and builtins need to replace the receiver with the 660 // global proxy when called as functions (without an explicit receiver 661 // object). 662 // TODO(mstarzinger/verwaest): Should this be moved back into the CallIC? 663 if (info->strict_mode() == SLOPPY && !info->is_native()) { 664 Label ok; 665 // +2 for return address and saved frame pointer. 666 int receiver_slot = info->scope()->num_parameters() + 2; 667 __ Ldr(x10, MemOperand(fp, receiver_slot * kXRegSize)); 668 __ JumpIfNotRoot(x10, Heap::kUndefinedValueRootIndex, &ok); 669 __ Ldr(x10, GlobalObjectMemOperand()); 670 __ Ldr(x10, FieldMemOperand(x10, GlobalObject::kGlobalProxyOffset)); 671 __ Str(x10, MemOperand(fp, receiver_slot * kXRegSize)); 672 __ Bind(&ok); 673 } 674 675 } else { 676 __ SetStackPointer(jssp); 677 __ StubPrologue(); 678 frame()->SetRegisterSaveAreaSize( 679 StandardFrameConstants::kFixedFrameSizeFromFp); 680 } 681 int stack_slots = frame()->GetSpillSlotCount(); 682 if (stack_slots > 0) { 683 Register sp = __ StackPointer(); 684 if (!sp.Is(csp)) { 685 __ Sub(sp, sp, stack_slots * kPointerSize); 686 } 687 __ Sub(csp, csp, AlignedStackSlots(stack_slots) * kPointerSize); 688 } 689} 690 691 692void CodeGenerator::AssembleReturn() { 693 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); 694 if (descriptor->kind() == CallDescriptor::kCallAddress) { 695 if (frame()->GetRegisterSaveAreaSize() > 0) { 696 // Remove this frame's spill slots first. 697 int stack_slots = frame()->GetSpillSlotCount(); 698 if (stack_slots > 0) { 699 __ Add(csp, csp, AlignedStackSlots(stack_slots) * kPointerSize); 700 } 701 // Restore registers. 702 // TODO(dcarney): correct callee saved registers. 703 __ PopCalleeSavedRegisters(); 704 } 705 __ Mov(csp, fp); 706 __ Pop(fp, lr); 707 __ Ret(); 708 } else { 709 __ Mov(jssp, fp); 710 __ Pop(fp, lr); 711 int pop_count = descriptor->IsJSFunctionCall() 712 ? static_cast<int>(descriptor->JSParameterCount()) 713 : 0; 714 __ Drop(pop_count); 715 __ Ret(); 716 } 717} 718 719 720void CodeGenerator::AssembleMove(InstructionOperand* source, 721 InstructionOperand* destination) { 722 Arm64OperandConverter g(this, NULL); 723 // Dispatch on the source and destination operand kinds. Not all 724 // combinations are possible. 725 if (source->IsRegister()) { 726 DCHECK(destination->IsRegister() || destination->IsStackSlot()); 727 Register src = g.ToRegister(source); 728 if (destination->IsRegister()) { 729 __ Mov(g.ToRegister(destination), src); 730 } else { 731 __ Str(src, g.ToMemOperand(destination, masm())); 732 } 733 } else if (source->IsStackSlot()) { 734 MemOperand src = g.ToMemOperand(source, masm()); 735 DCHECK(destination->IsRegister() || destination->IsStackSlot()); 736 if (destination->IsRegister()) { 737 __ Ldr(g.ToRegister(destination), src); 738 } else { 739 UseScratchRegisterScope scope(masm()); 740 Register temp = scope.AcquireX(); 741 __ Ldr(temp, src); 742 __ Str(temp, g.ToMemOperand(destination, masm())); 743 } 744 } else if (source->IsConstant()) { 745 ConstantOperand* constant_source = ConstantOperand::cast(source); 746 if (destination->IsRegister() || destination->IsStackSlot()) { 747 UseScratchRegisterScope scope(masm()); 748 Register dst = destination->IsRegister() ? g.ToRegister(destination) 749 : scope.AcquireX(); 750 Constant src = g.ToConstant(source); 751 if (src.type() == Constant::kHeapObject) { 752 __ LoadObject(dst, src.ToHeapObject()); 753 } else { 754 __ Mov(dst, g.ToImmediate(source)); 755 } 756 if (destination->IsStackSlot()) { 757 __ Str(dst, g.ToMemOperand(destination, masm())); 758 } 759 } else if (destination->IsDoubleRegister()) { 760 FPRegister result = g.ToDoubleRegister(destination); 761 __ Fmov(result, g.ToDouble(constant_source)); 762 } else { 763 DCHECK(destination->IsDoubleStackSlot()); 764 UseScratchRegisterScope scope(masm()); 765 FPRegister temp = scope.AcquireD(); 766 __ Fmov(temp, g.ToDouble(constant_source)); 767 __ Str(temp, g.ToMemOperand(destination, masm())); 768 } 769 } else if (source->IsDoubleRegister()) { 770 FPRegister src = g.ToDoubleRegister(source); 771 if (destination->IsDoubleRegister()) { 772 FPRegister dst = g.ToDoubleRegister(destination); 773 __ Fmov(dst, src); 774 } else { 775 DCHECK(destination->IsDoubleStackSlot()); 776 __ Str(src, g.ToMemOperand(destination, masm())); 777 } 778 } else if (source->IsDoubleStackSlot()) { 779 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot()); 780 MemOperand src = g.ToMemOperand(source, masm()); 781 if (destination->IsDoubleRegister()) { 782 __ Ldr(g.ToDoubleRegister(destination), src); 783 } else { 784 UseScratchRegisterScope scope(masm()); 785 FPRegister temp = scope.AcquireD(); 786 __ Ldr(temp, src); 787 __ Str(temp, g.ToMemOperand(destination, masm())); 788 } 789 } else { 790 UNREACHABLE(); 791 } 792} 793 794 795void CodeGenerator::AssembleSwap(InstructionOperand* source, 796 InstructionOperand* destination) { 797 Arm64OperandConverter g(this, NULL); 798 // Dispatch on the source and destination operand kinds. Not all 799 // combinations are possible. 800 if (source->IsRegister()) { 801 // Register-register. 802 UseScratchRegisterScope scope(masm()); 803 Register temp = scope.AcquireX(); 804 Register src = g.ToRegister(source); 805 if (destination->IsRegister()) { 806 Register dst = g.ToRegister(destination); 807 __ Mov(temp, src); 808 __ Mov(src, dst); 809 __ Mov(dst, temp); 810 } else { 811 DCHECK(destination->IsStackSlot()); 812 MemOperand dst = g.ToMemOperand(destination, masm()); 813 __ Mov(temp, src); 814 __ Ldr(src, dst); 815 __ Str(temp, dst); 816 } 817 } else if (source->IsStackSlot() || source->IsDoubleStackSlot()) { 818 UseScratchRegisterScope scope(masm()); 819 CPURegister temp_0 = scope.AcquireX(); 820 CPURegister temp_1 = scope.AcquireX(); 821 MemOperand src = g.ToMemOperand(source, masm()); 822 MemOperand dst = g.ToMemOperand(destination, masm()); 823 __ Ldr(temp_0, src); 824 __ Ldr(temp_1, dst); 825 __ Str(temp_0, dst); 826 __ Str(temp_1, src); 827 } else if (source->IsDoubleRegister()) { 828 UseScratchRegisterScope scope(masm()); 829 FPRegister temp = scope.AcquireD(); 830 FPRegister src = g.ToDoubleRegister(source); 831 if (destination->IsDoubleRegister()) { 832 FPRegister dst = g.ToDoubleRegister(destination); 833 __ Fmov(temp, src); 834 __ Fmov(src, dst); 835 __ Fmov(dst, temp); 836 } else { 837 DCHECK(destination->IsDoubleStackSlot()); 838 MemOperand dst = g.ToMemOperand(destination, masm()); 839 __ Fmov(temp, src); 840 __ Ldr(src, dst); 841 __ Str(temp, dst); 842 } 843 } else { 844 // No other combinations are possible. 845 UNREACHABLE(); 846 } 847} 848 849 850void CodeGenerator::AddNopForSmiCodeInlining() { __ movz(xzr, 0); } 851 852 853void CodeGenerator::EnsureSpaceForLazyDeopt() { 854 int space_needed = Deoptimizer::patch_size(); 855 if (!linkage()->info()->IsStub()) { 856 // Ensure that we have enough space after the previous lazy-bailout 857 // instruction for patching the code here. 858 intptr_t current_pc = masm()->pc_offset(); 859 860 if (current_pc < (last_lazy_deopt_pc_ + space_needed)) { 861 intptr_t padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; 862 DCHECK((padding_size % kInstructionSize) == 0); 863 InstructionAccurateScope instruction_accurate( 864 masm(), padding_size / kInstructionSize); 865 866 while (padding_size > 0) { 867 __ nop(); 868 padding_size -= kInstructionSize; 869 } 870 } 871 } 872 MarkLazyDeoptSite(); 873} 874 875#undef __ 876 877} // namespace compiler 878} // namespace internal 879} // namespace v8 880