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/ia32/assembler-ia32.h"
11 #include "src/ia32/macro-assembler-ia32.h"
12 #include "src/scopes.h"
21 // Adds IA-32 specific methods for decoding operands.
22 class IA32OperandConverter : public InstructionOperandConverter {
24 IA32OperandConverter(CodeGenerator* gen, Instruction* instr)
25 : InstructionOperandConverter(gen, instr) {}
27 Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); }
29 Immediate InputImmediate(int index) {
30 return ToImmediate(instr_->InputAt(index));
33 Operand OutputOperand() { return ToOperand(instr_->Output()); }
35 Operand ToOperand(InstructionOperand* op, int extra = 0) {
36 if (op->IsRegister()) {
38 return Operand(ToRegister(op));
39 } else if (op->IsDoubleRegister()) {
41 return Operand(ToDoubleRegister(op));
43 DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot());
44 // The linkage computes where all spill slots are located.
45 FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), extra);
46 return Operand(offset.from_stack_pointer() ? esp : ebp, offset.offset());
49 Operand HighOperand(InstructionOperand* op) {
50 DCHECK(op->IsDoubleStackSlot());
51 return ToOperand(op, kPointerSize);
54 Immediate ToImmediate(InstructionOperand* operand) {
55 Constant constant = ToConstant(operand);
56 switch (constant.type()) {
57 case Constant::kInt32:
58 return Immediate(constant.ToInt32());
59 case Constant::kFloat32:
61 isolate()->factory()->NewNumber(constant.ToFloat32(), TENURED));
62 case Constant::kFloat64:
64 isolate()->factory()->NewNumber(constant.ToFloat64(), TENURED));
65 case Constant::kExternalReference:
66 return Immediate(constant.ToExternalReference());
67 case Constant::kHeapObject:
68 return Immediate(constant.ToHeapObject());
69 case Constant::kInt64:
71 case Constant::kRpoNumber:
72 return Immediate::CodeRelativeOffset(ToLabel(operand));
78 static int NextOffset(int* offset) {
84 static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
85 STATIC_ASSERT(0 == static_cast<int>(times_1));
86 STATIC_ASSERT(1 == static_cast<int>(times_2));
87 STATIC_ASSERT(2 == static_cast<int>(times_4));
88 STATIC_ASSERT(3 == static_cast<int>(times_8));
89 int scale = static_cast<int>(mode - one);
90 DCHECK(scale >= 0 && scale < 4);
91 return static_cast<ScaleFactor>(scale);
94 Operand MemoryOperand(int* offset) {
95 AddressingMode mode = AddressingModeField::decode(instr_->opcode());
98 Register base = InputRegister(NextOffset(offset));
100 return Operand(base, disp);
103 Register base = InputRegister(NextOffset(offset));
104 int32_t disp = InputInt32(NextOffset(offset));
105 return Operand(base, disp);
111 Register base = InputRegister(NextOffset(offset));
112 Register index = InputRegister(NextOffset(offset));
113 ScaleFactor scale = ScaleFor(kMode_MR1, mode);
115 return Operand(base, index, scale, disp);
121 Register base = InputRegister(NextOffset(offset));
122 Register index = InputRegister(NextOffset(offset));
123 ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
124 int32_t disp = InputInt32(NextOffset(offset));
125 return Operand(base, index, scale, disp);
131 Register index = InputRegister(NextOffset(offset));
132 ScaleFactor scale = ScaleFor(kMode_M1, mode);
134 return Operand(index, scale, disp);
140 Register index = InputRegister(NextOffset(offset));
141 ScaleFactor scale = ScaleFor(kMode_M1I, mode);
142 int32_t disp = InputInt32(NextOffset(offset));
143 return Operand(index, scale, disp);
146 int32_t disp = InputInt32(NextOffset(offset));
147 return Operand(Immediate(disp));
151 return Operand(no_reg, 0);
154 return Operand(no_reg, 0);
157 Operand MemoryOperand(int first_input = 0) {
158 return MemoryOperand(&first_input);
165 bool HasImmediateInput(Instruction* instr, int index) {
166 return instr->InputAt(index)->IsImmediate();
170 class OutOfLineLoadInteger FINAL : public OutOfLineCode {
172 OutOfLineLoadInteger(CodeGenerator* gen, Register result)
173 : OutOfLineCode(gen), result_(result) {}
175 void Generate() FINAL { __ xor_(result_, result_); }
178 Register const result_;
182 class OutOfLineLoadFloat FINAL : public OutOfLineCode {
184 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result)
185 : OutOfLineCode(gen), result_(result) {}
187 void Generate() FINAL { __ pcmpeqd(result_, result_); }
190 XMMRegister const result_;
194 class OutOfLineTruncateDoubleToI FINAL : public OutOfLineCode {
196 OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result,
198 : OutOfLineCode(gen), result_(result), input_(input) {}
200 void Generate() FINAL {
201 __ sub(esp, Immediate(kDoubleSize));
202 __ movsd(MemOperand(esp, 0), input_);
203 __ SlowTruncateToI(result_, esp, 0);
204 __ add(esp, Immediate(kDoubleSize));
208 Register const result_;
209 XMMRegister const input_;
215 #define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr) \
217 auto result = i.OutputDoubleRegister(); \
218 auto offset = i.InputRegister(0); \
219 if (instr->InputAt(1)->IsRegister()) { \
220 __ cmp(offset, i.InputRegister(1)); \
222 __ cmp(offset, i.InputImmediate(1)); \
224 OutOfLineCode* ool = new (zone()) OutOfLineLoadFloat(this, result); \
225 __ j(above_equal, ool->entry()); \
226 __ asm_instr(result, i.MemoryOperand(2)); \
227 __ bind(ool->exit()); \
231 #define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \
233 auto result = i.OutputRegister(); \
234 auto offset = i.InputRegister(0); \
235 if (instr->InputAt(1)->IsRegister()) { \
236 __ cmp(offset, i.InputRegister(1)); \
238 __ cmp(offset, i.InputImmediate(1)); \
240 OutOfLineCode* ool = new (zone()) OutOfLineLoadInteger(this, result); \
241 __ j(above_equal, ool->entry()); \
242 __ asm_instr(result, i.MemoryOperand(2)); \
243 __ bind(ool->exit()); \
247 #define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \
249 auto offset = i.InputRegister(0); \
250 if (instr->InputAt(1)->IsRegister()) { \
251 __ cmp(offset, i.InputRegister(1)); \
253 __ cmp(offset, i.InputImmediate(1)); \
256 __ j(above_equal, &done, Label::kNear); \
257 __ asm_instr(i.MemoryOperand(3), i.InputDoubleRegister(2)); \
262 #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \
264 auto offset = i.InputRegister(0); \
265 if (instr->InputAt(1)->IsRegister()) { \
266 __ cmp(offset, i.InputRegister(1)); \
268 __ cmp(offset, i.InputImmediate(1)); \
271 __ j(above_equal, &done, Label::kNear); \
272 if (instr->InputAt(2)->IsRegister()) { \
273 __ asm_instr(i.MemoryOperand(3), i.InputRegister(2)); \
275 __ asm_instr(i.MemoryOperand(3), i.InputImmediate(2)); \
281 // Assembles an instruction after register allocation, producing machine code.
282 void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
283 IA32OperandConverter i(this, instr);
285 switch (ArchOpcodeField::decode(instr->opcode())) {
286 case kArchCallCodeObject: {
287 EnsureSpaceForLazyDeopt();
288 if (HasImmediateInput(instr, 0)) {
289 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0));
290 __ call(code, RelocInfo::CODE_TARGET);
292 Register reg = i.InputRegister(0);
293 __ call(Operand(reg, Code::kHeaderSize - kHeapObjectTag));
295 AddSafepointAndDeopt(instr);
298 case kArchCallJSFunction: {
299 EnsureSpaceForLazyDeopt();
300 Register func = i.InputRegister(0);
301 if (FLAG_debug_code) {
302 // Check the function's context matches the context argument.
303 __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset));
304 __ Assert(equal, kWrongFunctionContext);
306 __ call(FieldOperand(func, JSFunction::kCodeEntryOffset));
307 AddSafepointAndDeopt(instr);
311 AssembleArchJump(i.InputRpo(0));
313 case kArchLookupSwitch:
314 AssembleArchLookupSwitch(instr);
316 case kArchTableSwitch:
317 AssembleArchTableSwitch(instr);
320 // don't emit code for nops.
325 case kArchStackPointer:
326 __ mov(i.OutputRegister(), esp);
328 case kArchTruncateDoubleToI: {
329 auto result = i.OutputRegister();
330 auto input = i.InputDoubleRegister(0);
331 auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input);
332 __ cvttsd2si(result, Operand(input));
334 __ j(overflow, ool->entry());
335 __ bind(ool->exit());
339 if (HasImmediateInput(instr, 1)) {
340 __ add(i.InputOperand(0), i.InputImmediate(1));
342 __ add(i.InputRegister(0), i.InputOperand(1));
346 if (HasImmediateInput(instr, 1)) {
347 __ and_(i.InputOperand(0), i.InputImmediate(1));
349 __ and_(i.InputRegister(0), i.InputOperand(1));
353 if (HasImmediateInput(instr, 1)) {
354 __ cmp(i.InputOperand(0), i.InputImmediate(1));
356 __ cmp(i.InputRegister(0), i.InputOperand(1));
360 if (HasImmediateInput(instr, 1)) {
361 __ test(i.InputOperand(0), i.InputImmediate(1));
363 __ test(i.InputRegister(0), i.InputOperand(1));
367 if (HasImmediateInput(instr, 1)) {
368 __ imul(i.OutputRegister(), i.InputOperand(0), i.InputInt32(1));
370 __ imul(i.OutputRegister(), i.InputOperand(1));
374 __ imul(i.InputRegister(1));
377 __ mul(i.InputRegister(1));
381 __ idiv(i.InputOperand(1));
384 __ Move(edx, Immediate(0));
385 __ div(i.InputOperand(1));
388 __ not_(i.OutputOperand());
391 __ neg(i.OutputOperand());
394 if (HasImmediateInput(instr, 1)) {
395 __ or_(i.InputOperand(0), i.InputImmediate(1));
397 __ or_(i.InputRegister(0), i.InputOperand(1));
401 if (HasImmediateInput(instr, 1)) {
402 __ xor_(i.InputOperand(0), i.InputImmediate(1));
404 __ xor_(i.InputRegister(0), i.InputOperand(1));
408 if (HasImmediateInput(instr, 1)) {
409 __ sub(i.InputOperand(0), i.InputImmediate(1));
411 __ sub(i.InputRegister(0), i.InputOperand(1));
415 if (HasImmediateInput(instr, 1)) {
416 __ shl(i.OutputOperand(), i.InputInt5(1));
418 __ shl_cl(i.OutputOperand());
422 if (HasImmediateInput(instr, 1)) {
423 __ shr(i.OutputOperand(), i.InputInt5(1));
425 __ shr_cl(i.OutputOperand());
429 if (HasImmediateInput(instr, 1)) {
430 __ sar(i.OutputOperand(), i.InputInt5(1));
432 __ sar_cl(i.OutputOperand());
436 if (HasImmediateInput(instr, 1)) {
437 __ ror(i.OutputOperand(), i.InputInt5(1));
439 __ ror_cl(i.OutputOperand());
443 __ ucomisd(i.InputDoubleRegister(0), i.InputOperand(1));
446 __ addsd(i.InputDoubleRegister(0), i.InputOperand(1));
449 __ subsd(i.InputDoubleRegister(0), i.InputOperand(1));
452 __ mulsd(i.InputDoubleRegister(0), i.InputOperand(1));
455 __ divsd(i.InputDoubleRegister(0), i.InputOperand(1));
457 case kSSEFloat64Mod: {
458 // TODO(dcarney): alignment is wrong.
459 __ sub(esp, Immediate(kDoubleSize));
460 // Move values to st(0) and st(1).
461 __ movsd(Operand(esp, 0), i.InputDoubleRegister(1));
462 __ fld_d(Operand(esp, 0));
463 __ movsd(Operand(esp, 0), i.InputDoubleRegister(0));
464 __ fld_d(Operand(esp, 0));
465 // Loop while fprem isn't done.
468 // This instructions traps on all kinds inputs, but we are assuming the
469 // floating point control word is set to ignore them all.
471 // The following 2 instruction implicitly use eax.
474 __ j(parity_even, &mod_loop);
475 // Move output to stack and clean up.
477 __ fstp_d(Operand(esp, 0));
478 __ movsd(i.OutputDoubleRegister(), Operand(esp, 0));
479 __ add(esp, Immediate(kDoubleSize));
482 case kSSEFloat64Sqrt:
483 __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0));
485 case kSSEFloat64Floor: {
486 CpuFeatureScope sse_scope(masm(), SSE4_1);
487 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
488 v8::internal::Assembler::kRoundDown);
491 case kSSEFloat64Ceil: {
492 CpuFeatureScope sse_scope(masm(), SSE4_1);
493 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
494 v8::internal::Assembler::kRoundUp);
497 case kSSEFloat64RoundTruncate: {
498 CpuFeatureScope sse_scope(masm(), SSE4_1);
499 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
500 v8::internal::Assembler::kRoundToZero);
504 __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0));
507 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0));
509 case kSSEFloat64ToInt32:
510 __ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
512 case kSSEFloat64ToUint32: {
513 XMMRegister scratch = xmm0;
514 __ Move(scratch, -2147483648.0);
515 __ addsd(scratch, i.InputOperand(0));
516 __ cvttsd2si(i.OutputRegister(), scratch);
517 __ add(i.OutputRegister(), Immediate(0x80000000));
520 case kSSEInt32ToFloat64:
521 __ cvtsi2sd(i.OutputDoubleRegister(), i.InputOperand(0));
523 case kSSEUint32ToFloat64:
524 __ LoadUint32(i.OutputDoubleRegister(), i.InputOperand(0));
526 case kAVXFloat64Add: {
527 CpuFeatureScope avx_scope(masm(), AVX);
528 __ vaddsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
532 case kAVXFloat64Sub: {
533 CpuFeatureScope avx_scope(masm(), AVX);
534 __ vsubsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
538 case kAVXFloat64Mul: {
539 CpuFeatureScope avx_scope(masm(), AVX);
540 __ vmulsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
544 case kAVXFloat64Div: {
545 CpuFeatureScope avx_scope(masm(), AVX);
546 __ vdivsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
551 __ movsx_b(i.OutputRegister(), i.MemoryOperand());
554 __ movzx_b(i.OutputRegister(), i.MemoryOperand());
558 Operand operand = i.MemoryOperand(&index);
559 if (HasImmediateInput(instr, index)) {
560 __ mov_b(operand, i.InputInt8(index));
562 __ mov_b(operand, i.InputRegister(index));
567 __ movsx_w(i.OutputRegister(), i.MemoryOperand());
570 __ movzx_w(i.OutputRegister(), i.MemoryOperand());
574 Operand operand = i.MemoryOperand(&index);
575 if (HasImmediateInput(instr, index)) {
576 __ mov_w(operand, i.InputInt16(index));
578 __ mov_w(operand, i.InputRegister(index));
583 if (instr->HasOutput()) {
584 __ mov(i.OutputRegister(), i.MemoryOperand());
587 Operand operand = i.MemoryOperand(&index);
588 if (HasImmediateInput(instr, index)) {
589 __ mov(operand, i.InputImmediate(index));
591 __ mov(operand, i.InputRegister(index));
596 if (instr->HasOutput()) {
597 __ movsd(i.OutputDoubleRegister(), i.MemoryOperand());
600 Operand operand = i.MemoryOperand(&index);
601 __ movsd(operand, i.InputDoubleRegister(index));
605 if (instr->HasOutput()) {
606 __ movss(i.OutputDoubleRegister(), i.MemoryOperand());
609 Operand operand = i.MemoryOperand(&index);
610 __ movss(operand, i.InputDoubleRegister(index));
614 AddressingMode mode = AddressingModeField::decode(instr->opcode());
615 // Shorten "leal" to "addl", "subl" or "shll" if the register allocation
616 // and addressing mode just happens to work out. The "addl"/"subl" forms
617 // in these cases are faster based on measurements.
618 if (mode == kMode_MI) {
619 __ Move(i.OutputRegister(), Immediate(i.InputInt32(0)));
620 } else if (i.InputRegister(0).is(i.OutputRegister())) {
621 if (mode == kMode_MRI) {
622 int32_t constant_summand = i.InputInt32(1);
623 if (constant_summand > 0) {
624 __ add(i.OutputRegister(), Immediate(constant_summand));
625 } else if (constant_summand < 0) {
626 __ sub(i.OutputRegister(), Immediate(-constant_summand));
628 } else if (mode == kMode_MR1) {
629 if (i.InputRegister(1).is(i.OutputRegister())) {
630 __ shl(i.OutputRegister(), 1);
632 __ lea(i.OutputRegister(), i.MemoryOperand());
634 } else if (mode == kMode_M2) {
635 __ shl(i.OutputRegister(), 1);
636 } else if (mode == kMode_M4) {
637 __ shl(i.OutputRegister(), 2);
638 } else if (mode == kMode_M8) {
639 __ shl(i.OutputRegister(), 3);
641 __ lea(i.OutputRegister(), i.MemoryOperand());
644 __ lea(i.OutputRegister(), i.MemoryOperand());
649 if (HasImmediateInput(instr, 0)) {
650 __ push(i.InputImmediate(0));
652 __ push(i.InputOperand(0));
655 case kIA32StoreWriteBarrier: {
656 Register object = i.InputRegister(0);
657 Register index = i.InputRegister(1);
658 Register value = i.InputRegister(2);
659 __ mov(Operand(object, index, times_1, 0), value);
660 __ lea(index, Operand(object, index, times_1, 0));
661 SaveFPRegsMode mode =
662 frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
663 __ RecordWrite(object, index, value, mode);
666 case kCheckedLoadInt8:
667 ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b);
669 case kCheckedLoadUint8:
670 ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_b);
672 case kCheckedLoadInt16:
673 ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_w);
675 case kCheckedLoadUint16:
676 ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_w);
678 case kCheckedLoadWord32:
679 ASSEMBLE_CHECKED_LOAD_INTEGER(mov);
681 case kCheckedLoadFloat32:
682 ASSEMBLE_CHECKED_LOAD_FLOAT(movss);
684 case kCheckedLoadFloat64:
685 ASSEMBLE_CHECKED_LOAD_FLOAT(movsd);
687 case kCheckedStoreWord8:
688 ASSEMBLE_CHECKED_STORE_INTEGER(mov_b);
690 case kCheckedStoreWord16:
691 ASSEMBLE_CHECKED_STORE_INTEGER(mov_w);
693 case kCheckedStoreWord32:
694 ASSEMBLE_CHECKED_STORE_INTEGER(mov);
696 case kCheckedStoreFloat32:
697 ASSEMBLE_CHECKED_STORE_FLOAT(movss);
699 case kCheckedStoreFloat64:
700 ASSEMBLE_CHECKED_STORE_FLOAT(movsd);
706 // Assembles a branch after an instruction.
707 void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
708 IA32OperandConverter i(this, instr);
709 Label::Distance flabel_distance =
710 branch->fallthru ? Label::kNear : Label::kFar;
711 Label* tlabel = branch->true_label;
712 Label* flabel = branch->false_label;
713 switch (branch->condition) {
714 case kUnorderedEqual:
715 __ j(parity_even, flabel, flabel_distance);
720 case kUnorderedNotEqual:
721 __ j(parity_even, tlabel);
724 __ j(not_equal, tlabel);
726 case kSignedLessThan:
729 case kSignedGreaterThanOrEqual:
730 __ j(greater_equal, tlabel);
732 case kSignedLessThanOrEqual:
733 __ j(less_equal, tlabel);
735 case kSignedGreaterThan:
736 __ j(greater, tlabel);
738 case kUnsignedLessThan:
741 case kUnsignedGreaterThanOrEqual:
742 __ j(above_equal, tlabel);
744 case kUnsignedLessThanOrEqual:
745 __ j(below_equal, tlabel);
747 case kUnsignedGreaterThan:
751 __ j(overflow, tlabel);
754 __ j(no_overflow, tlabel);
757 // Add a jump if not falling through to the next block.
758 if (!branch->fallthru) __ jmp(flabel);
762 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
763 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target));
767 // Assembles boolean materializations after an instruction.
768 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
769 FlagsCondition condition) {
770 IA32OperandConverter i(this, instr);
773 // Materialize a full 32-bit 1 or 0 value. The result register is always the
774 // last output of the instruction.
776 DCHECK_NE(0u, instr->OutputCount());
777 Register reg = i.OutputRegister(instr->OutputCount() - 1);
778 Condition cc = no_condition;
780 case kUnorderedEqual:
781 __ j(parity_odd, &check, Label::kNear);
782 __ Move(reg, Immediate(0));
783 __ jmp(&done, Label::kNear);
788 case kUnorderedNotEqual:
789 __ j(parity_odd, &check, Label::kNear);
790 __ mov(reg, Immediate(1));
791 __ jmp(&done, Label::kNear);
796 case kSignedLessThan:
799 case kSignedGreaterThanOrEqual:
802 case kSignedLessThanOrEqual:
805 case kSignedGreaterThan:
808 case kUnsignedLessThan:
811 case kUnsignedGreaterThanOrEqual:
814 case kUnsignedLessThanOrEqual:
817 case kUnsignedGreaterThan:
828 if (reg.is_byte_register()) {
829 // setcc for byte registers (al, bl, cl, dl).
831 __ movzx_b(reg, reg);
833 // Emit a branch to set a register to either 1 or 0.
835 __ j(cc, &set, Label::kNear);
836 __ Move(reg, Immediate(0));
837 __ jmp(&done, Label::kNear);
839 __ mov(reg, Immediate(1));
845 void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
846 IA32OperandConverter i(this, instr);
847 Register input = i.InputRegister(0);
848 for (size_t index = 2; index < instr->InputCount(); index += 2) {
849 __ cmp(input, Immediate(i.InputInt32(index + 0)));
850 __ j(equal, GetLabel(i.InputRpo(index + 1)));
852 AssembleArchJump(i.InputRpo(1));
856 void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
857 IA32OperandConverter i(this, instr);
858 Register input = i.InputRegister(0);
859 size_t const case_count = instr->InputCount() - 2;
860 Label** cases = zone()->NewArray<Label*>(case_count);
861 for (size_t index = 0; index < case_count; ++index) {
862 cases[index] = GetLabel(i.InputRpo(index + 2));
864 Label* const table = AddJumpTable(cases, case_count);
865 __ cmp(input, Immediate(case_count));
866 __ j(above_equal, GetLabel(i.InputRpo(1)));
867 __ jmp(Operand::JumpTable(input, times_4, table));
871 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
872 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
873 isolate(), deoptimization_id, Deoptimizer::LAZY);
874 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
878 // The calling convention for JSFunctions on IA32 passes arguments on the
879 // stack and the JSFunction and context in EDI and ESI, respectively, thus
880 // the steps of the call look as follows:
882 // --{ before the call instruction }--------------------------------------------
886 // --{ push arguments and setup ESI, EDI }--------------------------------------
887 // | args + receiver | caller frame |
889 // [edi = JSFunction, esi = context]
891 // --{ call [edi + kCodeEntryOffset] }------------------------------------------
892 // | RET | args + receiver | caller frame |
895 // =={ prologue of called function }============================================
896 // --{ push ebp }---------------------------------------------------------------
897 // | FP | RET | args + receiver | caller frame |
900 // --{ mov ebp, esp }-----------------------------------------------------------
901 // | FP | RET | args + receiver | caller frame |
904 // --{ push esi }---------------------------------------------------------------
905 // | CTX | FP | RET | args + receiver | caller frame |
908 // --{ push edi }---------------------------------------------------------------
909 // | FNC | CTX | FP | RET | args + receiver | caller frame |
912 // --{ subi esp, #N }-----------------------------------------------------------
913 // | callee frame | FNC | CTX | FP | RET | args + receiver | caller frame |
916 // =={ body of called function }================================================
918 // =={ epilogue of called function }============================================
919 // --{ mov esp, ebp }-----------------------------------------------------------
920 // | FP | RET | args + receiver | caller frame |
923 // --{ pop ebp }-----------------------------------------------------------
924 // | | RET | args + receiver | caller frame |
927 // --{ ret #A+1 }-----------------------------------------------------------
928 // | | caller frame |
932 // Runtime function calls are accomplished by doing a stub call to the
933 // CEntryStub (a real code object). On IA32 passes arguments on the
934 // stack, the number of arguments in EAX, the address of the runtime function
935 // in EBX, and the context in ESI.
937 // --{ before the call instruction }--------------------------------------------
941 // --{ push arguments and setup EAX, EBX, and ESI }-----------------------------
942 // | args + receiver | caller frame |
944 // [eax = #args, ebx = runtime function, esi = context]
946 // --{ call #CEntryStub }-------------------------------------------------------
947 // | RET | args + receiver | caller frame |
950 // =={ body of runtime function }===============================================
952 // --{ runtime returns }--------------------------------------------------------
956 // Other custom linkages (e.g. for calling directly into and out of C++) may
957 // need to save callee-saved registers on the stack, which is done in the
958 // function prologue of generated code.
960 // --{ before the call instruction }--------------------------------------------
964 // --{ set up arguments in registers on stack }---------------------------------
965 // | args | caller frame |
967 // [r0 = arg0, r1 = arg1, ...]
969 // --{ call code }--------------------------------------------------------------
970 // | RET | args | caller frame |
973 // =={ prologue of called function }============================================
974 // --{ push ebp }---------------------------------------------------------------
975 // | FP | RET | args | caller frame |
978 // --{ mov ebp, esp }-----------------------------------------------------------
979 // | FP | RET | args | caller frame |
982 // --{ save registers }---------------------------------------------------------
983 // | regs | FP | RET | args | caller frame |
986 // --{ subi esp, #N }-----------------------------------------------------------
987 // | callee frame | regs | FP | RET | args | caller frame |
990 // =={ body of called function }================================================
992 // =={ epilogue of called function }============================================
993 // --{ restore registers }------------------------------------------------------
994 // | regs | FP | RET | args | caller frame |
997 // --{ mov esp, ebp }-----------------------------------------------------------
998 // | FP | RET | args | caller frame |
1001 // --{ pop ebp }----------------------------------------------------------------
1002 // | RET | args | caller frame |
1006 void CodeGenerator::AssemblePrologue() {
1007 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1008 int stack_slots = frame()->GetSpillSlotCount();
1009 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1010 // Assemble a prologue similar the to cdecl calling convention.
1013 const RegList saves = descriptor->CalleeSavedRegisters();
1014 if (saves != 0) { // Save callee-saved registers.
1015 int register_save_area_size = 0;
1016 for (int i = Register::kNumRegisters - 1; i >= 0; i--) {
1017 if (!((1 << i) & saves)) continue;
1018 __ push(Register::from_code(i));
1019 register_save_area_size += kPointerSize;
1021 frame()->SetRegisterSaveAreaSize(register_save_area_size);
1023 } else if (descriptor->IsJSFunctionCall()) {
1024 // TODO(turbofan): this prologue is redundant with OSR, but needed for
1026 CompilationInfo* info = this->info();
1027 __ Prologue(info->IsCodePreAgingActive());
1028 frame()->SetRegisterSaveAreaSize(
1029 StandardFrameConstants::kFixedFrameSizeFromFp);
1030 } else if (stack_slots > 0) {
1032 frame()->SetRegisterSaveAreaSize(
1033 StandardFrameConstants::kFixedFrameSizeFromFp);
1036 if (info()->is_osr()) {
1037 // TurboFan OSR-compiled functions cannot be entered directly.
1038 __ Abort(kShouldNotDirectlyEnterOsrFunction);
1040 // Unoptimized code jumps directly to this entrypoint while the unoptimized
1041 // frame is still on the stack. Optimized code uses OSR values directly from
1042 // the unoptimized frame. Thus, all that needs to be done is to allocate the
1043 // remaining stack slots.
1044 if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
1045 osr_pc_offset_ = __ pc_offset();
1046 DCHECK(stack_slots >= frame()->GetOsrStackSlotCount());
1047 stack_slots -= frame()->GetOsrStackSlotCount();
1050 if (stack_slots > 0) {
1051 // Allocate the stack slots used by this frame.
1052 __ sub(esp, Immediate(stack_slots * kPointerSize));
1057 void CodeGenerator::AssembleReturn() {
1058 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
1059 int stack_slots = frame()->GetSpillSlotCount();
1060 if (descriptor->kind() == CallDescriptor::kCallAddress) {
1061 const RegList saves = descriptor->CalleeSavedRegisters();
1062 if (frame()->GetRegisterSaveAreaSize() > 0) {
1063 // Remove this frame's spill slots first.
1064 if (stack_slots > 0) {
1065 __ add(esp, Immediate(stack_slots * kPointerSize));
1067 // Restore registers.
1069 for (int i = 0; i < Register::kNumRegisters; i++) {
1070 if (!((1 << i) & saves)) continue;
1071 __ pop(Register::from_code(i));
1074 __ pop(ebp); // Pop caller's frame pointer.
1077 // No saved registers.
1078 __ mov(esp, ebp); // Move stack pointer back to frame pointer.
1079 __ pop(ebp); // Pop caller's frame pointer.
1082 } else if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
1083 __ mov(esp, ebp); // Move stack pointer back to frame pointer.
1084 __ pop(ebp); // Pop caller's frame pointer.
1085 int pop_count = descriptor->IsJSFunctionCall()
1086 ? static_cast<int>(descriptor->JSParameterCount())
1088 __ ret(pop_count * kPointerSize);
1095 void CodeGenerator::AssembleMove(InstructionOperand* source,
1096 InstructionOperand* destination) {
1097 IA32OperandConverter g(this, NULL);
1098 // Dispatch on the source and destination operand kinds. Not all
1099 // combinations are possible.
1100 if (source->IsRegister()) {
1101 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1102 Register src = g.ToRegister(source);
1103 Operand dst = g.ToOperand(destination);
1105 } else if (source->IsStackSlot()) {
1106 DCHECK(destination->IsRegister() || destination->IsStackSlot());
1107 Operand src = g.ToOperand(source);
1108 if (destination->IsRegister()) {
1109 Register dst = g.ToRegister(destination);
1112 Operand dst = g.ToOperand(destination);
1116 } else if (source->IsConstant()) {
1117 Constant src_constant = g.ToConstant(source);
1118 if (src_constant.type() == Constant::kHeapObject) {
1119 Handle<HeapObject> src = src_constant.ToHeapObject();
1120 if (destination->IsRegister()) {
1121 Register dst = g.ToRegister(destination);
1122 __ LoadHeapObject(dst, src);
1124 DCHECK(destination->IsStackSlot());
1125 Operand dst = g.ToOperand(destination);
1126 AllowDeferredHandleDereference embedding_raw_address;
1127 if (isolate()->heap()->InNewSpace(*src)) {
1128 __ PushHeapObject(src);
1134 } else if (destination->IsRegister()) {
1135 Register dst = g.ToRegister(destination);
1136 __ Move(dst, g.ToImmediate(source));
1137 } else if (destination->IsStackSlot()) {
1138 Operand dst = g.ToOperand(destination);
1139 __ Move(dst, g.ToImmediate(source));
1140 } else if (src_constant.type() == Constant::kFloat32) {
1141 // TODO(turbofan): Can we do better here?
1142 uint32_t src = bit_cast<uint32_t>(src_constant.ToFloat32());
1143 if (destination->IsDoubleRegister()) {
1144 XMMRegister dst = g.ToDoubleRegister(destination);
1147 DCHECK(destination->IsDoubleStackSlot());
1148 Operand dst = g.ToOperand(destination);
1149 __ Move(dst, Immediate(src));
1152 DCHECK_EQ(Constant::kFloat64, src_constant.type());
1153 uint64_t src = bit_cast<uint64_t>(src_constant.ToFloat64());
1154 uint32_t lower = static_cast<uint32_t>(src);
1155 uint32_t upper = static_cast<uint32_t>(src >> 32);
1156 if (destination->IsDoubleRegister()) {
1157 XMMRegister dst = g.ToDoubleRegister(destination);
1160 DCHECK(destination->IsDoubleStackSlot());
1161 Operand dst0 = g.ToOperand(destination);
1162 Operand dst1 = g.HighOperand(destination);
1163 __ Move(dst0, Immediate(lower));
1164 __ Move(dst1, Immediate(upper));
1167 } else if (source->IsDoubleRegister()) {
1168 XMMRegister src = g.ToDoubleRegister(source);
1169 if (destination->IsDoubleRegister()) {
1170 XMMRegister dst = g.ToDoubleRegister(destination);
1171 __ movaps(dst, src);
1173 DCHECK(destination->IsDoubleStackSlot());
1174 Operand dst = g.ToOperand(destination);
1177 } else if (source->IsDoubleStackSlot()) {
1178 DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1179 Operand src = g.ToOperand(source);
1180 if (destination->IsDoubleRegister()) {
1181 XMMRegister dst = g.ToDoubleRegister(destination);
1184 // We rely on having xmm0 available as a fixed scratch register.
1185 Operand dst = g.ToOperand(destination);
1186 __ movsd(xmm0, src);
1187 __ movsd(dst, xmm0);
1195 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1196 InstructionOperand* destination) {
1197 IA32OperandConverter g(this, NULL);
1198 // Dispatch on the source and destination operand kinds. Not all
1199 // combinations are possible.
1200 if (source->IsRegister() && destination->IsRegister()) {
1201 // Register-register.
1202 Register src = g.ToRegister(source);
1203 Register dst = g.ToRegister(destination);
1205 } else if (source->IsRegister() && destination->IsStackSlot()) {
1207 __ xchg(g.ToRegister(source), g.ToOperand(destination));
1208 } else if (source->IsStackSlot() && destination->IsStackSlot()) {
1210 Operand src = g.ToOperand(source);
1211 Operand dst = g.ToOperand(destination);
1216 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
1217 // XMM register-register swap. We rely on having xmm0
1218 // available as a fixed scratch register.
1219 XMMRegister src = g.ToDoubleRegister(source);
1220 XMMRegister dst = g.ToDoubleRegister(destination);
1221 __ movaps(xmm0, src);
1222 __ movaps(src, dst);
1223 __ movaps(dst, xmm0);
1224 } else if (source->IsDoubleRegister() && destination->IsDoubleStackSlot()) {
1225 // XMM register-memory swap. We rely on having xmm0
1226 // available as a fixed scratch register.
1227 XMMRegister reg = g.ToDoubleRegister(source);
1228 Operand other = g.ToOperand(destination);
1229 __ movsd(xmm0, other);
1230 __ movsd(other, reg);
1231 __ movaps(reg, xmm0);
1232 } else if (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot()) {
1233 // Double-width memory-to-memory.
1234 Operand src0 = g.ToOperand(source);
1235 Operand src1 = g.HighOperand(source);
1236 Operand dst0 = g.ToOperand(destination);
1237 Operand dst1 = g.HighOperand(destination);
1238 __ movsd(xmm0, dst0); // Save destination in xmm0.
1239 __ push(src0); // Then use stack to copy source to destination.
1243 __ movsd(src0, xmm0);
1245 // No other combinations are possible.
1251 void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
1252 for (size_t index = 0; index < target_count; ++index) {
1253 __ dd(targets[index]);
1258 void CodeGenerator::AddNopForSmiCodeInlining() { __ nop(); }
1261 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1262 int space_needed = Deoptimizer::patch_size();
1263 if (!info()->IsStub()) {
1264 // Ensure that we have enough space after the previous lazy-bailout
1265 // instruction for patching the code here.
1266 int current_pc = masm()->pc_offset();
1267 if (current_pc < last_lazy_deopt_pc_ + space_needed) {
1268 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1269 __ Nop(padding_size);
1272 MarkLazyDeoptSite();
1277 } // namespace compiler
1278 } // namespace internal