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.
5 #include "src/compiler/code-generator.h"
7 #include "src/compiler/code-generator-impl.h"
8 #include "src/compiler/gap-resolver.h"
9 #include "src/compiler/node-matchers.h"
10 #include "src/compiler/node-properties-inl.h"
11 #include "src/compiler/osr.h"
12 #include "src/scopes.h"
13 #include "src/x64/assembler-x64.h"
14 #include "src/x64/macro-assembler-x64.h"
23 // Adds X64 specific methods for decoding operands.
24 class X64OperandConverter : public InstructionOperandConverter {
26 X64OperandConverter(CodeGenerator* gen, Instruction* instr)
27 : InstructionOperandConverter(gen, instr) {}
29 Immediate InputImmediate(int index) {
30 return ToImmediate(instr_->InputAt(index));
33 Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); }
35 Operand OutputOperand() { return ToOperand(instr_->Output()); }
37 Immediate ToImmediate(InstructionOperand* operand) {
38 return Immediate(ToConstant(operand).ToInt32());
41 Operand ToOperand(InstructionOperand* op, int extra = 0) {
42 DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot());
43 // The linkage computes where all spill slots are located.
44 FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), extra);
45 return Operand(offset.from_stack_pointer() ? rsp : rbp, offset.offset());
48 static int NextOffset(int* offset) {
54 static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
55 STATIC_ASSERT(0 == static_cast<int>(times_1));
56 STATIC_ASSERT(1 == static_cast<int>(times_2));
57 STATIC_ASSERT(2 == static_cast<int>(times_4));
58 STATIC_ASSERT(3 == static_cast<int>(times_8));
59 int scale = static_cast<int>(mode - one);
60 DCHECK(scale >= 0 && scale < 4);
61 return static_cast<ScaleFactor>(scale);
64 Operand MemoryOperand(int* offset) {
65 AddressingMode mode = AddressingModeField::decode(instr_->opcode());
68 Register base = InputRegister(NextOffset(offset));
70 return Operand(base, disp);
73 Register base = InputRegister(NextOffset(offset));
74 int32_t disp = InputInt32(NextOffset(offset));
75 return Operand(base, disp);
81 Register base = InputRegister(NextOffset(offset));
82 Register index = InputRegister(NextOffset(offset));
83 ScaleFactor scale = ScaleFor(kMode_MR1, mode);
85 return Operand(base, index, scale, disp);
91 Register base = InputRegister(NextOffset(offset));
92 Register index = InputRegister(NextOffset(offset));
93 ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
94 int32_t disp = InputInt32(NextOffset(offset));
95 return Operand(base, index, scale, disp);
98 Register base = InputRegister(NextOffset(offset));
100 return Operand(base, disp);
103 UNREACHABLE(); // Should use kModeMR with more compact encoding instead
104 return Operand(no_reg, 0);
107 Register index = InputRegister(NextOffset(offset));
108 ScaleFactor scale = ScaleFor(kMode_M1, mode);
110 return Operand(index, scale, disp);
116 Register index = InputRegister(NextOffset(offset));
117 ScaleFactor scale = ScaleFor(kMode_M1I, mode);
118 int32_t disp = InputInt32(NextOffset(offset));
119 return Operand(index, scale, disp);
123 return Operand(no_reg, 0);
126 return Operand(no_reg, 0);
129 Operand MemoryOperand(int first_input = 0) {
130 return MemoryOperand(&first_input);
137 bool HasImmediateInput(Instruction* instr, int index) {
138 return instr->InputAt(index)->IsImmediate();
142 class OutOfLineLoadZero FINAL : public OutOfLineCode {
144 OutOfLineLoadZero(CodeGenerator* gen, Register result)
145 : OutOfLineCode(gen), result_(result) {}
147 void Generate() FINAL { __ xorl(result_, result_); }
150 Register const result_;
154 class OutOfLineLoadNaN FINAL : public OutOfLineCode {
156 OutOfLineLoadNaN(CodeGenerator* gen, XMMRegister result)
157 : OutOfLineCode(gen), result_(result) {}
159 void Generate() FINAL { __ pcmpeqd(result_, result_); }
162 XMMRegister const result_;
166 class OutOfLineTruncateDoubleToI FINAL : public OutOfLineCode {
168 OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result,
170 : OutOfLineCode(gen), result_(result), input_(input) {}
172 void Generate() FINAL {
173 __ subp(rsp, Immediate(kDoubleSize));
174 __ movsd(MemOperand(rsp, 0), input_);
175 __ SlowTruncateToI(result_, rsp, 0);
176 __ addp(rsp, Immediate(kDoubleSize));
180 Register const result_;
181 XMMRegister const input_;
187 #define ASSEMBLE_UNOP(asm_instr) \
189 if (instr->Output()->IsRegister()) { \
190 __ asm_instr(i.OutputRegister()); \
192 __ asm_instr(i.OutputOperand()); \
197 #define ASSEMBLE_BINOP(asm_instr) \
199 if (HasImmediateInput(instr, 1)) { \
200 if (instr->InputAt(0)->IsRegister()) { \
201 __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \
203 __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
206 if (instr->InputAt(1)->IsRegister()) { \
207 __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \
209 __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
215 #define ASSEMBLE_MULT(asm_instr) \
217 if (HasImmediateInput(instr, 1)) { \
218 if (instr->InputAt(0)->IsRegister()) { \
219 __ asm_instr(i.OutputRegister(), i.InputRegister(0), \
220 i.InputImmediate(1)); \
222 __ asm_instr(i.OutputRegister(), i.InputOperand(0), \
223 i.InputImmediate(1)); \
226 if (instr->InputAt(1)->IsRegister()) { \
227 __ asm_instr(i.OutputRegister(), i.InputRegister(1)); \
229 __ asm_instr(i.OutputRegister(), i.InputOperand(1)); \
235 #define ASSEMBLE_SHIFT(asm_instr, width) \
237 if (HasImmediateInput(instr, 1)) { \
238 if (instr->Output()->IsRegister()) { \
239 __ asm_instr(i.OutputRegister(), Immediate(i.InputInt##width(1))); \
241 __ asm_instr(i.OutputOperand(), Immediate(i.InputInt##width(1))); \
244 if (instr->Output()->IsRegister()) { \
245 __ asm_instr##_cl(i.OutputRegister()); \
247 __ asm_instr##_cl(i.OutputOperand()); \
253 #define ASSEMBLE_DOUBLE_BINOP(asm_instr) \
255 if (instr->InputAt(1)->IsDoubleRegister()) { \
256 __ asm_instr(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); \
258 __ asm_instr(i.InputDoubleRegister(0), i.InputOperand(1)); \
263 #define ASSEMBLE_AVX_DOUBLE_BINOP(asm_instr) \
265 CpuFeatureScope avx_scope(masm(), AVX); \
266 if (instr->InputAt(1)->IsDoubleRegister()) { \
267 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
268 i.InputDoubleRegister(1)); \
270 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
271 i.InputOperand(1)); \
276 #define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr) \
278 auto result = i.OutputDoubleRegister(); \
279 auto buffer = i.InputRegister(0); \
280 auto index1 = i.InputRegister(1); \
281 auto index2 = i.InputInt32(2); \
282 OutOfLineCode* ool; \
283 if (instr->InputAt(3)->IsRegister()) { \
284 auto length = i.InputRegister(3); \
285 DCHECK_EQ(0, index2); \
286 __ cmpl(index1, length); \
287 ool = new (zone()) OutOfLineLoadNaN(this, result); \
289 auto length = i.InputInt32(3); \
290 DCHECK_LE(index2, length); \
291 __ cmpq(index1, Immediate(length - index2)); \
292 class OutOfLineLoadFloat FINAL : public OutOfLineCode { \
294 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result, \
295 Register buffer, Register index1, int32_t index2, \
297 : OutOfLineCode(gen), \
304 void Generate() FINAL { \
305 __ leal(kScratchRegister, Operand(index1_, index2_)); \
306 __ pcmpeqd(result_, result_); \
307 __ cmpl(kScratchRegister, Immediate(length_)); \
308 __ j(above_equal, exit()); \
309 __ asm_instr(result_, \
310 Operand(buffer_, kScratchRegister, times_1, 0)); \
314 XMMRegister const result_; \
315 Register const buffer_; \
316 Register const index1_; \
317 int32_t const index2_; \
318 int32_t const length_; \
321 OutOfLineLoadFloat(this, result, buffer, index1, index2, length); \
323 __ j(above_equal, ool->entry()); \
324 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
325 __ bind(ool->exit()); \
329 #define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \
331 auto result = i.OutputRegister(); \
332 auto buffer = i.InputRegister(0); \
333 auto index1 = i.InputRegister(1); \
334 auto index2 = i.InputInt32(2); \
335 OutOfLineCode* ool; \
336 if (instr->InputAt(3)->IsRegister()) { \
337 auto length = i.InputRegister(3); \
338 DCHECK_EQ(0, index2); \
339 __ cmpl(index1, length); \
340 ool = new (zone()) OutOfLineLoadZero(this, result); \
342 auto length = i.InputInt32(3); \
343 DCHECK_LE(index2, length); \
344 __ cmpq(index1, Immediate(length - index2)); \
345 class OutOfLineLoadInteger FINAL : public OutOfLineCode { \
347 OutOfLineLoadInteger(CodeGenerator* gen, Register result, \
348 Register buffer, Register index1, int32_t index2, \
350 : OutOfLineCode(gen), \
357 void Generate() FINAL { \
359 __ leal(kScratchRegister, Operand(index1_, index2_)); \
360 __ cmpl(kScratchRegister, Immediate(length_)); \
361 __ j(above_equal, &oob, Label::kNear); \
362 __ asm_instr(result_, \
363 Operand(buffer_, kScratchRegister, times_1, 0)); \
366 __ xorl(result_, result_); \
370 Register const result_; \
371 Register const buffer_; \
372 Register const index1_; \
373 int32_t const index2_; \
374 int32_t const length_; \
377 OutOfLineLoadInteger(this, result, buffer, index1, index2, length); \
379 __ j(above_equal, ool->entry()); \
380 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
381 __ bind(ool->exit()); \
385 #define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \
387 auto buffer = i.InputRegister(0); \
388 auto index1 = i.InputRegister(1); \
389 auto index2 = i.InputInt32(2); \
390 auto value = i.InputDoubleRegister(4); \
391 if (instr->InputAt(3)->IsRegister()) { \
392 auto length = i.InputRegister(3); \
393 DCHECK_EQ(0, index2); \
395 __ cmpl(index1, length); \
396 __ j(above_equal, &done, Label::kNear); \
397 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
400 auto length = i.InputInt32(3); \
401 DCHECK_LE(index2, length); \
402 __ cmpq(index1, Immediate(length - index2)); \
403 class OutOfLineStoreFloat FINAL : public OutOfLineCode { \
405 OutOfLineStoreFloat(CodeGenerator* gen, Register buffer, \
406 Register index1, int32_t index2, int32_t length, \
408 : OutOfLineCode(gen), \
415 void Generate() FINAL { \
416 __ leal(kScratchRegister, Operand(index1_, index2_)); \
417 __ cmpl(kScratchRegister, Immediate(length_)); \
418 __ j(above_equal, exit()); \
419 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
424 Register const buffer_; \
425 Register const index1_; \
426 int32_t const index2_; \
427 int32_t const length_; \
428 XMMRegister const value_; \
430 auto ool = new (zone()) \
431 OutOfLineStoreFloat(this, buffer, index1, index2, length, value); \
432 __ j(above_equal, ool->entry()); \
433 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
434 __ bind(ool->exit()); \
439 #define ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Value) \
441 auto buffer = i.InputRegister(0); \
442 auto index1 = i.InputRegister(1); \
443 auto index2 = i.InputInt32(2); \
444 if (instr->InputAt(3)->IsRegister()) { \
445 auto length = i.InputRegister(3); \
446 DCHECK_EQ(0, index2); \
448 __ cmpl(index1, length); \
449 __ j(above_equal, &done, Label::kNear); \
450 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
453 auto length = i.InputInt32(3); \
454 DCHECK_LE(index2, length); \
455 __ cmpq(index1, Immediate(length - index2)); \
456 class OutOfLineStoreInteger FINAL : public OutOfLineCode { \
458 OutOfLineStoreInteger(CodeGenerator* gen, Register buffer, \
459 Register index1, int32_t index2, int32_t length, \
461 : OutOfLineCode(gen), \
468 void Generate() FINAL { \
469 __ leal(kScratchRegister, Operand(index1_, index2_)); \
470 __ cmpl(kScratchRegister, Immediate(length_)); \
471 __ j(above_equal, exit()); \
472 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
477 Register const buffer_; \
478 Register const index1_; \
479 int32_t const index2_; \
480 int32_t const length_; \
481 Value const value_; \
483 auto ool = new (zone()) \
484 OutOfLineStoreInteger(this, buffer, index1, index2, length, value); \
485 __ j(above_equal, ool->entry()); \
486 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
487 __ bind(ool->exit()); \
492 #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \
494 if (instr->InputAt(4)->IsRegister()) { \
495 Register value = i.InputRegister(4); \
496 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Register); \
498 Immediate value = i.InputImmediate(4); \
499 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Immediate); \
504 // Assembles an instruction after register allocation, producing machine code.
505 void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
506 X64OperandConverter i(this, instr);
508 switch (ArchOpcodeField::decode(instr->opcode())) {
509 case kArchCallCodeObject: {
510 EnsureSpaceForLazyDeopt();
511 if (HasImmediateInput(instr, 0)) {
512 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
513 __ Call(code, RelocInfo::CODE_TARGET);
515 Register reg = i.InputRegister(0);
516 int entry = Code::kHeaderSize - kHeapObjectTag;
517 __ Call(Operand(reg, entry));
519 AddSafepointAndDeopt(instr);
522 case kArchCallJSFunction: {
523 EnsureSpaceForLazyDeopt();
524 Register func = i.InputRegister(0);
525 if (FLAG_debug_code) {
526 // Check the function's context matches the context argument.
527 __ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset));
528 __ Assert(equal, kWrongFunctionContext);
530 __ Call(FieldOperand(func, JSFunction::kCodeEntryOffset));
531 AddSafepointAndDeopt(instr);
535 AssembleArchJump(i.InputRpo(0));
538 // don't emit code for nops.
543 case kArchStackPointer:
544 __ movq(i.OutputRegister(), rsp);
546 case kArchTruncateDoubleToI: {
547 auto result = i.OutputRegister();
548 auto input = i.InputDoubleRegister(0);
549 auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input);
550 __ cvttsd2siq(result, input);
551 __ cmpq(result, Immediate(1));
552 __ j(overflow, ool->entry());
553 __ bind(ool->exit());
557 ASSEMBLE_BINOP(addl);
560 ASSEMBLE_BINOP(addq);
563 ASSEMBLE_BINOP(subl);
566 ASSEMBLE_BINOP(subq);
569 ASSEMBLE_BINOP(andl);
572 ASSEMBLE_BINOP(andq);
575 ASSEMBLE_BINOP(cmpl);
578 ASSEMBLE_BINOP(cmpq);
581 ASSEMBLE_BINOP(testl);
584 ASSEMBLE_BINOP(testq);
587 ASSEMBLE_MULT(imull);
590 ASSEMBLE_MULT(imulq);
593 if (instr->InputAt(1)->IsRegister()) {
594 __ imull(i.InputRegister(1));
596 __ imull(i.InputOperand(1));
600 if (instr->InputAt(1)->IsRegister()) {
601 __ mull(i.InputRegister(1));
603 __ mull(i.InputOperand(1));
608 __ idivl(i.InputRegister(1));
612 __ idivq(i.InputRegister(1));
616 __ divl(i.InputRegister(1));
620 __ divq(i.InputRegister(1));
641 ASSEMBLE_BINOP(xorl);
644 ASSEMBLE_BINOP(xorq);
647 ASSEMBLE_SHIFT(shll, 5);
650 ASSEMBLE_SHIFT(shlq, 6);
653 ASSEMBLE_SHIFT(shrl, 5);
656 ASSEMBLE_SHIFT(shrq, 6);
659 ASSEMBLE_SHIFT(sarl, 5);
662 ASSEMBLE_SHIFT(sarq, 6);
665 ASSEMBLE_SHIFT(rorl, 5);
668 ASSEMBLE_SHIFT(rorq, 6);
671 ASSEMBLE_DOUBLE_BINOP(ucomisd);
674 ASSEMBLE_DOUBLE_BINOP(addsd);
677 ASSEMBLE_DOUBLE_BINOP(subsd);
680 ASSEMBLE_DOUBLE_BINOP(mulsd);
683 ASSEMBLE_DOUBLE_BINOP(divsd);
685 case kSSEFloat64Mod: {
686 __ subq(rsp, Immediate(kDoubleSize));
687 // Move values to st(0) and st(1).
688 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(1));
689 __ fld_d(Operand(rsp, 0));
690 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(0));
691 __ fld_d(Operand(rsp, 0));
692 // Loop while fprem isn't done.
695 // This instructions traps on all kinds inputs, but we are assuming the
696 // floating point control word is set to ignore them all.
698 // The following 2 instruction implicitly use rax.
700 if (CpuFeatures::IsSupported(SAHF)) {
701 CpuFeatureScope sahf_scope(masm(), SAHF);
704 __ shrl(rax, Immediate(8));
705 __ andl(rax, Immediate(0xFF));
709 __ j(parity_even, &mod_loop);
710 // Move output to stack and clean up.
712 __ fstp_d(Operand(rsp, 0));
713 __ movsd(i.OutputDoubleRegister(), Operand(rsp, 0));
714 __ addq(rsp, Immediate(kDoubleSize));
717 case kSSEFloat64Sqrt:
718 if (instr->InputAt(0)->IsDoubleRegister()) {
719 __ sqrtsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
721 __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0));
724 case kSSEFloat64Floor: {
725 CpuFeatureScope sse_scope(masm(), SSE4_1);
726 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
727 v8::internal::Assembler::kRoundDown);
730 case kSSEFloat64Ceil: {
731 CpuFeatureScope sse_scope(masm(), SSE4_1);
732 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
733 v8::internal::Assembler::kRoundUp);
736 case kSSEFloat64RoundTruncate: {
737 CpuFeatureScope sse_scope(masm(), SSE4_1);
738 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
739 v8::internal::Assembler::kRoundToZero);
743 if (instr->InputAt(0)->IsDoubleRegister()) {
744 __ cvtss2sd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
746 __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0));
750 if (instr->InputAt(0)->IsDoubleRegister()) {
751 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
753 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0));
756 case kSSEFloat64ToInt32:
757 if (instr->InputAt(0)->IsDoubleRegister()) {
758 __ cvttsd2si(i.OutputRegister(), i.InputDoubleRegister(0));
760 __ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
763 case kSSEFloat64ToUint32: {
764 if (instr->InputAt(0)->IsDoubleRegister()) {
765 __ cvttsd2siq(i.OutputRegister(), i.InputDoubleRegister(0));
767 __ cvttsd2siq(i.OutputRegister(), i.InputOperand(0));
769 __ AssertZeroExtended(i.OutputRegister());
772 case kSSEInt32ToFloat64:
773 if (instr->InputAt(0)->IsRegister()) {
774 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputRegister(0));
776 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputOperand(0));
779 case kSSEUint32ToFloat64:
780 if (instr->InputAt(0)->IsRegister()) {
781 __ movl(kScratchRegister, i.InputRegister(0));
783 __ movl(kScratchRegister, i.InputOperand(0));
785 __ cvtqsi2sd(i.OutputDoubleRegister(), kScratchRegister);
788 ASSEMBLE_AVX_DOUBLE_BINOP(vaddsd);
791 ASSEMBLE_AVX_DOUBLE_BINOP(vsubsd);
794 ASSEMBLE_AVX_DOUBLE_BINOP(vmulsd);
797 ASSEMBLE_AVX_DOUBLE_BINOP(vdivsd);
800 if (instr->addressing_mode() != kMode_None) {
801 __ movsxbl(i.OutputRegister(), i.MemoryOperand());
802 } else if (instr->InputAt(0)->IsRegister()) {
803 __ movsxbl(i.OutputRegister(), i.InputRegister(0));
805 __ movsxbl(i.OutputRegister(), i.InputOperand(0));
807 __ AssertZeroExtended(i.OutputRegister());
810 __ movzxbl(i.OutputRegister(), i.MemoryOperand());
814 Operand operand = i.MemoryOperand(&index);
815 if (HasImmediateInput(instr, index)) {
816 __ movb(operand, Immediate(i.InputInt8(index)));
818 __ movb(operand, i.InputRegister(index));
823 if (instr->addressing_mode() != kMode_None) {
824 __ movsxwl(i.OutputRegister(), i.MemoryOperand());
825 } else if (instr->InputAt(0)->IsRegister()) {
826 __ movsxwl(i.OutputRegister(), i.InputRegister(0));
828 __ movsxwl(i.OutputRegister(), i.InputOperand(0));
830 __ AssertZeroExtended(i.OutputRegister());
833 __ movzxwl(i.OutputRegister(), i.MemoryOperand());
834 __ AssertZeroExtended(i.OutputRegister());
838 Operand operand = i.MemoryOperand(&index);
839 if (HasImmediateInput(instr, index)) {
840 __ movw(operand, Immediate(i.InputInt16(index)));
842 __ movw(operand, i.InputRegister(index));
847 if (instr->HasOutput()) {
848 if (instr->addressing_mode() == kMode_None) {
849 if (instr->InputAt(0)->IsRegister()) {
850 __ movl(i.OutputRegister(), i.InputRegister(0));
852 __ movl(i.OutputRegister(), i.InputOperand(0));
855 __ movl(i.OutputRegister(), i.MemoryOperand());
857 __ AssertZeroExtended(i.OutputRegister());
860 Operand operand = i.MemoryOperand(&index);
861 if (HasImmediateInput(instr, index)) {
862 __ movl(operand, i.InputImmediate(index));
864 __ movl(operand, i.InputRegister(index));
869 if (instr->InputAt(0)->IsRegister()) {
870 __ movsxlq(i.OutputRegister(), i.InputRegister(0));
872 __ movsxlq(i.OutputRegister(), i.InputOperand(0));
877 if (instr->HasOutput()) {
878 __ movq(i.OutputRegister(), i.MemoryOperand());
881 Operand operand = i.MemoryOperand(&index);
882 if (HasImmediateInput(instr, index)) {
883 __ movq(operand, i.InputImmediate(index));
885 __ movq(operand, i.InputRegister(index));
890 if (instr->HasOutput()) {
891 __ movss(i.OutputDoubleRegister(), i.MemoryOperand());
894 Operand operand = i.MemoryOperand(&index);
895 __ movss(operand, i.InputDoubleRegister(index));
899 if (instr->HasOutput()) {
900 __ movsd(i.OutputDoubleRegister(), i.MemoryOperand());
903 Operand operand = i.MemoryOperand(&index);
904 __ movsd(operand, i.InputDoubleRegister(index));
908 AddressingMode mode = AddressingModeField::decode(instr->opcode());
909 // Shorten "leal" to "addl", "subl" or "shll" if the register allocation
910 // and addressing mode just happens to work out. The "addl"/"subl" forms
911 // in these cases are faster based on measurements.
912 if (i.InputRegister(0).is(i.OutputRegister())) {
913 if (mode == kMode_MRI) {
914 int32_t constant_summand = i.InputInt32(1);
915 if (constant_summand > 0) {
916 __ addl(i.OutputRegister(), Immediate(constant_summand));
917 } else if (constant_summand < 0) {
918 __ subl(i.OutputRegister(), Immediate(-constant_summand));
920 } else if (mode == kMode_MR1) {
921 if (i.InputRegister(1).is(i.OutputRegister())) {
922 __ shll(i.OutputRegister(), Immediate(1));
924 __ leal(i.OutputRegister(), i.MemoryOperand());
926 } else if (mode == kMode_M2) {
927 __ shll(i.OutputRegister(), Immediate(1));
928 } else if (mode == kMode_M4) {
929 __ shll(i.OutputRegister(), Immediate(2));
930 } else if (mode == kMode_M8) {
931 __ shll(i.OutputRegister(), Immediate(3));
933 __ leal(i.OutputRegister(), i.MemoryOperand());
936 __ leal(i.OutputRegister(), i.MemoryOperand());
938 __ AssertZeroExtended(i.OutputRegister());
942 __ leaq(i.OutputRegister(), i.MemoryOperand());
945 __ decl(i.OutputRegister());
948 __ incl(i.OutputRegister());
951 if (HasImmediateInput(instr, 0)) {
952 __ pushq(i.InputImmediate(0));
954 if (instr->InputAt(0)->IsRegister()) {
955 __ pushq(i.InputRegister(0));
957 __ pushq(i.InputOperand(0));
961 case kX64StoreWriteBarrier: {
962 Register object = i.InputRegister(0);
963 Register index = i.InputRegister(1);
964 Register value = i.InputRegister(2);
965 __ movsxlq(index, index);
966 __ movq(Operand(object, index, times_1, 0), value);
967 __ leaq(index, Operand(object, index, times_1, 0));
968 SaveFPRegsMode mode =
969 frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
970 __ RecordWrite(object, index, value, mode);
973 case kCheckedLoadInt8:
974 ASSEMBLE_CHECKED_LOAD_INTEGER(movsxbl);
976 case kCheckedLoadUint8:
977 ASSEMBLE_CHECKED_LOAD_INTEGER(movzxbl);
979 case kCheckedLoadInt16:
980 ASSEMBLE_CHECKED_LOAD_INTEGER(movsxwl);
982 case kCheckedLoadUint16:
983 ASSEMBLE_CHECKED_LOAD_INTEGER(movzxwl);
985 case kCheckedLoadWord32:
986 ASSEMBLE_CHECKED_LOAD_INTEGER(movl);
988 case kCheckedLoadFloat32:
989 ASSEMBLE_CHECKED_LOAD_FLOAT(movss);
991 case kCheckedLoadFloat64:
992 ASSEMBLE_CHECKED_LOAD_FLOAT(movsd);
994 case kCheckedStoreWord8:
995 ASSEMBLE_CHECKED_STORE_INTEGER(movb);
997 case kCheckedStoreWord16:
998 ASSEMBLE_CHECKED_STORE_INTEGER(movw);
1000 case kCheckedStoreWord32:
1001 ASSEMBLE_CHECKED_STORE_INTEGER(movl);
1003 case kCheckedStoreFloat32:
1004 ASSEMBLE_CHECKED_STORE_FLOAT(movss);
1006 case kCheckedStoreFloat64:
1007 ASSEMBLE_CHECKED_STORE_FLOAT(movsd);
1013 // Assembles branches after this instruction.
1014 void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
1015 X64OperandConverter i(this, instr);
1016 Label::Distance flabel_distance =
1017 branch->fallthru ? Label::kNear : Label::kFar;
1018 Label* tlabel = branch->true_label;
1019 Label* flabel = branch->false_label;
1020 switch (branch->condition) {
1021 case kUnorderedEqual:
1022 __ j(parity_even, flabel, flabel_distance);
1025 __ j(equal, tlabel);
1027 case kUnorderedNotEqual:
1028 __ j(parity_even, tlabel);
1031 __ j(not_equal, tlabel);
1033 case kSignedLessThan:
1036 case kSignedGreaterThanOrEqual:
1037 __ j(greater_equal, tlabel);
1039 case kSignedLessThanOrEqual:
1040 __ j(less_equal, tlabel);
1042 case kSignedGreaterThan:
1043 __ j(greater, tlabel);
1045 case kUnsignedLessThan:
1046 __ j(below, tlabel);
1048 case kUnsignedGreaterThanOrEqual:
1049 __ j(above_equal, tlabel);
1051 case kUnsignedLessThanOrEqual:
1052 __ j(below_equal, tlabel);
1054 case kUnsignedGreaterThan:
1055 __ j(above, tlabel);
1058 __ j(overflow, tlabel);
1061 __ j(no_overflow, tlabel);
1064 if (!branch->fallthru) __ jmp(flabel, flabel_distance);
1068 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
1069 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target));
1073 // Assembles boolean materializations after this instruction.
1074 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
1075 FlagsCondition condition) {
1076 X64OperandConverter i(this, instr);
1079 // Materialize a full 64-bit 1 or 0 value. The result register is always the
1080 // last output of the instruction.
1082 DCHECK_NE(0, static_cast<int>(instr->OutputCount()));
1083 Register reg = i.OutputRegister(static_cast<int>(instr->OutputCount() - 1));
1084 Condition cc = no_condition;
1085 switch (condition) {
1086 case kUnorderedEqual:
1087 __ j(parity_odd, &check, Label::kNear);
1088 __ movl(reg, Immediate(0));
1089 __ jmp(&done, Label::kNear);
1094 case kUnorderedNotEqual:
1095 __ j(parity_odd, &check, Label::kNear);
1096 __ movl(reg, Immediate(1));
1097 __ jmp(&done, Label::kNear);
1102 case kSignedLessThan:
1105 case kSignedGreaterThanOrEqual:
1108 case kSignedLessThanOrEqual:
1111 case kSignedGreaterThan:
1114 case kUnsignedLessThan:
1117 case kUnsignedGreaterThanOrEqual:
1120 case kUnsignedLessThanOrEqual:
1123 case kUnsignedGreaterThan:
1135 __ movzxbl(reg, reg);
1140 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
1141 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
1142 isolate(), deoptimization_id, Deoptimizer::LAZY);
1143 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
1147 void CodeGenerator::AssemblePrologue() {
1148 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1149 int stack_slots = frame()->GetSpillSlotCount();
1150 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1153 const RegList saves = descriptor->CalleeSavedRegisters();
1154 if (saves != 0) { // Save callee-saved registers.
1155 int register_save_area_size = 0;
1156 for (int i = Register::kNumRegisters - 1; i >= 0; i--) {
1157 if (!((1 << i) & saves)) continue;
1158 __ pushq(Register::from_code(i));
1159 register_save_area_size += kPointerSize;
1161 frame()->SetRegisterSaveAreaSize(register_save_area_size);
1163 } else if (descriptor->IsJSFunctionCall()) {
1164 CompilationInfo* info = this->info();
1165 __ Prologue(info->IsCodePreAgingActive());
1166 frame()->SetRegisterSaveAreaSize(
1167 StandardFrameConstants::kFixedFrameSizeFromFp);
1170 frame()->SetRegisterSaveAreaSize(
1171 StandardFrameConstants::kFixedFrameSizeFromFp);
1174 if (info()->is_osr()) {
1175 // TurboFan OSR-compiled functions cannot be entered directly.
1176 __ Abort(kShouldNotDirectlyEnterOsrFunction);
1178 // Unoptimized code jumps directly to this entrypoint while the unoptimized
1179 // frame is still on the stack. Optimized code uses OSR values directly from
1180 // the unoptimized frame. Thus, all that needs to be done is to allocate the
1181 // remaining stack slots.
1182 if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
1183 osr_pc_offset_ = __ pc_offset();
1184 int unoptimized_slots =
1185 static_cast<int>(OsrHelper(info()).UnoptimizedFrameSlots());
1186 DCHECK(stack_slots >= unoptimized_slots);
1187 stack_slots -= unoptimized_slots;
1190 if (stack_slots > 0) {
1191 __ subq(rsp, Immediate(stack_slots * kPointerSize));
1196 void CodeGenerator::AssembleReturn() {
1197 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1198 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1199 if (frame()->GetRegisterSaveAreaSize() > 0) {
1200 // Remove this frame's spill slots first.
1201 int stack_slots = frame()->GetSpillSlotCount();
1202 if (stack_slots > 0) {
1203 __ addq(rsp, Immediate(stack_slots * kPointerSize));
1205 const RegList saves = descriptor->CalleeSavedRegisters();
1206 // Restore registers.
1208 for (int i = 0; i < Register::kNumRegisters; i++) {
1209 if (!((1 << i) & saves)) continue;
1210 __ popq(Register::from_code(i));
1213 __ popq(rbp); // Pop caller's frame pointer.
1216 // No saved registers.
1217 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1218 __ popq(rbp); // Pop caller's frame pointer.
1222 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1223 __ popq(rbp); // Pop caller's frame pointer.
1224 int pop_count = descriptor->IsJSFunctionCall()
1225 ? static_cast<int>(descriptor->JSParameterCount())
1227 __ ret(pop_count * kPointerSize);
1232 void CodeGenerator::AssembleMove(InstructionOperand* source,
1233 InstructionOperand* destination) {
1234 X64OperandConverter g(this, NULL);
1235 // Dispatch on the source and destination operand kinds. Not all
1236 // combinations are possible.
1237 if (source->IsRegister()) {
1238 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1239 Register src = g.ToRegister(source);
1240 if (destination->IsRegister()) {
1241 __ movq(g.ToRegister(destination), src);
1243 __ movq(g.ToOperand(destination), src);
1245 } else if (source->IsStackSlot()) {
1246 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1247 Operand src = g.ToOperand(source);
1248 if (destination->IsRegister()) {
1249 Register dst = g.ToRegister(destination);
1252 // Spill on demand to use a temporary register for memory-to-memory
1254 Register tmp = kScratchRegister;
1255 Operand dst = g.ToOperand(destination);
1259 } else if (source->IsConstant()) {
1260 ConstantOperand* constant_source = ConstantOperand::cast(source);
1261 Constant src = g.ToConstant(constant_source);
1262 if (destination->IsRegister() || destination->IsStackSlot()) {
1263 Register dst = destination->IsRegister() ? g.ToRegister(destination)
1265 switch (src.type()) {
1266 case Constant::kInt32:
1267 // TODO(dcarney): don't need scratch in this case.
1268 __ Set(dst, src.ToInt32());
1270 case Constant::kInt64:
1271 __ Set(dst, src.ToInt64());
1273 case Constant::kFloat32:
1275 isolate()->factory()->NewNumber(src.ToFloat32(), TENURED));
1277 case Constant::kFloat64:
1279 isolate()->factory()->NewNumber(src.ToFloat64(), TENURED));
1281 case Constant::kExternalReference:
1282 __ Move(dst, src.ToExternalReference());
1284 case Constant::kHeapObject:
1285 __ Move(dst, src.ToHeapObject());
1287 case Constant::kRpoNumber:
1288 UNREACHABLE(); // TODO(dcarney): load of labels on x64.
1291 if (destination->IsStackSlot()) {
1292 __ movq(g.ToOperand(destination), kScratchRegister);
1294 } else if (src.type() == Constant::kFloat32) {
1295 // TODO(turbofan): Can we do better here?
1296 uint32_t src_const = bit_cast<uint32_t>(src.ToFloat32());
1297 if (destination->IsDoubleRegister()) {
1298 __ Move(g.ToDoubleRegister(destination), src_const);
1300 DCHECK(destination->IsDoubleStackSlot());
1301 Operand dst = g.ToOperand(destination);
1302 __ movl(dst, Immediate(src_const));
1305 DCHECK_EQ(Constant::kFloat64, src.type());
1306 uint64_t src_const = bit_cast<uint64_t>(src.ToFloat64());
1307 if (destination->IsDoubleRegister()) {
1308 __ Move(g.ToDoubleRegister(destination), src_const);
1310 DCHECK(destination->IsDoubleStackSlot());
1311 __ movq(kScratchRegister, src_const);
1312 __ movq(g.ToOperand(destination), kScratchRegister);
1315 } else if (source->IsDoubleRegister()) {
1316 XMMRegister src = g.ToDoubleRegister(source);
1317 if (destination->IsDoubleRegister()) {
1318 XMMRegister dst = g.ToDoubleRegister(destination);
1321 DCHECK(destination->IsDoubleStackSlot());
1322 Operand dst = g.ToOperand(destination);
1325 } else if (source->IsDoubleStackSlot()) {
1326 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1327 Operand src = g.ToOperand(source);
1328 if (destination->IsDoubleRegister()) {
1329 XMMRegister dst = g.ToDoubleRegister(destination);
1332 // We rely on having xmm0 available as a fixed scratch register.
1333 Operand dst = g.ToOperand(destination);
1334 __ movsd(xmm0, src);
1335 __ movsd(dst, xmm0);
1343 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1344 InstructionOperand* destination) {
1345 X64OperandConverter g(this, NULL);
1346 // Dispatch on the source and destination operand kinds. Not all
1347 // combinations are possible.
1348 if (source->IsRegister() && destination->IsRegister()) {
1349 // Register-register.
1350 __ xchgq(g.ToRegister(source), g.ToRegister(destination));
1351 } else if (source->IsRegister() && destination->IsStackSlot()) {
1352 Register src = g.ToRegister(source);
1353 Operand dst = g.ToOperand(destination);
1355 } else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
1356 (source->IsDoubleStackSlot() &&
1357 destination->IsDoubleStackSlot())) {
1359 Register tmp = kScratchRegister;
1360 Operand src = g.ToOperand(source);
1361 Operand dst = g.ToOperand(destination);
1365 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
1366 // XMM register-register swap. We rely on having xmm0
1367 // available as a fixed scratch register.
1368 XMMRegister src = g.ToDoubleRegister(source);
1369 XMMRegister dst = g.ToDoubleRegister(destination);
1370 __ movsd(xmm0, src);
1372 __ movsd(dst, xmm0);
1373 } else if (source->IsDoubleRegister() && destination->IsDoubleStackSlot()) {
1374 // XMM register-memory swap. We rely on having xmm0
1375 // available as a fixed scratch register.
1376 XMMRegister src = g.ToDoubleRegister(source);
1377 Operand dst = g.ToOperand(destination);
1378 __ movsd(xmm0, src);
1380 __ movsd(dst, xmm0);
1382 // No other combinations are possible.
1388 void CodeGenerator::AddNopForSmiCodeInlining() { __ nop(); }
1391 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1392 int space_needed = Deoptimizer::patch_size();
1393 if (!info()->IsStub()) {
1394 // Ensure that we have enough space after the previous lazy-bailout
1395 // instruction for patching the code here.
1396 int current_pc = masm()->pc_offset();
1397 if (current_pc < last_lazy_deopt_pc_ + space_needed) {
1398 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1399 __ Nop(padding_size);
1402 MarkLazyDeoptSite();
1407 } // namespace internal
1408 } // namespace compiler