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/scopes.h"
12 #include "src/x64/assembler-x64.h"
13 #include "src/x64/macro-assembler-x64.h"
22 // Adds X64 specific methods for decoding operands.
23 class X64OperandConverter : public InstructionOperandConverter {
25 X64OperandConverter(CodeGenerator* gen, Instruction* instr)
26 : InstructionOperandConverter(gen, instr) {}
28 Immediate InputImmediate(int index) {
29 return ToImmediate(instr_->InputAt(index));
32 Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); }
34 Operand OutputOperand() { return ToOperand(instr_->Output()); }
36 Immediate ToImmediate(InstructionOperand* operand) {
37 return Immediate(ToConstant(operand).ToInt32());
40 Operand ToOperand(InstructionOperand* op, int extra = 0) {
41 DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot());
42 // The linkage computes where all spill slots are located.
43 FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), extra);
44 return Operand(offset.from_stack_pointer() ? rsp : rbp, offset.offset());
47 static int NextOffset(int* offset) {
53 static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
54 STATIC_ASSERT(0 == static_cast<int>(times_1));
55 STATIC_ASSERT(1 == static_cast<int>(times_2));
56 STATIC_ASSERT(2 == static_cast<int>(times_4));
57 STATIC_ASSERT(3 == static_cast<int>(times_8));
58 int scale = static_cast<int>(mode - one);
59 DCHECK(scale >= 0 && scale < 4);
60 return static_cast<ScaleFactor>(scale);
63 Operand MemoryOperand(int* offset) {
64 AddressingMode mode = AddressingModeField::decode(instr_->opcode());
67 Register base = InputRegister(NextOffset(offset));
69 return Operand(base, disp);
72 Register base = InputRegister(NextOffset(offset));
73 int32_t disp = InputInt32(NextOffset(offset));
74 return Operand(base, disp);
80 Register base = InputRegister(NextOffset(offset));
81 Register index = InputRegister(NextOffset(offset));
82 ScaleFactor scale = ScaleFor(kMode_MR1, mode);
84 return Operand(base, index, scale, disp);
90 Register base = InputRegister(NextOffset(offset));
91 Register index = InputRegister(NextOffset(offset));
92 ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
93 int32_t disp = InputInt32(NextOffset(offset));
94 return Operand(base, index, scale, disp);
97 Register base = InputRegister(NextOffset(offset));
99 return Operand(base, disp);
102 UNREACHABLE(); // Should use kModeMR with more compact encoding instead
103 return Operand(no_reg, 0);
106 Register index = InputRegister(NextOffset(offset));
107 ScaleFactor scale = ScaleFor(kMode_M1, mode);
109 return Operand(index, scale, disp);
115 Register index = InputRegister(NextOffset(offset));
116 ScaleFactor scale = ScaleFor(kMode_M1I, mode);
117 int32_t disp = InputInt32(NextOffset(offset));
118 return Operand(index, scale, disp);
122 return Operand(no_reg, 0);
125 return Operand(no_reg, 0);
128 Operand MemoryOperand(int first_input = 0) {
129 return MemoryOperand(&first_input);
136 bool HasImmediateInput(Instruction* instr, int index) {
137 return instr->InputAt(index)->IsImmediate();
141 class OutOfLineLoadZero FINAL : public OutOfLineCode {
143 OutOfLineLoadZero(CodeGenerator* gen, Register result)
144 : OutOfLineCode(gen), result_(result) {}
146 void Generate() FINAL { __ xorl(result_, result_); }
149 Register const result_;
153 class OutOfLineLoadNaN FINAL : public OutOfLineCode {
155 OutOfLineLoadNaN(CodeGenerator* gen, XMMRegister result)
156 : OutOfLineCode(gen), result_(result) {}
158 void Generate() FINAL { __ pcmpeqd(result_, result_); }
161 XMMRegister const result_;
165 class OutOfLineTruncateDoubleToI FINAL : public OutOfLineCode {
167 OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result,
169 : OutOfLineCode(gen), result_(result), input_(input) {}
171 void Generate() FINAL {
172 __ subp(rsp, Immediate(kDoubleSize));
173 __ movsd(MemOperand(rsp, 0), input_);
174 __ SlowTruncateToI(result_, rsp, 0);
175 __ addp(rsp, Immediate(kDoubleSize));
179 Register const result_;
180 XMMRegister const input_;
186 #define ASSEMBLE_UNOP(asm_instr) \
188 if (instr->Output()->IsRegister()) { \
189 __ asm_instr(i.OutputRegister()); \
191 __ asm_instr(i.OutputOperand()); \
196 #define ASSEMBLE_BINOP(asm_instr) \
198 if (HasImmediateInput(instr, 1)) { \
199 if (instr->InputAt(0)->IsRegister()) { \
200 __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \
202 __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
205 if (instr->InputAt(1)->IsRegister()) { \
206 __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \
208 __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
214 #define ASSEMBLE_MULT(asm_instr) \
216 if (HasImmediateInput(instr, 1)) { \
217 if (instr->InputAt(0)->IsRegister()) { \
218 __ asm_instr(i.OutputRegister(), i.InputRegister(0), \
219 i.InputImmediate(1)); \
221 __ asm_instr(i.OutputRegister(), i.InputOperand(0), \
222 i.InputImmediate(1)); \
225 if (instr->InputAt(1)->IsRegister()) { \
226 __ asm_instr(i.OutputRegister(), i.InputRegister(1)); \
228 __ asm_instr(i.OutputRegister(), i.InputOperand(1)); \
234 #define ASSEMBLE_SHIFT(asm_instr, width) \
236 if (HasImmediateInput(instr, 1)) { \
237 if (instr->Output()->IsRegister()) { \
238 __ asm_instr(i.OutputRegister(), Immediate(i.InputInt##width(1))); \
240 __ asm_instr(i.OutputOperand(), Immediate(i.InputInt##width(1))); \
243 if (instr->Output()->IsRegister()) { \
244 __ asm_instr##_cl(i.OutputRegister()); \
246 __ asm_instr##_cl(i.OutputOperand()); \
252 #define ASSEMBLE_DOUBLE_BINOP(asm_instr) \
254 if (instr->InputAt(1)->IsDoubleRegister()) { \
255 __ asm_instr(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); \
257 __ asm_instr(i.InputDoubleRegister(0), i.InputOperand(1)); \
262 #define ASSEMBLE_AVX_DOUBLE_BINOP(asm_instr) \
264 CpuFeatureScope avx_scope(masm(), AVX); \
265 if (instr->InputAt(1)->IsDoubleRegister()) { \
266 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
267 i.InputDoubleRegister(1)); \
269 __ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
270 i.InputOperand(1)); \
275 #define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr) \
277 auto result = i.OutputDoubleRegister(); \
278 auto buffer = i.InputRegister(0); \
279 auto index1 = i.InputRegister(1); \
280 auto index2 = i.InputInt32(2); \
281 OutOfLineCode* ool; \
282 if (instr->InputAt(3)->IsRegister()) { \
283 auto length = i.InputRegister(3); \
284 DCHECK_EQ(0, index2); \
285 __ cmpl(index1, length); \
286 ool = new (zone()) OutOfLineLoadNaN(this, result); \
288 auto length = i.InputInt32(3); \
289 DCHECK_LE(index2, length); \
290 __ cmpq(index1, Immediate(length - index2)); \
291 class OutOfLineLoadFloat FINAL : public OutOfLineCode { \
293 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result, \
294 Register buffer, Register index1, int32_t index2, \
296 : OutOfLineCode(gen), \
303 void Generate() FINAL { \
304 __ leal(kScratchRegister, Operand(index1_, index2_)); \
305 __ pcmpeqd(result_, result_); \
306 __ cmpl(kScratchRegister, Immediate(length_)); \
307 __ j(above_equal, exit()); \
308 __ asm_instr(result_, \
309 Operand(buffer_, kScratchRegister, times_1, 0)); \
313 XMMRegister const result_; \
314 Register const buffer_; \
315 Register const index1_; \
316 int32_t const index2_; \
317 int32_t const length_; \
320 OutOfLineLoadFloat(this, result, buffer, index1, index2, length); \
322 __ j(above_equal, ool->entry()); \
323 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
324 __ bind(ool->exit()); \
328 #define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \
330 auto result = i.OutputRegister(); \
331 auto buffer = i.InputRegister(0); \
332 auto index1 = i.InputRegister(1); \
333 auto index2 = i.InputInt32(2); \
334 OutOfLineCode* ool; \
335 if (instr->InputAt(3)->IsRegister()) { \
336 auto length = i.InputRegister(3); \
337 DCHECK_EQ(0, index2); \
338 __ cmpl(index1, length); \
339 ool = new (zone()) OutOfLineLoadZero(this, result); \
341 auto length = i.InputInt32(3); \
342 DCHECK_LE(index2, length); \
343 __ cmpq(index1, Immediate(length - index2)); \
344 class OutOfLineLoadInteger FINAL : public OutOfLineCode { \
346 OutOfLineLoadInteger(CodeGenerator* gen, Register result, \
347 Register buffer, Register index1, int32_t index2, \
349 : OutOfLineCode(gen), \
356 void Generate() FINAL { \
358 __ leal(kScratchRegister, Operand(index1_, index2_)); \
359 __ cmpl(kScratchRegister, Immediate(length_)); \
360 __ j(above_equal, &oob, Label::kNear); \
361 __ asm_instr(result_, \
362 Operand(buffer_, kScratchRegister, times_1, 0)); \
365 __ xorl(result_, result_); \
369 Register const result_; \
370 Register const buffer_; \
371 Register const index1_; \
372 int32_t const index2_; \
373 int32_t const length_; \
376 OutOfLineLoadInteger(this, result, buffer, index1, index2, length); \
378 __ j(above_equal, ool->entry()); \
379 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \
380 __ bind(ool->exit()); \
384 #define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \
386 auto buffer = i.InputRegister(0); \
387 auto index1 = i.InputRegister(1); \
388 auto index2 = i.InputInt32(2); \
389 auto value = i.InputDoubleRegister(4); \
390 if (instr->InputAt(3)->IsRegister()) { \
391 auto length = i.InputRegister(3); \
392 DCHECK_EQ(0, index2); \
394 __ cmpl(index1, length); \
395 __ j(above_equal, &done, Label::kNear); \
396 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
399 auto length = i.InputInt32(3); \
400 DCHECK_LE(index2, length); \
401 __ cmpq(index1, Immediate(length - index2)); \
402 class OutOfLineStoreFloat FINAL : public OutOfLineCode { \
404 OutOfLineStoreFloat(CodeGenerator* gen, Register buffer, \
405 Register index1, int32_t index2, int32_t length, \
407 : OutOfLineCode(gen), \
414 void Generate() FINAL { \
415 __ leal(kScratchRegister, Operand(index1_, index2_)); \
416 __ cmpl(kScratchRegister, Immediate(length_)); \
417 __ j(above_equal, exit()); \
418 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
423 Register const buffer_; \
424 Register const index1_; \
425 int32_t const index2_; \
426 int32_t const length_; \
427 XMMRegister const value_; \
429 auto ool = new (zone()) \
430 OutOfLineStoreFloat(this, buffer, index1, index2, length, value); \
431 __ j(above_equal, ool->entry()); \
432 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
433 __ bind(ool->exit()); \
438 #define ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Value) \
440 auto buffer = i.InputRegister(0); \
441 auto index1 = i.InputRegister(1); \
442 auto index2 = i.InputInt32(2); \
443 if (instr->InputAt(3)->IsRegister()) { \
444 auto length = i.InputRegister(3); \
445 DCHECK_EQ(0, index2); \
447 __ cmpl(index1, length); \
448 __ j(above_equal, &done, Label::kNear); \
449 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
452 auto length = i.InputInt32(3); \
453 DCHECK_LE(index2, length); \
454 __ cmpq(index1, Immediate(length - index2)); \
455 class OutOfLineStoreInteger FINAL : public OutOfLineCode { \
457 OutOfLineStoreInteger(CodeGenerator* gen, Register buffer, \
458 Register index1, int32_t index2, int32_t length, \
460 : OutOfLineCode(gen), \
467 void Generate() FINAL { \
468 __ leal(kScratchRegister, Operand(index1_, index2_)); \
469 __ cmpl(kScratchRegister, Immediate(length_)); \
470 __ j(above_equal, exit()); \
471 __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0), \
476 Register const buffer_; \
477 Register const index1_; \
478 int32_t const index2_; \
479 int32_t const length_; \
480 Value const value_; \
482 auto ool = new (zone()) \
483 OutOfLineStoreInteger(this, buffer, index1, index2, length, value); \
484 __ j(above_equal, ool->entry()); \
485 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \
486 __ bind(ool->exit()); \
491 #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \
493 if (instr->InputAt(4)->IsRegister()) { \
494 Register value = i.InputRegister(4); \
495 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Register); \
497 Immediate value = i.InputImmediate(4); \
498 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Immediate); \
503 // Assembles an instruction after register allocation, producing machine code.
504 void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
505 X64OperandConverter i(this, instr);
507 switch (ArchOpcodeField::decode(instr->opcode())) {
508 case kArchCallCodeObject: {
509 EnsureSpaceForLazyDeopt();
510 if (HasImmediateInput(instr, 0)) {
511 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
512 __ Call(code, RelocInfo::CODE_TARGET);
514 Register reg = i.InputRegister(0);
515 int entry = Code::kHeaderSize - kHeapObjectTag;
516 __ Call(Operand(reg, entry));
518 AddSafepointAndDeopt(instr);
521 case kArchCallJSFunction: {
522 EnsureSpaceForLazyDeopt();
523 Register func = i.InputRegister(0);
524 if (FLAG_debug_code) {
525 // Check the function's context matches the context argument.
526 __ cmpp(rsi, FieldOperand(func, JSFunction::kContextOffset));
527 __ Assert(equal, kWrongFunctionContext);
529 __ Call(FieldOperand(func, JSFunction::kCodeEntryOffset));
530 AddSafepointAndDeopt(instr);
534 AssembleArchJump(i.InputRpo(0));
537 // don't emit code for nops.
542 case kArchStackPointer:
543 __ movq(i.OutputRegister(), rsp);
545 case kArchTruncateDoubleToI: {
546 auto result = i.OutputRegister();
547 auto input = i.InputDoubleRegister(0);
548 auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input);
549 __ cvttsd2siq(result, input);
550 __ cmpq(result, Immediate(1));
551 __ j(overflow, ool->entry());
552 __ bind(ool->exit());
556 ASSEMBLE_BINOP(addl);
559 ASSEMBLE_BINOP(addq);
562 ASSEMBLE_BINOP(subl);
565 ASSEMBLE_BINOP(subq);
568 ASSEMBLE_BINOP(andl);
571 ASSEMBLE_BINOP(andq);
574 ASSEMBLE_BINOP(cmpl);
577 ASSEMBLE_BINOP(cmpq);
580 ASSEMBLE_BINOP(testl);
583 ASSEMBLE_BINOP(testq);
586 ASSEMBLE_MULT(imull);
589 ASSEMBLE_MULT(imulq);
592 if (instr->InputAt(1)->IsRegister()) {
593 __ imull(i.InputRegister(1));
595 __ imull(i.InputOperand(1));
599 if (instr->InputAt(1)->IsRegister()) {
600 __ mull(i.InputRegister(1));
602 __ mull(i.InputOperand(1));
607 __ idivl(i.InputRegister(1));
611 __ idivq(i.InputRegister(1));
615 __ divl(i.InputRegister(1));
619 __ divq(i.InputRegister(1));
640 ASSEMBLE_BINOP(xorl);
643 ASSEMBLE_BINOP(xorq);
646 ASSEMBLE_SHIFT(shll, 5);
649 ASSEMBLE_SHIFT(shlq, 6);
652 ASSEMBLE_SHIFT(shrl, 5);
655 ASSEMBLE_SHIFT(shrq, 6);
658 ASSEMBLE_SHIFT(sarl, 5);
661 ASSEMBLE_SHIFT(sarq, 6);
664 ASSEMBLE_SHIFT(rorl, 5);
667 ASSEMBLE_SHIFT(rorq, 6);
670 ASSEMBLE_DOUBLE_BINOP(ucomisd);
673 ASSEMBLE_DOUBLE_BINOP(addsd);
676 ASSEMBLE_DOUBLE_BINOP(subsd);
679 ASSEMBLE_DOUBLE_BINOP(mulsd);
682 ASSEMBLE_DOUBLE_BINOP(divsd);
684 case kSSEFloat64Mod: {
685 __ subq(rsp, Immediate(kDoubleSize));
686 // Move values to st(0) and st(1).
687 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(1));
688 __ fld_d(Operand(rsp, 0));
689 __ movsd(Operand(rsp, 0), i.InputDoubleRegister(0));
690 __ fld_d(Operand(rsp, 0));
691 // Loop while fprem isn't done.
694 // This instructions traps on all kinds inputs, but we are assuming the
695 // floating point control word is set to ignore them all.
697 // The following 2 instruction implicitly use rax.
699 if (CpuFeatures::IsSupported(SAHF)) {
700 CpuFeatureScope sahf_scope(masm(), SAHF);
703 __ shrl(rax, Immediate(8));
704 __ andl(rax, Immediate(0xFF));
708 __ j(parity_even, &mod_loop);
709 // Move output to stack and clean up.
711 __ fstp_d(Operand(rsp, 0));
712 __ movsd(i.OutputDoubleRegister(), Operand(rsp, 0));
713 __ addq(rsp, Immediate(kDoubleSize));
716 case kSSEFloat64Sqrt:
717 if (instr->InputAt(0)->IsDoubleRegister()) {
718 __ sqrtsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
720 __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0));
723 case kSSEFloat64Floor: {
724 CpuFeatureScope sse_scope(masm(), SSE4_1);
725 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
726 v8::internal::Assembler::kRoundDown);
729 case kSSEFloat64Ceil: {
730 CpuFeatureScope sse_scope(masm(), SSE4_1);
731 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
732 v8::internal::Assembler::kRoundUp);
735 case kSSEFloat64RoundTruncate: {
736 CpuFeatureScope sse_scope(masm(), SSE4_1);
737 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
738 v8::internal::Assembler::kRoundToZero);
742 if (instr->InputAt(0)->IsDoubleRegister()) {
743 __ cvtss2sd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
745 __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0));
749 if (instr->InputAt(0)->IsDoubleRegister()) {
750 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
752 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0));
755 case kSSEFloat64ToInt32:
756 if (instr->InputAt(0)->IsDoubleRegister()) {
757 __ cvttsd2si(i.OutputRegister(), i.InputDoubleRegister(0));
759 __ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
762 case kSSEFloat64ToUint32: {
763 if (instr->InputAt(0)->IsDoubleRegister()) {
764 __ cvttsd2siq(i.OutputRegister(), i.InputDoubleRegister(0));
766 __ cvttsd2siq(i.OutputRegister(), i.InputOperand(0));
768 __ AssertZeroExtended(i.OutputRegister());
771 case kSSEInt32ToFloat64:
772 if (instr->InputAt(0)->IsRegister()) {
773 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputRegister(0));
775 __ cvtlsi2sd(i.OutputDoubleRegister(), i.InputOperand(0));
778 case kSSEUint32ToFloat64:
779 if (instr->InputAt(0)->IsRegister()) {
780 __ movl(kScratchRegister, i.InputRegister(0));
782 __ movl(kScratchRegister, i.InputOperand(0));
784 __ cvtqsi2sd(i.OutputDoubleRegister(), kScratchRegister);
787 ASSEMBLE_AVX_DOUBLE_BINOP(vaddsd);
790 ASSEMBLE_AVX_DOUBLE_BINOP(vsubsd);
793 ASSEMBLE_AVX_DOUBLE_BINOP(vmulsd);
796 ASSEMBLE_AVX_DOUBLE_BINOP(vdivsd);
799 if (instr->addressing_mode() != kMode_None) {
800 __ movsxbl(i.OutputRegister(), i.MemoryOperand());
801 } else if (instr->InputAt(0)->IsRegister()) {
802 __ movsxbl(i.OutputRegister(), i.InputRegister(0));
804 __ movsxbl(i.OutputRegister(), i.InputOperand(0));
806 __ AssertZeroExtended(i.OutputRegister());
809 __ movzxbl(i.OutputRegister(), i.MemoryOperand());
813 Operand operand = i.MemoryOperand(&index);
814 if (HasImmediateInput(instr, index)) {
815 __ movb(operand, Immediate(i.InputInt8(index)));
817 __ movb(operand, i.InputRegister(index));
822 if (instr->addressing_mode() != kMode_None) {
823 __ movsxwl(i.OutputRegister(), i.MemoryOperand());
824 } else if (instr->InputAt(0)->IsRegister()) {
825 __ movsxwl(i.OutputRegister(), i.InputRegister(0));
827 __ movsxwl(i.OutputRegister(), i.InputOperand(0));
829 __ AssertZeroExtended(i.OutputRegister());
832 __ movzxwl(i.OutputRegister(), i.MemoryOperand());
833 __ AssertZeroExtended(i.OutputRegister());
837 Operand operand = i.MemoryOperand(&index);
838 if (HasImmediateInput(instr, index)) {
839 __ movw(operand, Immediate(i.InputInt16(index)));
841 __ movw(operand, i.InputRegister(index));
846 if (instr->HasOutput()) {
847 if (instr->addressing_mode() == kMode_None) {
848 if (instr->InputAt(0)->IsRegister()) {
849 __ movl(i.OutputRegister(), i.InputRegister(0));
851 __ movl(i.OutputRegister(), i.InputOperand(0));
854 __ movl(i.OutputRegister(), i.MemoryOperand());
856 __ AssertZeroExtended(i.OutputRegister());
859 Operand operand = i.MemoryOperand(&index);
860 if (HasImmediateInput(instr, index)) {
861 __ movl(operand, i.InputImmediate(index));
863 __ movl(operand, i.InputRegister(index));
868 if (instr->InputAt(0)->IsRegister()) {
869 __ movsxlq(i.OutputRegister(), i.InputRegister(0));
871 __ movsxlq(i.OutputRegister(), i.InputOperand(0));
876 if (instr->HasOutput()) {
877 __ movq(i.OutputRegister(), i.MemoryOperand());
880 Operand operand = i.MemoryOperand(&index);
881 if (HasImmediateInput(instr, index)) {
882 __ movq(operand, i.InputImmediate(index));
884 __ movq(operand, i.InputRegister(index));
889 if (instr->HasOutput()) {
890 __ movss(i.OutputDoubleRegister(), i.MemoryOperand());
893 Operand operand = i.MemoryOperand(&index);
894 __ movss(operand, i.InputDoubleRegister(index));
898 if (instr->HasOutput()) {
899 __ movsd(i.OutputDoubleRegister(), i.MemoryOperand());
902 Operand operand = i.MemoryOperand(&index);
903 __ movsd(operand, i.InputDoubleRegister(index));
907 AddressingMode mode = AddressingModeField::decode(instr->opcode());
908 // Shorten "leal" to "addl", "subl" or "shll" if the register allocation
909 // and addressing mode just happens to work out. The "addl"/"subl" forms
910 // in these cases are faster based on measurements.
911 if (i.InputRegister(0).is(i.OutputRegister())) {
912 if (mode == kMode_MRI) {
913 int32_t constant_summand = i.InputInt32(1);
914 if (constant_summand > 0) {
915 __ addl(i.OutputRegister(), Immediate(constant_summand));
916 } else if (constant_summand < 0) {
917 __ subl(i.OutputRegister(), Immediate(-constant_summand));
919 } else if (mode == kMode_MR1) {
920 if (i.InputRegister(1).is(i.OutputRegister())) {
921 __ shll(i.OutputRegister(), Immediate(1));
923 __ leal(i.OutputRegister(), i.MemoryOperand());
925 } else if (mode == kMode_M2) {
926 __ shll(i.OutputRegister(), Immediate(1));
927 } else if (mode == kMode_M4) {
928 __ shll(i.OutputRegister(), Immediate(2));
929 } else if (mode == kMode_M8) {
930 __ shll(i.OutputRegister(), Immediate(3));
932 __ leal(i.OutputRegister(), i.MemoryOperand());
935 __ leal(i.OutputRegister(), i.MemoryOperand());
937 __ AssertZeroExtended(i.OutputRegister());
941 __ leaq(i.OutputRegister(), i.MemoryOperand());
944 __ decl(i.OutputRegister());
947 __ incl(i.OutputRegister());
950 if (HasImmediateInput(instr, 0)) {
951 __ pushq(i.InputImmediate(0));
953 if (instr->InputAt(0)->IsRegister()) {
954 __ pushq(i.InputRegister(0));
956 __ pushq(i.InputOperand(0));
960 case kX64StoreWriteBarrier: {
961 Register object = i.InputRegister(0);
962 Register index = i.InputRegister(1);
963 Register value = i.InputRegister(2);
964 __ movsxlq(index, index);
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 kUnorderedLessThan:
1045 __ j(parity_even, flabel, flabel_distance);
1047 case kUnsignedLessThan:
1048 __ j(below, tlabel);
1050 case kUnorderedGreaterThanOrEqual:
1051 __ j(parity_even, tlabel);
1053 case kUnsignedGreaterThanOrEqual:
1054 __ j(above_equal, tlabel);
1056 case kUnorderedLessThanOrEqual:
1057 __ j(parity_even, flabel, flabel_distance);
1059 case kUnsignedLessThanOrEqual:
1060 __ j(below_equal, tlabel);
1062 case kUnorderedGreaterThan:
1063 __ j(parity_even, tlabel);
1065 case kUnsignedGreaterThan:
1066 __ j(above, tlabel);
1069 __ j(overflow, tlabel);
1072 __ j(no_overflow, tlabel);
1075 if (!branch->fallthru) __ jmp(flabel, flabel_distance);
1079 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
1080 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target));
1084 // Assembles boolean materializations after this instruction.
1085 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
1086 FlagsCondition condition) {
1087 X64OperandConverter i(this, instr);
1090 // Materialize a full 64-bit 1 or 0 value. The result register is always the
1091 // last output of the instruction.
1093 DCHECK_NE(0, static_cast<int>(instr->OutputCount()));
1094 Register reg = i.OutputRegister(static_cast<int>(instr->OutputCount() - 1));
1095 Condition cc = no_condition;
1096 switch (condition) {
1097 case kUnorderedEqual:
1098 __ j(parity_odd, &check, Label::kNear);
1099 __ movl(reg, Immediate(0));
1100 __ jmp(&done, Label::kNear);
1105 case kUnorderedNotEqual:
1106 __ j(parity_odd, &check, Label::kNear);
1107 __ movl(reg, Immediate(1));
1108 __ jmp(&done, Label::kNear);
1113 case kSignedLessThan:
1116 case kSignedGreaterThanOrEqual:
1119 case kSignedLessThanOrEqual:
1122 case kSignedGreaterThan:
1125 case kUnorderedLessThan:
1126 __ j(parity_odd, &check, Label::kNear);
1127 __ movl(reg, Immediate(0));
1128 __ jmp(&done, Label::kNear);
1130 case kUnsignedLessThan:
1133 case kUnorderedGreaterThanOrEqual:
1134 __ j(parity_odd, &check, Label::kNear);
1135 __ movl(reg, Immediate(1));
1136 __ jmp(&done, Label::kNear);
1138 case kUnsignedGreaterThanOrEqual:
1141 case kUnorderedLessThanOrEqual:
1142 __ j(parity_odd, &check, Label::kNear);
1143 __ movl(reg, Immediate(0));
1144 __ jmp(&done, Label::kNear);
1146 case kUnsignedLessThanOrEqual:
1149 case kUnorderedGreaterThan:
1150 __ j(parity_odd, &check, Label::kNear);
1151 __ movl(reg, Immediate(1));
1152 __ jmp(&done, Label::kNear);
1154 case kUnsignedGreaterThan:
1166 __ movzxbl(reg, reg);
1171 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
1172 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
1173 isolate(), deoptimization_id, Deoptimizer::LAZY);
1174 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
1178 void CodeGenerator::AssemblePrologue() {
1179 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1180 int stack_slots = frame()->GetSpillSlotCount();
1181 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1184 const RegList saves = descriptor->CalleeSavedRegisters();
1185 if (saves != 0) { // Save callee-saved registers.
1186 int register_save_area_size = 0;
1187 for (int i = Register::kNumRegisters - 1; i >= 0; i--) {
1188 if (!((1 << i) & saves)) continue;
1189 __ pushq(Register::from_code(i));
1190 register_save_area_size += kPointerSize;
1192 frame()->SetRegisterSaveAreaSize(register_save_area_size);
1194 } else if (descriptor->IsJSFunctionCall()) {
1195 CompilationInfo* info = this->info();
1196 __ Prologue(info->IsCodePreAgingActive());
1197 frame()->SetRegisterSaveAreaSize(
1198 StandardFrameConstants::kFixedFrameSizeFromFp);
1201 frame()->SetRegisterSaveAreaSize(
1202 StandardFrameConstants::kFixedFrameSizeFromFp);
1204 if (stack_slots > 0) {
1205 __ subq(rsp, Immediate(stack_slots * kPointerSize));
1210 void CodeGenerator::AssembleReturn() {
1211 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1212 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1213 if (frame()->GetRegisterSaveAreaSize() > 0) {
1214 // Remove this frame's spill slots first.
1215 int stack_slots = frame()->GetSpillSlotCount();
1216 if (stack_slots > 0) {
1217 __ addq(rsp, Immediate(stack_slots * kPointerSize));
1219 const RegList saves = descriptor->CalleeSavedRegisters();
1220 // Restore registers.
1222 for (int i = 0; i < Register::kNumRegisters; i++) {
1223 if (!((1 << i) & saves)) continue;
1224 __ popq(Register::from_code(i));
1227 __ popq(rbp); // Pop caller's frame pointer.
1230 // No saved registers.
1231 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1232 __ popq(rbp); // Pop caller's frame pointer.
1236 __ movq(rsp, rbp); // Move stack pointer back to frame pointer.
1237 __ popq(rbp); // Pop caller's frame pointer.
1238 int pop_count = descriptor->IsJSFunctionCall()
1239 ? static_cast<int>(descriptor->JSParameterCount())
1241 __ ret(pop_count * kPointerSize);
1246 void CodeGenerator::AssembleMove(InstructionOperand* source,
1247 InstructionOperand* destination) {
1248 X64OperandConverter g(this, NULL);
1249 // Dispatch on the source and destination operand kinds. Not all
1250 // combinations are possible.
1251 if (source->IsRegister()) {
1252 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1253 Register src = g.ToRegister(source);
1254 if (destination->IsRegister()) {
1255 __ movq(g.ToRegister(destination), src);
1257 __ movq(g.ToOperand(destination), src);
1259 } else if (source->IsStackSlot()) {
1260 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1261 Operand src = g.ToOperand(source);
1262 if (destination->IsRegister()) {
1263 Register dst = g.ToRegister(destination);
1266 // Spill on demand to use a temporary register for memory-to-memory
1268 Register tmp = kScratchRegister;
1269 Operand dst = g.ToOperand(destination);
1273 } else if (source->IsConstant()) {
1274 ConstantOperand* constant_source = ConstantOperand::cast(source);
1275 Constant src = g.ToConstant(constant_source);
1276 if (destination->IsRegister() || destination->IsStackSlot()) {
1277 Register dst = destination->IsRegister() ? g.ToRegister(destination)
1279 switch (src.type()) {
1280 case Constant::kInt32:
1281 // TODO(dcarney): don't need scratch in this case.
1282 __ Set(dst, src.ToInt32());
1284 case Constant::kInt64:
1285 __ Set(dst, src.ToInt64());
1287 case Constant::kFloat32:
1289 isolate()->factory()->NewNumber(src.ToFloat32(), TENURED));
1291 case Constant::kFloat64:
1293 isolate()->factory()->NewNumber(src.ToFloat64(), TENURED));
1295 case Constant::kExternalReference:
1296 __ Move(dst, src.ToExternalReference());
1298 case Constant::kHeapObject:
1299 __ Move(dst, src.ToHeapObject());
1301 case Constant::kRpoNumber:
1302 UNREACHABLE(); // TODO(dcarney): load of labels on x64.
1305 if (destination->IsStackSlot()) {
1306 __ movq(g.ToOperand(destination), kScratchRegister);
1308 } else if (src.type() == Constant::kFloat32) {
1309 // TODO(turbofan): Can we do better here?
1310 uint32_t src_const = bit_cast<uint32_t>(src.ToFloat32());
1311 if (destination->IsDoubleRegister()) {
1312 __ Move(g.ToDoubleRegister(destination), src_const);
1314 DCHECK(destination->IsDoubleStackSlot());
1315 Operand dst = g.ToOperand(destination);
1316 __ movl(dst, Immediate(src_const));
1319 DCHECK_EQ(Constant::kFloat64, src.type());
1320 uint64_t src_const = bit_cast<uint64_t>(src.ToFloat64());
1321 if (destination->IsDoubleRegister()) {
1322 __ Move(g.ToDoubleRegister(destination), src_const);
1324 DCHECK(destination->IsDoubleStackSlot());
1325 __ movq(kScratchRegister, src_const);
1326 __ movq(g.ToOperand(destination), kScratchRegister);
1329 } else if (source->IsDoubleRegister()) {
1330 XMMRegister src = g.ToDoubleRegister(source);
1331 if (destination->IsDoubleRegister()) {
1332 XMMRegister dst = g.ToDoubleRegister(destination);
1335 DCHECK(destination->IsDoubleStackSlot());
1336 Operand dst = g.ToOperand(destination);
1339 } else if (source->IsDoubleStackSlot()) {
1340 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1341 Operand src = g.ToOperand(source);
1342 if (destination->IsDoubleRegister()) {
1343 XMMRegister dst = g.ToDoubleRegister(destination);
1346 // We rely on having xmm0 available as a fixed scratch register.
1347 Operand dst = g.ToOperand(destination);
1348 __ movsd(xmm0, src);
1349 __ movsd(dst, xmm0);
1357 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1358 InstructionOperand* destination) {
1359 X64OperandConverter g(this, NULL);
1360 // Dispatch on the source and destination operand kinds. Not all
1361 // combinations are possible.
1362 if (source->IsRegister() && destination->IsRegister()) {
1363 // Register-register.
1364 __ xchgq(g.ToRegister(source), g.ToRegister(destination));
1365 } else if (source->IsRegister() && destination->IsStackSlot()) {
1366 Register src = g.ToRegister(source);
1367 Operand dst = g.ToOperand(destination);
1369 } else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
1370 (source->IsDoubleStackSlot() &&
1371 destination->IsDoubleStackSlot())) {
1373 Register tmp = kScratchRegister;
1374 Operand src = g.ToOperand(source);
1375 Operand dst = g.ToOperand(destination);
1379 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
1380 // XMM register-register swap. We rely on having xmm0
1381 // available as a fixed scratch register.
1382 XMMRegister src = g.ToDoubleRegister(source);
1383 XMMRegister dst = g.ToDoubleRegister(destination);
1384 __ movsd(xmm0, src);
1386 __ movsd(dst, xmm0);
1387 } else if (source->IsDoubleRegister() && destination->IsDoubleStackSlot()) {
1388 // XMM register-memory swap. We rely on having xmm0
1389 // available as a fixed scratch register.
1390 XMMRegister src = g.ToDoubleRegister(source);
1391 Operand dst = g.ToOperand(destination);
1392 __ movsd(xmm0, src);
1394 __ movsd(dst, xmm0);
1396 // No other combinations are possible.
1402 void CodeGenerator::AddNopForSmiCodeInlining() { __ nop(); }
1405 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1406 int space_needed = Deoptimizer::patch_size();
1407 if (!info()->IsStub()) {
1408 // Ensure that we have enough space after the previous lazy-bailout
1409 // instruction for patching the code here.
1410 int current_pc = masm()->pc_offset();
1411 if (current_pc < last_lazy_deopt_pc_ + space_needed) {
1412 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1413 __ Nop(padding_size);
1416 MarkLazyDeoptSite();
1421 } // namespace internal
1422 } // namespace compiler