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 kUnorderedLessThan:
1046 __ j(parity_even, flabel, flabel_distance);
1048 case kUnsignedLessThan:
1049 __ j(below, tlabel);
1051 case kUnorderedGreaterThanOrEqual:
1052 __ j(parity_even, tlabel);
1054 case kUnsignedGreaterThanOrEqual:
1055 __ j(above_equal, tlabel);
1057 case kUnorderedLessThanOrEqual:
1058 __ j(parity_even, flabel, flabel_distance);
1060 case kUnsignedLessThanOrEqual:
1061 __ j(below_equal, tlabel);
1063 case kUnorderedGreaterThan:
1064 __ j(parity_even, tlabel);
1066 case kUnsignedGreaterThan:
1067 __ j(above, tlabel);
1070 __ j(overflow, tlabel);
1073 __ j(no_overflow, tlabel);
1076 if (!branch->fallthru) __ jmp(flabel, flabel_distance);
1080 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
1081 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target));
1085 // Assembles boolean materializations after this instruction.
1086 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
1087 FlagsCondition condition) {
1088 X64OperandConverter i(this, instr);
1091 // Materialize a full 64-bit 1 or 0 value. The result register is always the
1092 // last output of the instruction.
1094 DCHECK_NE(0, static_cast<int>(instr->OutputCount()));
1095 Register reg = i.OutputRegister(static_cast<int>(instr->OutputCount() - 1));
1096 Condition cc = no_condition;
1097 switch (condition) {
1098 case kUnorderedEqual:
1099 __ j(parity_odd, &check, Label::kNear);
1100 __ movl(reg, Immediate(0));
1101 __ jmp(&done, Label::kNear);
1106 case kUnorderedNotEqual:
1107 __ j(parity_odd, &check, Label::kNear);
1108 __ movl(reg, Immediate(1));
1109 __ jmp(&done, Label::kNear);
1114 case kSignedLessThan:
1117 case kSignedGreaterThanOrEqual:
1120 case kSignedLessThanOrEqual:
1123 case kSignedGreaterThan:
1126 case kUnorderedLessThan:
1127 __ j(parity_odd, &check, Label::kNear);
1128 __ movl(reg, Immediate(0));
1129 __ jmp(&done, Label::kNear);
1131 case kUnsignedLessThan:
1134 case kUnorderedGreaterThanOrEqual:
1135 __ j(parity_odd, &check, Label::kNear);
1136 __ movl(reg, Immediate(1));
1137 __ jmp(&done, Label::kNear);
1139 case kUnsignedGreaterThanOrEqual:
1142 case kUnorderedLessThanOrEqual:
1143 __ j(parity_odd, &check, Label::kNear);
1144 __ movl(reg, Immediate(0));
1145 __ jmp(&done, Label::kNear);
1147 case kUnsignedLessThanOrEqual:
1150 case kUnorderedGreaterThan:
1151 __ j(parity_odd, &check, Label::kNear);
1152 __ movl(reg, Immediate(1));
1153 __ jmp(&done, Label::kNear);
1155 case kUnsignedGreaterThan:
1167 __ movzxbl(reg, reg);
1172 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
1173 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
1174 isolate(), deoptimization_id, Deoptimizer::LAZY);
1175 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
1179 void CodeGenerator::AssemblePrologue() {
1180 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1181 int stack_slots = frame()->GetSpillSlotCount();
1182 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1185 const RegList saves = descriptor->CalleeSavedRegisters();
1186 if (saves != 0) { // Save callee-saved registers.
1187 int register_save_area_size = 0;
1188 for (int i = Register::kNumRegisters - 1; i >= 0; i--) {
1189 if (!((1 << i) & saves)) continue;
1190 __ pushq(Register::from_code(i));
1191 register_save_area_size += kPointerSize;
1193 frame()->SetRegisterSaveAreaSize(register_save_area_size);
1195 } else if (descriptor->IsJSFunctionCall()) {
1196 CompilationInfo* info = this->info();
1197 __ Prologue(info->IsCodePreAgingActive());
1198 frame()->SetRegisterSaveAreaSize(
1199 StandardFrameConstants::kFixedFrameSizeFromFp);
1202 frame()->SetRegisterSaveAreaSize(
1203 StandardFrameConstants::kFixedFrameSizeFromFp);
1206 if (info()->is_osr()) {
1207 // TurboFan OSR-compiled functions cannot be entered directly.
1208 __ Abort(kShouldNotDirectlyEnterOsrFunction);
1210 // Unoptimized code jumps directly to this entrypoint while the unoptimized
1211 // frame is still on the stack. Optimized code uses OSR values directly from
1212 // the unoptimized frame. Thus, all that needs to be done is to allocate the
1213 // remaining stack slots.
1214 if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
1215 osr_pc_offset_ = __ pc_offset();
1216 int unoptimized_slots =
1217 static_cast<int>(OsrHelper(info()).UnoptimizedFrameSlots());
1218 DCHECK(stack_slots >= unoptimized_slots);
1219 stack_slots -= unoptimized_slots;
1222 if (stack_slots > 0) {
1223 __ subq(rsp, Immediate(stack_slots * kPointerSize));
1228 void CodeGenerator::AssembleReturn() {
1229 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1230 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1231 if (frame()->GetRegisterSaveAreaSize() > 0) {
1232 // Remove this frame's spill slots first.
1233 int stack_slots = frame()->GetSpillSlotCount();
1234 if (stack_slots > 0) {
1235 __ addq(rsp, Immediate(stack_slots * kPointerSize));
1237 const RegList saves = descriptor->CalleeSavedRegisters();
1238 // Restore registers.
1240 for (int i = 0; i < Register::kNumRegisters; i++) {
1241 if (!((1 << i) & saves)) continue;
1242 __ popq(Register::from_code(i));
1245 __ popq(rbp); // Pop caller's frame pointer.
1248 // No saved registers.
1249 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1250 __ popq(rbp); // Pop caller's frame pointer.
1254 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1255 __ popq(rbp); // Pop caller's frame pointer.
1256 int pop_count = descriptor->IsJSFunctionCall()
1257 ? static_cast<int>(descriptor->JSParameterCount())
1259 __ ret(pop_count * kPointerSize);
1264 void CodeGenerator::AssembleMove(InstructionOperand* source,
1265 InstructionOperand* destination) {
1266 X64OperandConverter g(this, NULL);
1267 // Dispatch on the source and destination operand kinds. Not all
1268 // combinations are possible.
1269 if (source->IsRegister()) {
1270 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1271 Register src = g.ToRegister(source);
1272 if (destination->IsRegister()) {
1273 __ movq(g.ToRegister(destination), src);
1275 __ movq(g.ToOperand(destination), src);
1277 } else if (source->IsStackSlot()) {
1278 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1279 Operand src = g.ToOperand(source);
1280 if (destination->IsRegister()) {
1281 Register dst = g.ToRegister(destination);
1284 // Spill on demand to use a temporary register for memory-to-memory
1286 Register tmp = kScratchRegister;
1287 Operand dst = g.ToOperand(destination);
1291 } else if (source->IsConstant()) {
1292 ConstantOperand* constant_source = ConstantOperand::cast(source);
1293 Constant src = g.ToConstant(constant_source);
1294 if (destination->IsRegister() || destination->IsStackSlot()) {
1295 Register dst = destination->IsRegister() ? g.ToRegister(destination)
1297 switch (src.type()) {
1298 case Constant::kInt32:
1299 // TODO(dcarney): don't need scratch in this case.
1300 __ Set(dst, src.ToInt32());
1302 case Constant::kInt64:
1303 __ Set(dst, src.ToInt64());
1305 case Constant::kFloat32:
1307 isolate()->factory()->NewNumber(src.ToFloat32(), TENURED));
1309 case Constant::kFloat64:
1311 isolate()->factory()->NewNumber(src.ToFloat64(), TENURED));
1313 case Constant::kExternalReference:
1314 __ Move(dst, src.ToExternalReference());
1316 case Constant::kHeapObject:
1317 __ Move(dst, src.ToHeapObject());
1319 case Constant::kRpoNumber:
1320 UNREACHABLE(); // TODO(dcarney): load of labels on x64.
1323 if (destination->IsStackSlot()) {
1324 __ movq(g.ToOperand(destination), kScratchRegister);
1326 } else if (src.type() == Constant::kFloat32) {
1327 // TODO(turbofan): Can we do better here?
1328 uint32_t src_const = bit_cast<uint32_t>(src.ToFloat32());
1329 if (destination->IsDoubleRegister()) {
1330 __ Move(g.ToDoubleRegister(destination), src_const);
1332 DCHECK(destination->IsDoubleStackSlot());
1333 Operand dst = g.ToOperand(destination);
1334 __ movl(dst, Immediate(src_const));
1337 DCHECK_EQ(Constant::kFloat64, src.type());
1338 uint64_t src_const = bit_cast<uint64_t>(src.ToFloat64());
1339 if (destination->IsDoubleRegister()) {
1340 __ Move(g.ToDoubleRegister(destination), src_const);
1342 DCHECK(destination->IsDoubleStackSlot());
1343 __ movq(kScratchRegister, src_const);
1344 __ movq(g.ToOperand(destination), kScratchRegister);
1347 } else if (source->IsDoubleRegister()) {
1348 XMMRegister src = g.ToDoubleRegister(source);
1349 if (destination->IsDoubleRegister()) {
1350 XMMRegister dst = g.ToDoubleRegister(destination);
1353 DCHECK(destination->IsDoubleStackSlot());
1354 Operand dst = g.ToOperand(destination);
1357 } else if (source->IsDoubleStackSlot()) {
1358 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1359 Operand src = g.ToOperand(source);
1360 if (destination->IsDoubleRegister()) {
1361 XMMRegister dst = g.ToDoubleRegister(destination);
1364 // We rely on having xmm0 available as a fixed scratch register.
1365 Operand dst = g.ToOperand(destination);
1366 __ movsd(xmm0, src);
1367 __ movsd(dst, xmm0);
1375 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1376 InstructionOperand* destination) {
1377 X64OperandConverter g(this, NULL);
1378 // Dispatch on the source and destination operand kinds. Not all
1379 // combinations are possible.
1380 if (source->IsRegister() && destination->IsRegister()) {
1381 // Register-register.
1382 __ xchgq(g.ToRegister(source), g.ToRegister(destination));
1383 } else if (source->IsRegister() && destination->IsStackSlot()) {
1384 Register src = g.ToRegister(source);
1385 Operand dst = g.ToOperand(destination);
1387 } else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
1388 (source->IsDoubleStackSlot() &&
1389 destination->IsDoubleStackSlot())) {
1391 Register tmp = kScratchRegister;
1392 Operand src = g.ToOperand(source);
1393 Operand dst = g.ToOperand(destination);
1397 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
1398 // XMM register-register swap. We rely on having xmm0
1399 // available as a fixed scratch register.
1400 XMMRegister src = g.ToDoubleRegister(source);
1401 XMMRegister dst = g.ToDoubleRegister(destination);
1402 __ movsd(xmm0, src);
1404 __ movsd(dst, xmm0);
1405 } else if (source->IsDoubleRegister() && destination->IsDoubleStackSlot()) {
1406 // XMM register-memory swap. We rely on having xmm0
1407 // available as a fixed scratch register.
1408 XMMRegister src = g.ToDoubleRegister(source);
1409 Operand dst = g.ToOperand(destination);
1410 __ movsd(xmm0, src);
1412 __ movsd(dst, xmm0);
1414 // No other combinations are possible.
1420 void CodeGenerator::AddNopForSmiCodeInlining() { __ nop(); }
1423 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1424 int space_needed = Deoptimizer::patch_size();
1425 if (!info()->IsStub()) {
1426 // Ensure that we have enough space after the previous lazy-bailout
1427 // instruction for patching the code here.
1428 int current_pc = masm()->pc_offset();
1429 if (current_pc < last_lazy_deopt_pc_ + space_needed) {
1430 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1431 __ Nop(padding_size);
1434 MarkLazyDeoptSite();
1439 } // namespace internal
1440 } // namespace compiler