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/scopes.h"
11 #include "src/x64/assembler-x64.h"
12 #include "src/x64/macro-assembler-x64.h"
21 // Adds X64 specific methods for decoding operands.
22 class X64OperandConverter : public InstructionOperandConverter {
24 X64OperandConverter(CodeGenerator* gen, Instruction* instr)
25 : InstructionOperandConverter(gen, instr) {}
27 Immediate InputImmediate(int index) {
28 return ToImmediate(instr_->InputAt(index));
31 Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); }
33 Operand OutputOperand() { return ToOperand(instr_->Output()); }
35 Immediate ToImmediate(InstructionOperand* operand) {
36 return Immediate(ToConstant(operand).ToInt32());
39 Operand ToOperand(InstructionOperand* op, int extra = 0) {
40 DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot());
41 // The linkage computes where all spill slots are located.
42 FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), extra);
43 return Operand(offset.from_stack_pointer() ? rsp : rbp, offset.offset());
46 static int NextOffset(int* offset) {
52 static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
53 STATIC_ASSERT(0 == static_cast<int>(times_1));
54 STATIC_ASSERT(1 == static_cast<int>(times_2));
55 STATIC_ASSERT(2 == static_cast<int>(times_4));
56 STATIC_ASSERT(3 == static_cast<int>(times_8));
57 int scale = static_cast<int>(mode - one);
58 DCHECK(scale >= 0 && scale < 4);
59 return static_cast<ScaleFactor>(scale);
62 Operand MemoryOperand(int* offset) {
63 AddressingMode mode = AddressingModeField::decode(instr_->opcode());
66 Register base = InputRegister(NextOffset(offset));
68 return Operand(base, disp);
71 Register base = InputRegister(NextOffset(offset));
72 int32_t disp = InputInt32(NextOffset(offset));
73 return Operand(base, disp);
79 Register base = InputRegister(NextOffset(offset));
80 Register index = InputRegister(NextOffset(offset));
81 ScaleFactor scale = ScaleFor(kMode_MR1, mode);
83 return Operand(base, index, scale, disp);
89 Register base = InputRegister(NextOffset(offset));
90 Register index = InputRegister(NextOffset(offset));
91 ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
92 int32_t disp = InputInt32(NextOffset(offset));
93 return Operand(base, index, scale, disp);
96 Register base = InputRegister(NextOffset(offset));
98 return Operand(base, disp);
101 UNREACHABLE(); // Should use kModeMR with more compact encoding instead
102 return Operand(no_reg, 0);
105 Register index = InputRegister(NextOffset(offset));
106 ScaleFactor scale = ScaleFor(kMode_M1, mode);
108 return Operand(index, scale, disp);
114 Register index = InputRegister(NextOffset(offset));
115 ScaleFactor scale = ScaleFor(kMode_M1I, mode);
116 int32_t disp = InputInt32(NextOffset(offset));
117 return Operand(index, scale, disp);
121 return Operand(no_reg, 0);
124 return Operand(no_reg, 0);
127 Operand MemoryOperand(int first_input = 0) {
128 return MemoryOperand(&first_input);
135 bool HasImmediateInput(Instruction* instr, int index) {
136 return instr->InputAt(index)->IsImmediate();
140 class OutOfLineLoadZero FINAL : public OutOfLineCode {
142 OutOfLineLoadZero(CodeGenerator* gen, Register result)
143 : OutOfLineCode(gen), result_(result) {}
145 void Generate() FINAL { __ xorl(result_, result_); }
148 Register const result_;
152 class OutOfLineLoadNaN FINAL : public OutOfLineCode {
154 OutOfLineLoadNaN(CodeGenerator* gen, XMMRegister result)
155 : OutOfLineCode(gen), result_(result) {}
157 void Generate() FINAL { __ pcmpeqd(result_, result_); }
160 XMMRegister const result_;
164 class OutOfLineTruncateDoubleToI FINAL : public OutOfLineCode {
166 OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result,
168 : OutOfLineCode(gen), result_(result), input_(input) {}
170 void Generate() FINAL {
171 __ subp(rsp, Immediate(kDoubleSize));
172 __ movsd(MemOperand(rsp, 0), input_);
173 __ SlowTruncateToI(result_, rsp, 0);
174 __ addp(rsp, Immediate(kDoubleSize));
178 Register const result_;
179 XMMRegister const input_;
185 #define ASSEMBLE_UNOP(asm_instr) \
187 if (instr->Output()->IsRegister()) { \
188 __ asm_instr(i.OutputRegister()); \
190 __ asm_instr(i.OutputOperand()); \
195 #define ASSEMBLE_BINOP(asm_instr) \
197 if (HasImmediateInput(instr, 1)) { \
198 if (instr->InputAt(0)->IsRegister()) { \
199 __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \
201 __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
204 if (instr->InputAt(1)->IsRegister()) { \
205 __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \
207 __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
213 #define ASSEMBLE_MULT(asm_instr) \
215 if (HasImmediateInput(instr, 1)) { \
216 if (instr->InputAt(0)->IsRegister()) { \
217 __ asm_instr(i.OutputRegister(), i.InputRegister(0), \
218 i.InputImmediate(1)); \
220 __ asm_instr(i.OutputRegister(), i.InputOperand(0), \
221 i.InputImmediate(1)); \
224 if (instr->InputAt(1)->IsRegister()) { \
225 __ asm_instr(i.OutputRegister(), i.InputRegister(1)); \
227 __ asm_instr(i.OutputRegister(), i.InputOperand(1)); \
233 #define ASSEMBLE_SHIFT(asm_instr, width) \
235 if (HasImmediateInput(instr, 1)) { \
236 if (instr->Output()->IsRegister()) { \
237 __ asm_instr(i.OutputRegister(), Immediate(i.InputInt##width(1))); \
239 __ asm_instr(i.OutputOperand(), Immediate(i.InputInt##width(1))); \
242 if (instr->Output()->IsRegister()) { \
243 __ asm_instr##_cl(i.OutputRegister()); \
245 __ asm_instr##_cl(i.OutputOperand()); \
251 #define ASSEMBLE_MOVX(asm_instr) \
253 if (instr->addressing_mode() != kMode_None) { \
254 __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \
255 } else if (instr->InputAt(0)->IsRegister()) { \
256 __ asm_instr(i.OutputRegister(), i.InputRegister(0)); \
258 __ asm_instr(i.OutputRegister(), i.InputOperand(0)); \
263 #define ASSEMBLE_DOUBLE_BINOP(asm_instr) \
265 if (instr->InputAt(1)->IsDoubleRegister()) { \
266 __ asm_instr(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); \
268 __ asm_instr(i.InputDoubleRegister(0), i.InputOperand(1)); \
273 #define ASSEMBLE_AVX_DOUBLE_BINOP(asm_instr) \
275 CpuFeatureScope avx_scope(masm(), AVX); \
276 if (instr->InputAt(1)->IsDoubleRegister()) { \
277 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
278 i.InputDoubleRegister(1)); \
280 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
281 i.InputOperand(1)); \
286 #define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr) \
288 auto result = i.OutputDoubleRegister(); \
289 auto buffer = i.InputRegister(0); \
290 auto index1 = i.InputRegister(1); \
291 auto index2 = i.InputInt32(2); \
292 OutOfLineCode* ool; \
293 if (instr->InputAt(3)->IsRegister()) { \
294 auto length = i.InputRegister(3); \
295 DCHECK_EQ(0, index2); \
296 __ cmpl(index1, length); \
297 ool = new (zone()) OutOfLineLoadNaN(this, result); \
299 auto length = i.InputInt32(3); \
300 DCHECK_LE(index2, length); \
301 __ cmpq(index1, Immediate(length - index2)); \
302 class OutOfLineLoadFloat FINAL : public OutOfLineCode { \
304 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result, \
305 Register buffer, Register index1, int32_t index2, \
307 : OutOfLineCode(gen), \
314 void Generate() FINAL { \
315 __ leal(kScratchRegister, Operand(index1_, index2_)); \
316 __ pcmpeqd(result_, result_); \
317 __ cmpl(kScratchRegister, Immediate(length_)); \
318 __ j(above_equal, exit()); \
319 __ asm_instr(result_, \
320 Operand(buffer_, kScratchRegister, times_1, 0)); \
324 XMMRegister const result_; \
325 Register const buffer_; \
326 Register const index1_; \
327 int32_t const index2_; \
328 int32_t const length_; \
331 OutOfLineLoadFloat(this, result, buffer, index1, index2, length); \
333 __ j(above_equal, ool->entry()); \
334 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
335 __ bind(ool->exit()); \
339 #define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \
341 auto result = i.OutputRegister(); \
342 auto buffer = i.InputRegister(0); \
343 auto index1 = i.InputRegister(1); \
344 auto index2 = i.InputInt32(2); \
345 OutOfLineCode* ool; \
346 if (instr->InputAt(3)->IsRegister()) { \
347 auto length = i.InputRegister(3); \
348 DCHECK_EQ(0, index2); \
349 __ cmpl(index1, length); \
350 ool = new (zone()) OutOfLineLoadZero(this, result); \
352 auto length = i.InputInt32(3); \
353 DCHECK_LE(index2, length); \
354 __ cmpq(index1, Immediate(length - index2)); \
355 class OutOfLineLoadInteger FINAL : public OutOfLineCode { \
357 OutOfLineLoadInteger(CodeGenerator* gen, Register result, \
358 Register buffer, Register index1, int32_t index2, \
360 : OutOfLineCode(gen), \
367 void Generate() FINAL { \
369 __ leal(kScratchRegister, Operand(index1_, index2_)); \
370 __ cmpl(kScratchRegister, Immediate(length_)); \
371 __ j(above_equal, &oob, Label::kNear); \
372 __ asm_instr(result_, \
373 Operand(buffer_, kScratchRegister, times_1, 0)); \
376 __ xorl(result_, result_); \
380 Register const result_; \
381 Register const buffer_; \
382 Register const index1_; \
383 int32_t const index2_; \
384 int32_t const length_; \
387 OutOfLineLoadInteger(this, result, buffer, index1, index2, length); \
389 __ j(above_equal, ool->entry()); \
390 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
391 __ bind(ool->exit()); \
395 #define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \
397 auto buffer = i.InputRegister(0); \
398 auto index1 = i.InputRegister(1); \
399 auto index2 = i.InputInt32(2); \
400 auto value = i.InputDoubleRegister(4); \
401 if (instr->InputAt(3)->IsRegister()) { \
402 auto length = i.InputRegister(3); \
403 DCHECK_EQ(0, index2); \
405 __ cmpl(index1, length); \
406 __ j(above_equal, &done, Label::kNear); \
407 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
410 auto length = i.InputInt32(3); \
411 DCHECK_LE(index2, length); \
412 __ cmpq(index1, Immediate(length - index2)); \
413 class OutOfLineStoreFloat FINAL : public OutOfLineCode { \
415 OutOfLineStoreFloat(CodeGenerator* gen, Register buffer, \
416 Register index1, int32_t index2, int32_t length, \
418 : OutOfLineCode(gen), \
425 void Generate() FINAL { \
426 __ leal(kScratchRegister, Operand(index1_, index2_)); \
427 __ cmpl(kScratchRegister, Immediate(length_)); \
428 __ j(above_equal, exit()); \
429 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
434 Register const buffer_; \
435 Register const index1_; \
436 int32_t const index2_; \
437 int32_t const length_; \
438 XMMRegister const value_; \
440 auto ool = new (zone()) \
441 OutOfLineStoreFloat(this, buffer, index1, index2, length, value); \
442 __ j(above_equal, ool->entry()); \
443 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
444 __ bind(ool->exit()); \
449 #define ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Value) \
451 auto buffer = i.InputRegister(0); \
452 auto index1 = i.InputRegister(1); \
453 auto index2 = i.InputInt32(2); \
454 if (instr->InputAt(3)->IsRegister()) { \
455 auto length = i.InputRegister(3); \
456 DCHECK_EQ(0, index2); \
458 __ cmpl(index1, length); \
459 __ j(above_equal, &done, Label::kNear); \
460 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
463 auto length = i.InputInt32(3); \
464 DCHECK_LE(index2, length); \
465 __ cmpq(index1, Immediate(length - index2)); \
466 class OutOfLineStoreInteger FINAL : public OutOfLineCode { \
468 OutOfLineStoreInteger(CodeGenerator* gen, Register buffer, \
469 Register index1, int32_t index2, int32_t length, \
471 : OutOfLineCode(gen), \
478 void Generate() FINAL { \
479 __ leal(kScratchRegister, Operand(index1_, index2_)); \
480 __ cmpl(kScratchRegister, Immediate(length_)); \
481 __ j(above_equal, exit()); \
482 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
487 Register const buffer_; \
488 Register const index1_; \
489 int32_t const index2_; \
490 int32_t const length_; \
491 Value const value_; \
493 auto ool = new (zone()) \
494 OutOfLineStoreInteger(this, buffer, index1, index2, length, value); \
495 __ j(above_equal, ool->entry()); \
496 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
497 __ bind(ool->exit()); \
502 #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \
504 if (instr->InputAt(4)->IsRegister()) { \
505 Register value = i.InputRegister(4); \
506 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Register); \
508 Immediate value = i.InputImmediate(4); \
509 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Immediate); \
514 // Assembles an instruction after register allocation, producing machine code.
515 void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
516 X64OperandConverter i(this, instr);
518 switch (ArchOpcodeField::decode(instr->opcode())) {
519 case kArchCallCodeObject: {
520 EnsureSpaceForLazyDeopt();
521 if (HasImmediateInput(instr, 0)) {
522 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
523 __ Call(code, RelocInfo::CODE_TARGET);
525 Register reg = i.InputRegister(0);
526 int entry = Code::kHeaderSize - kHeapObjectTag;
527 __ Call(Operand(reg, entry));
529 AddSafepointAndDeopt(instr);
532 case kArchCallJSFunction: {
533 EnsureSpaceForLazyDeopt();
534 Register func = i.InputRegister(0);
535 if (FLAG_debug_code) {
536 // Check the function's context matches the context argument.
537 __ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset));
538 __ Assert(equal, kWrongFunctionContext);
540 __ Call(FieldOperand(func, JSFunction::kCodeEntryOffset));
541 AddSafepointAndDeopt(instr);
545 AssembleArchJump(i.InputRpo(0));
547 case kArchLookupSwitch:
548 AssembleArchLookupSwitch(instr);
550 case kArchTableSwitch:
551 AssembleArchTableSwitch(instr);
554 // don't emit code for nops.
559 case kArchStackPointer:
560 __ movq(i.OutputRegister(), rsp);
562 case kArchTruncateDoubleToI: {
563 auto result = i.OutputRegister();
564 auto input = i.InputDoubleRegister(0);
565 auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input);
566 __ cvttsd2siq(result, input);
567 __ cmpq(result, Immediate(1));
568 __ j(overflow, ool->entry());
569 __ bind(ool->exit());
573 ASSEMBLE_BINOP(addl);
576 ASSEMBLE_BINOP(addq);
579 ASSEMBLE_BINOP(subl);
582 ASSEMBLE_BINOP(subq);
585 ASSEMBLE_BINOP(andl);
588 ASSEMBLE_BINOP(andq);
591 ASSEMBLE_BINOP(cmpl);
594 ASSEMBLE_BINOP(cmpq);
597 ASSEMBLE_BINOP(testl);
600 ASSEMBLE_BINOP(testq);
603 ASSEMBLE_MULT(imull);
606 ASSEMBLE_MULT(imulq);
609 if (instr->InputAt(1)->IsRegister()) {
610 __ imull(i.InputRegister(1));
612 __ imull(i.InputOperand(1));
616 if (instr->InputAt(1)->IsRegister()) {
617 __ mull(i.InputRegister(1));
619 __ mull(i.InputOperand(1));
624 __ idivl(i.InputRegister(1));
628 __ idivq(i.InputRegister(1));
632 __ divl(i.InputRegister(1));
636 __ divq(i.InputRegister(1));
657 ASSEMBLE_BINOP(xorl);
660 ASSEMBLE_BINOP(xorq);
663 ASSEMBLE_SHIFT(shll, 5);
666 ASSEMBLE_SHIFT(shlq, 6);
669 ASSEMBLE_SHIFT(shrl, 5);
672 ASSEMBLE_SHIFT(shrq, 6);
675 ASSEMBLE_SHIFT(sarl, 5);
678 ASSEMBLE_SHIFT(sarq, 6);
681 ASSEMBLE_SHIFT(rorl, 5);
684 ASSEMBLE_SHIFT(rorq, 6);
687 ASSEMBLE_DOUBLE_BINOP(ucomisd);
690 ASSEMBLE_DOUBLE_BINOP(addsd);
693 ASSEMBLE_DOUBLE_BINOP(subsd);
696 ASSEMBLE_DOUBLE_BINOP(mulsd);
699 ASSEMBLE_DOUBLE_BINOP(divsd);
701 case kSSEFloat64Mod: {
702 __ subq(rsp, Immediate(kDoubleSize));
703 // Move values to st(0) and st(1).
704 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(1));
705 __ fld_d(Operand(rsp, 0));
706 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(0));
707 __ fld_d(Operand(rsp, 0));
708 // Loop while fprem isn't done.
711 // This instructions traps on all kinds inputs, but we are assuming the
712 // floating point control word is set to ignore them all.
714 // The following 2 instruction implicitly use rax.
716 if (CpuFeatures::IsSupported(SAHF)) {
717 CpuFeatureScope sahf_scope(masm(), SAHF);
720 __ shrl(rax, Immediate(8));
721 __ andl(rax, Immediate(0xFF));
725 __ j(parity_even, &mod_loop);
726 // Move output to stack and clean up.
728 __ fstp_d(Operand(rsp, 0));
729 __ movsd(i.OutputDoubleRegister(), Operand(rsp, 0));
730 __ addq(rsp, Immediate(kDoubleSize));
733 case kSSEFloat64Sqrt:
734 if (instr->InputAt(0)->IsDoubleRegister()) {
735 __ sqrtsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
737 __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0));
740 case kSSEFloat64Floor: {
741 CpuFeatureScope sse_scope(masm(), SSE4_1);
742 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
743 v8::internal::Assembler::kRoundDown);
746 case kSSEFloat64Ceil: {
747 CpuFeatureScope sse_scope(masm(), SSE4_1);
748 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
749 v8::internal::Assembler::kRoundUp);
752 case kSSEFloat64RoundTruncate: {
753 CpuFeatureScope sse_scope(masm(), SSE4_1);
754 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
755 v8::internal::Assembler::kRoundToZero);
759 if (instr->InputAt(0)->IsDoubleRegister()) {
760 __ cvtss2sd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
762 __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0));
766 if (instr->InputAt(0)->IsDoubleRegister()) {
767 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
769 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0));
772 case kSSEFloat64ToInt32:
773 if (instr->InputAt(0)->IsDoubleRegister()) {
774 __ cvttsd2si(i.OutputRegister(), i.InputDoubleRegister(0));
776 __ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
779 case kSSEFloat64ToUint32: {
780 if (instr->InputAt(0)->IsDoubleRegister()) {
781 __ cvttsd2siq(i.OutputRegister(), i.InputDoubleRegister(0));
783 __ cvttsd2siq(i.OutputRegister(), i.InputOperand(0));
785 __ AssertZeroExtended(i.OutputRegister());
788 case kSSEInt32ToFloat64:
789 if (instr->InputAt(0)->IsRegister()) {
790 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputRegister(0));
792 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputOperand(0));
795 case kSSEUint32ToFloat64:
796 if (instr->InputAt(0)->IsRegister()) {
797 __ movl(kScratchRegister, i.InputRegister(0));
799 __ movl(kScratchRegister, i.InputOperand(0));
801 __ cvtqsi2sd(i.OutputDoubleRegister(), kScratchRegister);
804 ASSEMBLE_AVX_DOUBLE_BINOP(vaddsd);
807 ASSEMBLE_AVX_DOUBLE_BINOP(vsubsd);
810 ASSEMBLE_AVX_DOUBLE_BINOP(vmulsd);
813 ASSEMBLE_AVX_DOUBLE_BINOP(vdivsd);
816 ASSEMBLE_MOVX(movsxbl);
817 __ AssertZeroExtended(i.OutputRegister());
820 ASSEMBLE_MOVX(movzxbl);
821 __ AssertZeroExtended(i.OutputRegister());
825 Operand operand = i.MemoryOperand(&index);
826 if (HasImmediateInput(instr, index)) {
827 __ movb(operand, Immediate(i.InputInt8(index)));
829 __ movb(operand, i.InputRegister(index));
834 ASSEMBLE_MOVX(movsxwl);
835 __ AssertZeroExtended(i.OutputRegister());
838 ASSEMBLE_MOVX(movzxwl);
839 __ AssertZeroExtended(i.OutputRegister());
843 Operand operand = i.MemoryOperand(&index);
844 if (HasImmediateInput(instr, index)) {
845 __ movw(operand, Immediate(i.InputInt16(index)));
847 __ movw(operand, i.InputRegister(index));
852 if (instr->HasOutput()) {
853 if (instr->addressing_mode() == kMode_None) {
854 if (instr->InputAt(0)->IsRegister()) {
855 __ movl(i.OutputRegister(), i.InputRegister(0));
857 __ movl(i.OutputRegister(), i.InputOperand(0));
860 __ movl(i.OutputRegister(), i.MemoryOperand());
862 __ AssertZeroExtended(i.OutputRegister());
865 Operand operand = i.MemoryOperand(&index);
866 if (HasImmediateInput(instr, index)) {
867 __ movl(operand, i.InputImmediate(index));
869 __ movl(operand, i.InputRegister(index));
874 ASSEMBLE_MOVX(movsxlq);
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 __ movq(Operand(object, index, times_1, 0), value);
966 __ leaq(index, Operand(object, index, times_1, 0));
967 SaveFPRegsMode mode =
968 frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
969 __ RecordWrite(object, index, value, mode);
972 case kCheckedLoadInt8:
973 ASSEMBLE_CHECKED_LOAD_INTEGER(movsxbl);
975 case kCheckedLoadUint8:
976 ASSEMBLE_CHECKED_LOAD_INTEGER(movzxbl);
978 case kCheckedLoadInt16:
979 ASSEMBLE_CHECKED_LOAD_INTEGER(movsxwl);
981 case kCheckedLoadUint16:
982 ASSEMBLE_CHECKED_LOAD_INTEGER(movzxwl);
984 case kCheckedLoadWord32:
985 ASSEMBLE_CHECKED_LOAD_INTEGER(movl);
987 case kCheckedLoadFloat32:
988 ASSEMBLE_CHECKED_LOAD_FLOAT(movss);
990 case kCheckedLoadFloat64:
991 ASSEMBLE_CHECKED_LOAD_FLOAT(movsd);
993 case kCheckedStoreWord8:
994 ASSEMBLE_CHECKED_STORE_INTEGER(movb);
996 case kCheckedStoreWord16:
997 ASSEMBLE_CHECKED_STORE_INTEGER(movw);
999 case kCheckedStoreWord32:
1000 ASSEMBLE_CHECKED_STORE_INTEGER(movl);
1002 case kCheckedStoreFloat32:
1003 ASSEMBLE_CHECKED_STORE_FLOAT(movss);
1005 case kCheckedStoreFloat64:
1006 ASSEMBLE_CHECKED_STORE_FLOAT(movsd);
1012 // Assembles branches after this instruction.
1013 void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
1014 X64OperandConverter i(this, instr);
1015 Label::Distance flabel_distance =
1016 branch->fallthru ? Label::kNear : Label::kFar;
1017 Label* tlabel = branch->true_label;
1018 Label* flabel = branch->false_label;
1019 switch (branch->condition) {
1020 case kUnorderedEqual:
1021 __ j(parity_even, flabel, flabel_distance);
1024 __ j(equal, tlabel);
1026 case kUnorderedNotEqual:
1027 __ j(parity_even, tlabel);
1030 __ j(not_equal, tlabel);
1032 case kSignedLessThan:
1035 case kSignedGreaterThanOrEqual:
1036 __ j(greater_equal, tlabel);
1038 case kSignedLessThanOrEqual:
1039 __ j(less_equal, tlabel);
1041 case kSignedGreaterThan:
1042 __ j(greater, tlabel);
1044 case kUnsignedLessThan:
1045 __ j(below, tlabel);
1047 case kUnsignedGreaterThanOrEqual:
1048 __ j(above_equal, tlabel);
1050 case kUnsignedLessThanOrEqual:
1051 __ j(below_equal, tlabel);
1053 case kUnsignedGreaterThan:
1054 __ j(above, tlabel);
1057 __ j(overflow, tlabel);
1060 __ j(no_overflow, tlabel);
1063 if (!branch->fallthru) __ jmp(flabel, flabel_distance);
1067 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
1068 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target));
1072 // Assembles boolean materializations after this instruction.
1073 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
1074 FlagsCondition condition) {
1075 X64OperandConverter i(this, instr);
1078 // Materialize a full 64-bit 1 or 0 value. The result register is always the
1079 // last output of the instruction.
1081 DCHECK_NE(0, static_cast<int>(instr->OutputCount()));
1082 Register reg = i.OutputRegister(static_cast<int>(instr->OutputCount() - 1));
1083 Condition cc = no_condition;
1084 switch (condition) {
1085 case kUnorderedEqual:
1086 __ j(parity_odd, &check, Label::kNear);
1087 __ movl(reg, Immediate(0));
1088 __ jmp(&done, Label::kNear);
1093 case kUnorderedNotEqual:
1094 __ j(parity_odd, &check, Label::kNear);
1095 __ movl(reg, Immediate(1));
1096 __ jmp(&done, Label::kNear);
1101 case kSignedLessThan:
1104 case kSignedGreaterThanOrEqual:
1107 case kSignedLessThanOrEqual:
1110 case kSignedGreaterThan:
1113 case kUnsignedLessThan:
1116 case kUnsignedGreaterThanOrEqual:
1119 case kUnsignedLessThanOrEqual:
1122 case kUnsignedGreaterThan:
1134 __ movzxbl(reg, reg);
1139 void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
1140 X64OperandConverter i(this, instr);
1141 Register input = i.InputRegister(0);
1142 for (size_t index = 2; index < instr->InputCount(); index += 2) {
1143 __ cmpl(input, Immediate(i.InputInt32(static_cast<int>(index + 0))));
1144 __ j(equal, GetLabel(i.InputRpo(static_cast<int>(index + 1))));
1146 AssembleArchJump(i.InputRpo(1));
1150 void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
1151 X64OperandConverter i(this, instr);
1152 Register input = i.InputRegister(0);
1153 int32_t const case_count = static_cast<int32_t>(instr->InputCount() - 2);
1154 Label** cases = zone()->NewArray<Label*>(case_count);
1155 for (int32_t index = 0; index < case_count; ++index) {
1156 cases[index] = GetLabel(i.InputRpo(index + 2));
1158 Label* const table = AddJumpTable(cases, case_count);
1159 __ cmpl(input, Immediate(case_count));
1160 __ j(above_equal, GetLabel(i.InputRpo(1)));
1161 __ leaq(kScratchRegister, Operand(table));
1162 __ jmp(Operand(kScratchRegister, input, times_8, 0));
1166 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
1167 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
1168 isolate(), deoptimization_id, Deoptimizer::LAZY);
1169 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
1173 void CodeGenerator::AssemblePrologue() {
1174 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1175 int stack_slots = frame()->GetSpillSlotCount();
1176 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1179 const RegList saves = descriptor->CalleeSavedRegisters();
1180 if (saves != 0) { // Save callee-saved registers.
1181 int register_save_area_size = 0;
1182 for (int i = Register::kNumRegisters - 1; i >= 0; i--) {
1183 if (!((1 << i) & saves)) continue;
1184 __ pushq(Register::from_code(i));
1185 register_save_area_size += kPointerSize;
1187 frame()->SetRegisterSaveAreaSize(register_save_area_size);
1189 } else if (descriptor->IsJSFunctionCall()) {
1190 CompilationInfo* info = this->info();
1191 __ Prologue(info->IsCodePreAgingActive());
1192 frame()->SetRegisterSaveAreaSize(
1193 StandardFrameConstants::kFixedFrameSizeFromFp);
1194 } else if (stack_slots > 0) {
1196 frame()->SetRegisterSaveAreaSize(
1197 StandardFrameConstants::kFixedFrameSizeFromFp);
1200 if (info()->is_osr()) {
1201 // TurboFan OSR-compiled functions cannot be entered directly.
1202 __ Abort(kShouldNotDirectlyEnterOsrFunction);
1204 // Unoptimized code jumps directly to this entrypoint while the unoptimized
1205 // frame is still on the stack. Optimized code uses OSR values directly from
1206 // the unoptimized frame. Thus, all that needs to be done is to allocate the
1207 // remaining stack slots.
1208 if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
1209 osr_pc_offset_ = __ pc_offset();
1210 DCHECK(stack_slots >= frame()->GetOsrStackSlotCount());
1211 stack_slots -= frame()->GetOsrStackSlotCount();
1214 if (stack_slots > 0) {
1215 __ subq(rsp, Immediate(stack_slots * kPointerSize));
1220 void CodeGenerator::AssembleReturn() {
1221 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1222 int stack_slots = frame()->GetSpillSlotCount();
1223 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1224 if (frame()->GetRegisterSaveAreaSize() > 0) {
1225 // Remove this frame's spill slots first.
1226 if (stack_slots > 0) {
1227 __ addq(rsp, Immediate(stack_slots * kPointerSize));
1229 const RegList saves = descriptor->CalleeSavedRegisters();
1230 // Restore registers.
1232 for (int i = 0; i < Register::kNumRegisters; i++) {
1233 if (!((1 << i) & saves)) continue;
1234 __ popq(Register::from_code(i));
1237 __ popq(rbp); // Pop caller's frame pointer.
1240 // No saved registers.
1241 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1242 __ popq(rbp); // Pop caller's frame pointer.
1245 } else if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
1246 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1247 __ popq(rbp); // Pop caller's frame pointer.
1248 int pop_count = descriptor->IsJSFunctionCall()
1249 ? static_cast<int>(descriptor->JSParameterCount())
1251 __ ret(pop_count * kPointerSize);
1258 void CodeGenerator::AssembleMove(InstructionOperand* source,
1259 InstructionOperand* destination) {
1260 X64OperandConverter g(this, NULL);
1261 // Dispatch on the source and destination operand kinds. Not all
1262 // combinations are possible.
1263 if (source->IsRegister()) {
1264 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1265 Register src = g.ToRegister(source);
1266 if (destination->IsRegister()) {
1267 __ movq(g.ToRegister(destination), src);
1269 __ movq(g.ToOperand(destination), src);
1271 } else if (source->IsStackSlot()) {
1272 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1273 Operand src = g.ToOperand(source);
1274 if (destination->IsRegister()) {
1275 Register dst = g.ToRegister(destination);
1278 // Spill on demand to use a temporary register for memory-to-memory
1280 Register tmp = kScratchRegister;
1281 Operand dst = g.ToOperand(destination);
1285 } else if (source->IsConstant()) {
1286 ConstantOperand* constant_source = ConstantOperand::cast(source);
1287 Constant src = g.ToConstant(constant_source);
1288 if (destination->IsRegister() || destination->IsStackSlot()) {
1289 Register dst = destination->IsRegister() ? g.ToRegister(destination)
1291 switch (src.type()) {
1292 case Constant::kInt32:
1293 // TODO(dcarney): don't need scratch in this case.
1294 __ Set(dst, src.ToInt32());
1296 case Constant::kInt64:
1297 __ Set(dst, src.ToInt64());
1299 case Constant::kFloat32:
1301 isolate()->factory()->NewNumber(src.ToFloat32(), TENURED));
1303 case Constant::kFloat64:
1305 isolate()->factory()->NewNumber(src.ToFloat64(), TENURED));
1307 case Constant::kExternalReference:
1308 __ Move(dst, src.ToExternalReference());
1310 case Constant::kHeapObject:
1311 __ Move(dst, src.ToHeapObject());
1313 case Constant::kRpoNumber:
1314 UNREACHABLE(); // TODO(dcarney): load of labels on x64.
1317 if (destination->IsStackSlot()) {
1318 __ movq(g.ToOperand(destination), kScratchRegister);
1320 } else if (src.type() == Constant::kFloat32) {
1321 // TODO(turbofan): Can we do better here?
1322 uint32_t src_const = bit_cast<uint32_t>(src.ToFloat32());
1323 if (destination->IsDoubleRegister()) {
1324 __ Move(g.ToDoubleRegister(destination), src_const);
1326 DCHECK(destination->IsDoubleStackSlot());
1327 Operand dst = g.ToOperand(destination);
1328 __ movl(dst, Immediate(src_const));
1331 DCHECK_EQ(Constant::kFloat64, src.type());
1332 uint64_t src_const = bit_cast<uint64_t>(src.ToFloat64());
1333 if (destination->IsDoubleRegister()) {
1334 __ Move(g.ToDoubleRegister(destination), src_const);
1336 DCHECK(destination->IsDoubleStackSlot());
1337 __ movq(kScratchRegister, src_const);
1338 __ movq(g.ToOperand(destination), kScratchRegister);
1341 } else if (source->IsDoubleRegister()) {
1342 XMMRegister src = g.ToDoubleRegister(source);
1343 if (destination->IsDoubleRegister()) {
1344 XMMRegister dst = g.ToDoubleRegister(destination);
1347 DCHECK(destination->IsDoubleStackSlot());
1348 Operand dst = g.ToOperand(destination);
1351 } else if (source->IsDoubleStackSlot()) {
1352 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1353 Operand src = g.ToOperand(source);
1354 if (destination->IsDoubleRegister()) {
1355 XMMRegister dst = g.ToDoubleRegister(destination);
1358 // We rely on having xmm0 available as a fixed scratch register.
1359 Operand dst = g.ToOperand(destination);
1360 __ movsd(xmm0, src);
1361 __ movsd(dst, xmm0);
1369 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1370 InstructionOperand* destination) {
1371 X64OperandConverter g(this, NULL);
1372 // Dispatch on the source and destination operand kinds. Not all
1373 // combinations are possible.
1374 if (source->IsRegister() && destination->IsRegister()) {
1375 // Register-register.
1376 __ xchgq(g.ToRegister(source), g.ToRegister(destination));
1377 } else if (source->IsRegister() && destination->IsStackSlot()) {
1378 Register src = g.ToRegister(source);
1379 Operand dst = g.ToOperand(destination);
1381 } else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
1382 (source->IsDoubleStackSlot() &&
1383 destination->IsDoubleStackSlot())) {
1385 Register tmp = kScratchRegister;
1386 Operand src = g.ToOperand(source);
1387 Operand dst = g.ToOperand(destination);
1391 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
1392 // XMM register-register swap. We rely on having xmm0
1393 // available as a fixed scratch register.
1394 XMMRegister src = g.ToDoubleRegister(source);
1395 XMMRegister dst = g.ToDoubleRegister(destination);
1396 __ movsd(xmm0, src);
1398 __ movsd(dst, xmm0);
1399 } else if (source->IsDoubleRegister() && destination->IsDoubleStackSlot()) {
1400 // XMM register-memory swap. We rely on having xmm0
1401 // available as a fixed scratch register.
1402 XMMRegister src = g.ToDoubleRegister(source);
1403 Operand dst = g.ToOperand(destination);
1404 __ movsd(xmm0, src);
1406 __ movsd(dst, xmm0);
1408 // No other combinations are possible.
1414 void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
1415 for (size_t index = 0; index < target_count; ++index) {
1416 __ dq(targets[index]);
1421 void CodeGenerator::AddNopForSmiCodeInlining() { __ nop(); }
1424 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1425 int space_needed = Deoptimizer::patch_size();
1426 if (!info()->IsStub()) {
1427 // Ensure that we have enough space after the previous lazy-bailout
1428 // instruction for patching the code here.
1429 int current_pc = masm()->pc_offset();
1430 if (current_pc < last_lazy_deopt_pc_ + space_needed) {
1431 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1432 __ Nop(padding_size);
1435 MarkLazyDeoptSite();
1440 } // namespace internal
1441 } // namespace compiler