89c2ffb6f8fd15854f3a29e248946c1f05641c41
[platform/upstream/nodejs.git] / deps / v8 / src / compiler / arm64 / code-generator-arm64.cc
1 // Copyright 2014 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.
4
5 #include "src/compiler/code-generator.h"
6
7 #include "src/arm64/macro-assembler-arm64.h"
8 #include "src/compiler/code-generator-impl.h"
9 #include "src/compiler/gap-resolver.h"
10 #include "src/compiler/node-matchers.h"
11 #include "src/scopes.h"
12
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16
17 #define __ masm()->
18
19
20 // Adds Arm64-specific methods to convert InstructionOperands.
21 class Arm64OperandConverter FINAL : public InstructionOperandConverter {
22  public:
23   Arm64OperandConverter(CodeGenerator* gen, Instruction* instr)
24       : InstructionOperandConverter(gen, instr) {}
25
26   DoubleRegister InputFloat32Register(int index) {
27     return InputDoubleRegister(index).S();
28   }
29
30   DoubleRegister InputFloat64Register(int index) {
31     return InputDoubleRegister(index);
32   }
33
34   DoubleRegister OutputFloat32Register() { return OutputDoubleRegister().S(); }
35
36   DoubleRegister OutputFloat64Register() { return OutputDoubleRegister(); }
37
38   Register InputRegister32(int index) {
39     return ToRegister(instr_->InputAt(index)).W();
40   }
41
42   Register InputRegister64(int index) { return InputRegister(index); }
43
44   Operand InputImmediate(int index) {
45     return ToImmediate(instr_->InputAt(index));
46   }
47
48   Operand InputOperand(int index) { return ToOperand(instr_->InputAt(index)); }
49
50   Operand InputOperand64(int index) { return InputOperand(index); }
51
52   Operand InputOperand32(int index) {
53     return ToOperand32(instr_->InputAt(index));
54   }
55
56   Register OutputRegister64() { return OutputRegister(); }
57
58   Register OutputRegister32() { return ToRegister(instr_->Output()).W(); }
59
60   Operand InputOperand2_32(int index) {
61     switch (AddressingModeField::decode(instr_->opcode())) {
62       case kMode_None:
63         return InputOperand32(index);
64       case kMode_Operand2_R_LSL_I:
65         return Operand(InputRegister32(index), LSL, InputInt5(index + 1));
66       case kMode_Operand2_R_LSR_I:
67         return Operand(InputRegister32(index), LSR, InputInt5(index + 1));
68       case kMode_Operand2_R_ASR_I:
69         return Operand(InputRegister32(index), ASR, InputInt5(index + 1));
70       case kMode_Operand2_R_ROR_I:
71         return Operand(InputRegister32(index), ROR, InputInt5(index + 1));
72       case kMode_MRI:
73       case kMode_MRR:
74         break;
75     }
76     UNREACHABLE();
77     return Operand(-1);
78   }
79
80   Operand InputOperand2_64(int index) {
81     switch (AddressingModeField::decode(instr_->opcode())) {
82       case kMode_None:
83         return InputOperand64(index);
84       case kMode_Operand2_R_LSL_I:
85         return Operand(InputRegister64(index), LSL, InputInt6(index + 1));
86       case kMode_Operand2_R_LSR_I:
87         return Operand(InputRegister64(index), LSR, InputInt6(index + 1));
88       case kMode_Operand2_R_ASR_I:
89         return Operand(InputRegister64(index), ASR, InputInt6(index + 1));
90       case kMode_Operand2_R_ROR_I:
91         return Operand(InputRegister64(index), ROR, InputInt6(index + 1));
92       case kMode_MRI:
93       case kMode_MRR:
94         break;
95     }
96     UNREACHABLE();
97     return Operand(-1);
98   }
99
100   MemOperand MemoryOperand(int* first_index) {
101     const int index = *first_index;
102     switch (AddressingModeField::decode(instr_->opcode())) {
103       case kMode_None:
104       case kMode_Operand2_R_LSL_I:
105       case kMode_Operand2_R_LSR_I:
106       case kMode_Operand2_R_ASR_I:
107       case kMode_Operand2_R_ROR_I:
108         break;
109       case kMode_MRI:
110         *first_index += 2;
111         return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
112       case kMode_MRR:
113         *first_index += 2;
114         return MemOperand(InputRegister(index + 0), InputRegister(index + 1));
115     }
116     UNREACHABLE();
117     return MemOperand(no_reg);
118   }
119
120   MemOperand MemoryOperand(int first_index = 0) {
121     return MemoryOperand(&first_index);
122   }
123
124   Operand ToOperand(InstructionOperand* op) {
125     if (op->IsRegister()) {
126       return Operand(ToRegister(op));
127     }
128     return ToImmediate(op);
129   }
130
131   Operand ToOperand32(InstructionOperand* op) {
132     if (op->IsRegister()) {
133       return Operand(ToRegister(op).W());
134     }
135     return ToImmediate(op);
136   }
137
138   Operand ToImmediate(InstructionOperand* operand) {
139     Constant constant = ToConstant(operand);
140     switch (constant.type()) {
141       case Constant::kInt32:
142         return Operand(constant.ToInt32());
143       case Constant::kInt64:
144         return Operand(constant.ToInt64());
145       case Constant::kFloat32:
146         return Operand(
147             isolate()->factory()->NewNumber(constant.ToFloat32(), TENURED));
148       case Constant::kFloat64:
149         return Operand(
150             isolate()->factory()->NewNumber(constant.ToFloat64(), TENURED));
151       case Constant::kExternalReference:
152         return Operand(constant.ToExternalReference());
153       case Constant::kHeapObject:
154         return Operand(constant.ToHeapObject());
155       case Constant::kRpoNumber:
156         UNREACHABLE();  // TODO(dcarney): RPO immediates on arm64.
157         break;
158     }
159     UNREACHABLE();
160     return Operand(-1);
161   }
162
163   MemOperand ToMemOperand(InstructionOperand* op, MacroAssembler* masm) const {
164     DCHECK(op != NULL);
165     DCHECK(!op->IsRegister());
166     DCHECK(!op->IsDoubleRegister());
167     DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot());
168     // The linkage computes where all spill slots are located.
169     FrameOffset offset = linkage()->GetFrameOffset(op->index(), frame(), 0);
170     return MemOperand(offset.from_stack_pointer() ? masm->StackPointer() : fp,
171                       offset.offset());
172   }
173 };
174
175
176 namespace {
177
178 class OutOfLineLoadNaN32 FINAL : public OutOfLineCode {
179  public:
180   OutOfLineLoadNaN32(CodeGenerator* gen, DoubleRegister result)
181       : OutOfLineCode(gen), result_(result) {}
182
183   void Generate() FINAL {
184     __ Fmov(result_, std::numeric_limits<float>::quiet_NaN());
185   }
186
187  private:
188   DoubleRegister const result_;
189 };
190
191
192 class OutOfLineLoadNaN64 FINAL : public OutOfLineCode {
193  public:
194   OutOfLineLoadNaN64(CodeGenerator* gen, DoubleRegister result)
195       : OutOfLineCode(gen), result_(result) {}
196
197   void Generate() FINAL {
198     __ Fmov(result_, std::numeric_limits<double>::quiet_NaN());
199   }
200
201  private:
202   DoubleRegister const result_;
203 };
204
205
206 class OutOfLineLoadZero FINAL : public OutOfLineCode {
207  public:
208   OutOfLineLoadZero(CodeGenerator* gen, Register result)
209       : OutOfLineCode(gen), result_(result) {}
210
211   void Generate() FINAL { __ Mov(result_, 0); }
212
213  private:
214   Register const result_;
215 };
216
217
218 Condition FlagsConditionToCondition(FlagsCondition condition) {
219   switch (condition) {
220     case kEqual:
221       return eq;
222     case kNotEqual:
223       return ne;
224     case kSignedLessThan:
225       return lt;
226     case kSignedGreaterThanOrEqual:
227       return ge;
228     case kSignedLessThanOrEqual:
229       return le;
230     case kSignedGreaterThan:
231       return gt;
232     case kUnsignedLessThan:
233       return lo;
234     case kUnsignedGreaterThanOrEqual:
235       return hs;
236     case kUnsignedLessThanOrEqual:
237       return ls;
238     case kUnsignedGreaterThan:
239       return hi;
240     case kOverflow:
241       return vs;
242     case kNotOverflow:
243       return vc;
244     case kUnorderedEqual:
245     case kUnorderedNotEqual:
246       break;
247   }
248   UNREACHABLE();
249   return nv;
250 }
251
252 }  // namespace
253
254
255 #define ASSEMBLE_CHECKED_LOAD_FLOAT(width)                         \
256   do {                                                             \
257     auto result = i.OutputFloat##width##Register();                \
258     auto buffer = i.InputRegister(0);                              \
259     auto offset = i.InputRegister32(1);                            \
260     auto length = i.InputOperand32(2);                             \
261     __ Cmp(offset, length);                                        \
262     auto ool = new (zone()) OutOfLineLoadNaN##width(this, result); \
263     __ B(hs, ool->entry());                                        \
264     __ Ldr(result, MemOperand(buffer, offset, UXTW));              \
265     __ Bind(ool->exit());                                          \
266   } while (0)
267
268
269 #define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr)             \
270   do {                                                       \
271     auto result = i.OutputRegister32();                      \
272     auto buffer = i.InputRegister(0);                        \
273     auto offset = i.InputRegister32(1);                      \
274     auto length = i.InputOperand32(2);                       \
275     __ Cmp(offset, length);                                  \
276     auto ool = new (zone()) OutOfLineLoadZero(this, result); \
277     __ B(hs, ool->entry());                                  \
278     __ asm_instr(result, MemOperand(buffer, offset, UXTW));  \
279     __ Bind(ool->exit());                                    \
280   } while (0)
281
282
283 #define ASSEMBLE_CHECKED_STORE_FLOAT(width)          \
284   do {                                               \
285     auto buffer = i.InputRegister(0);                \
286     auto offset = i.InputRegister32(1);              \
287     auto length = i.InputOperand32(2);               \
288     auto value = i.InputFloat##width##Register(3);   \
289     __ Cmp(offset, length);                          \
290     Label done;                                      \
291     __ B(hs, &done);                                 \
292     __ Str(value, MemOperand(buffer, offset, UXTW)); \
293     __ Bind(&done);                                  \
294   } while (0)
295
296
297 #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr)          \
298   do {                                                     \
299     auto buffer = i.InputRegister(0);                      \
300     auto offset = i.InputRegister32(1);                    \
301     auto length = i.InputOperand32(2);                     \
302     auto value = i.InputRegister32(3);                     \
303     __ Cmp(offset, length);                                \
304     Label done;                                            \
305     __ B(hs, &done);                                       \
306     __ asm_instr(value, MemOperand(buffer, offset, UXTW)); \
307     __ Bind(&done);                                        \
308   } while (0)
309
310
311 #define ASSEMBLE_SHIFT(asm_instr, width)                                       \
312   do {                                                                         \
313     if (instr->InputAt(1)->IsRegister()) {                                     \
314       __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0),       \
315                    i.InputRegister##width(1));                                 \
316     } else {                                                                   \
317       int64_t imm = i.InputOperand##width(1).immediate().value();              \
318       __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0), imm); \
319     }                                                                          \
320   } while (0)
321
322
323 // Assembles an instruction after register allocation, producing machine code.
324 void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
325   Arm64OperandConverter i(this, instr);
326   InstructionCode opcode = instr->opcode();
327   switch (ArchOpcodeField::decode(opcode)) {
328     case kArchCallCodeObject: {
329       EnsureSpaceForLazyDeopt();
330       if (instr->InputAt(0)->IsImmediate()) {
331         __ Call(Handle<Code>::cast(i.InputHeapObject(0)),
332                 RelocInfo::CODE_TARGET);
333       } else {
334         Register target = i.InputRegister(0);
335         __ Add(target, target, Code::kHeaderSize - kHeapObjectTag);
336         __ Call(target);
337       }
338       AddSafepointAndDeopt(instr);
339       break;
340     }
341     case kArchCallJSFunction: {
342       EnsureSpaceForLazyDeopt();
343       Register func = i.InputRegister(0);
344       if (FLAG_debug_code) {
345         // Check the function's context matches the context argument.
346         UseScratchRegisterScope scope(masm());
347         Register temp = scope.AcquireX();
348         __ Ldr(temp, FieldMemOperand(func, JSFunction::kContextOffset));
349         __ cmp(cp, temp);
350         __ Assert(eq, kWrongFunctionContext);
351       }
352       __ Ldr(x10, FieldMemOperand(func, JSFunction::kCodeEntryOffset));
353       __ Call(x10);
354       AddSafepointAndDeopt(instr);
355       break;
356     }
357     case kArchJmp:
358       AssembleArchJump(i.InputRpo(0));
359       break;
360     case kArchTableSwitch:
361       AssembleArchTableSwitch(instr);
362       break;
363     case kArchLookupSwitch:
364       AssembleArchLookupSwitch(instr);
365       break;
366     case kArchNop:
367       // don't emit code for nops.
368       break;
369     case kArchRet:
370       AssembleReturn();
371       break;
372     case kArchStackPointer:
373       __ mov(i.OutputRegister(), masm()->StackPointer());
374       break;
375     case kArchTruncateDoubleToI:
376       __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0));
377       break;
378     case kArm64Float64Ceil:
379       __ Frintp(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
380       break;
381     case kArm64Float64Floor:
382       __ Frintm(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
383       break;
384     case kArm64Float64RoundTruncate:
385       __ Frintz(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
386       break;
387     case kArm64Float64RoundTiesAway:
388       __ Frinta(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
389       break;
390     case kArm64Add:
391       __ Add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
392       break;
393     case kArm64Add32:
394       if (FlagsModeField::decode(opcode) != kFlags_none) {
395         __ Adds(i.OutputRegister32(), i.InputRegister32(0),
396                 i.InputOperand2_32(1));
397       } else {
398         __ Add(i.OutputRegister32(), i.InputRegister32(0),
399                i.InputOperand2_32(1));
400       }
401       break;
402     case kArm64And:
403       __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
404       break;
405     case kArm64And32:
406       __ And(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
407       break;
408     case kArm64Bic:
409       __ Bic(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
410       break;
411     case kArm64Bic32:
412       __ Bic(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
413       break;
414     case kArm64Mul:
415       __ Mul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
416       break;
417     case kArm64Mul32:
418       __ Mul(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
419       break;
420     case kArm64Smull:
421       __ Smull(i.OutputRegister(), i.InputRegister32(0), i.InputRegister32(1));
422       break;
423     case kArm64Umull:
424       __ Umull(i.OutputRegister(), i.InputRegister32(0), i.InputRegister32(1));
425       break;
426     case kArm64Madd:
427       __ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
428               i.InputRegister(2));
429       break;
430     case kArm64Madd32:
431       __ Madd(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
432               i.InputRegister32(2));
433       break;
434     case kArm64Msub:
435       __ Msub(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
436               i.InputRegister(2));
437       break;
438     case kArm64Msub32:
439       __ Msub(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
440               i.InputRegister32(2));
441       break;
442     case kArm64Mneg:
443       __ Mneg(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
444       break;
445     case kArm64Mneg32:
446       __ Mneg(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
447       break;
448     case kArm64Idiv:
449       __ Sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
450       break;
451     case kArm64Idiv32:
452       __ Sdiv(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
453       break;
454     case kArm64Udiv:
455       __ Udiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
456       break;
457     case kArm64Udiv32:
458       __ Udiv(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
459       break;
460     case kArm64Imod: {
461       UseScratchRegisterScope scope(masm());
462       Register temp = scope.AcquireX();
463       __ Sdiv(temp, i.InputRegister(0), i.InputRegister(1));
464       __ Msub(i.OutputRegister(), temp, i.InputRegister(1), i.InputRegister(0));
465       break;
466     }
467     case kArm64Imod32: {
468       UseScratchRegisterScope scope(masm());
469       Register temp = scope.AcquireW();
470       __ Sdiv(temp, i.InputRegister32(0), i.InputRegister32(1));
471       __ Msub(i.OutputRegister32(), temp, i.InputRegister32(1),
472               i.InputRegister32(0));
473       break;
474     }
475     case kArm64Umod: {
476       UseScratchRegisterScope scope(masm());
477       Register temp = scope.AcquireX();
478       __ Udiv(temp, i.InputRegister(0), i.InputRegister(1));
479       __ Msub(i.OutputRegister(), temp, i.InputRegister(1), i.InputRegister(0));
480       break;
481     }
482     case kArm64Umod32: {
483       UseScratchRegisterScope scope(masm());
484       Register temp = scope.AcquireW();
485       __ Udiv(temp, i.InputRegister32(0), i.InputRegister32(1));
486       __ Msub(i.OutputRegister32(), temp, i.InputRegister32(1),
487               i.InputRegister32(0));
488       break;
489     }
490     // TODO(dcarney): use mvn instr??
491     case kArm64Not:
492       __ Orn(i.OutputRegister(), xzr, i.InputOperand(0));
493       break;
494     case kArm64Not32:
495       __ Orn(i.OutputRegister32(), wzr, i.InputOperand32(0));
496       break;
497     case kArm64Neg:
498       __ Neg(i.OutputRegister(), i.InputOperand(0));
499       break;
500     case kArm64Neg32:
501       __ Neg(i.OutputRegister32(), i.InputOperand32(0));
502       break;
503     case kArm64Or:
504       __ Orr(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
505       break;
506     case kArm64Or32:
507       __ Orr(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
508       break;
509     case kArm64Orn:
510       __ Orn(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
511       break;
512     case kArm64Orn32:
513       __ Orn(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
514       break;
515     case kArm64Eor:
516       __ Eor(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
517       break;
518     case kArm64Eor32:
519       __ Eor(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
520       break;
521     case kArm64Eon:
522       __ Eon(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
523       break;
524     case kArm64Eon32:
525       __ Eon(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand2_32(1));
526       break;
527     case kArm64Sub:
528       __ Sub(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1));
529       break;
530     case kArm64Sub32:
531       if (FlagsModeField::decode(opcode) != kFlags_none) {
532         __ Subs(i.OutputRegister32(), i.InputRegister32(0),
533                 i.InputOperand2_32(1));
534       } else {
535         __ Sub(i.OutputRegister32(), i.InputRegister32(0),
536                i.InputOperand2_32(1));
537       }
538       break;
539     case kArm64Lsl:
540       ASSEMBLE_SHIFT(Lsl, 64);
541       break;
542     case kArm64Lsl32:
543       ASSEMBLE_SHIFT(Lsl, 32);
544       break;
545     case kArm64Lsr:
546       ASSEMBLE_SHIFT(Lsr, 64);
547       break;
548     case kArm64Lsr32:
549       ASSEMBLE_SHIFT(Lsr, 32);
550       break;
551     case kArm64Asr:
552       ASSEMBLE_SHIFT(Asr, 64);
553       break;
554     case kArm64Asr32:
555       ASSEMBLE_SHIFT(Asr, 32);
556       break;
557     case kArm64Ror:
558       ASSEMBLE_SHIFT(Ror, 64);
559       break;
560     case kArm64Ror32:
561       ASSEMBLE_SHIFT(Ror, 32);
562       break;
563     case kArm64Mov32:
564       __ Mov(i.OutputRegister32(), i.InputRegister32(0));
565       break;
566     case kArm64Sxtb32:
567       __ Sxtb(i.OutputRegister32(), i.InputRegister32(0));
568       break;
569     case kArm64Sxth32:
570       __ Sxth(i.OutputRegister32(), i.InputRegister32(0));
571       break;
572     case kArm64Sxtw:
573       __ Sxtw(i.OutputRegister(), i.InputRegister32(0));
574       break;
575     case kArm64Ubfx:
576       __ Ubfx(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
577               i.InputInt8(2));
578       break;
579     case kArm64Ubfx32:
580       __ Ubfx(i.OutputRegister32(), i.InputRegister32(0), i.InputInt8(1),
581               i.InputInt8(2));
582       break;
583     case kArm64TestAndBranch32:
584     case kArm64TestAndBranch:
585       // Pseudo instructions turned into tbz/tbnz in AssembleArchBranch.
586       break;
587     case kArm64CompareAndBranch32:
588       // Pseudo instruction turned into cbz/cbnz in AssembleArchBranch.
589       break;
590     case kArm64Claim: {
591       int words = MiscField::decode(instr->opcode());
592       __ Claim(words);
593       break;
594     }
595     case kArm64Poke: {
596       int slot = MiscField::decode(instr->opcode());
597       Operand operand(slot * kPointerSize);
598       __ Poke(i.InputRegister(0), operand);
599       break;
600     }
601     case kArm64PokePairZero: {
602       // TODO(dcarney): test slot offset and register order.
603       int slot = MiscField::decode(instr->opcode()) - 1;
604       __ PokePair(i.InputRegister(0), xzr, slot * kPointerSize);
605       break;
606     }
607     case kArm64PokePair: {
608       int slot = MiscField::decode(instr->opcode()) - 1;
609       __ PokePair(i.InputRegister(1), i.InputRegister(0), slot * kPointerSize);
610       break;
611     }
612     case kArm64Cmp:
613       __ Cmp(i.InputRegister(0), i.InputOperand(1));
614       break;
615     case kArm64Cmp32:
616       __ Cmp(i.InputRegister32(0), i.InputOperand32(1));
617       break;
618     case kArm64Cmn:
619       __ Cmn(i.InputRegister(0), i.InputOperand(1));
620       break;
621     case kArm64Cmn32:
622       __ Cmn(i.InputRegister32(0), i.InputOperand32(1));
623       break;
624     case kArm64Tst:
625       __ Tst(i.InputRegister(0), i.InputOperand(1));
626       break;
627     case kArm64Tst32:
628       __ Tst(i.InputRegister32(0), i.InputOperand32(1));
629       break;
630     case kArm64Float64Cmp:
631       if (instr->InputAt(1)->IsDoubleRegister()) {
632         __ Fcmp(i.InputDoubleRegister(0), i.InputDoubleRegister(1));
633       } else {
634         DCHECK(instr->InputAt(1)->IsImmediate());
635         // 0.0 is the only immediate supported by fcmp instructions.
636         DCHECK(i.InputDouble(1) == 0.0);
637         __ Fcmp(i.InputDoubleRegister(0), i.InputDouble(1));
638       }
639       break;
640     case kArm64Float64Add:
641       __ Fadd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
642               i.InputDoubleRegister(1));
643       break;
644     case kArm64Float64Sub:
645       __ Fsub(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
646               i.InputDoubleRegister(1));
647       break;
648     case kArm64Float64Mul:
649       __ Fmul(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
650               i.InputDoubleRegister(1));
651       break;
652     case kArm64Float64Div:
653       __ Fdiv(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
654               i.InputDoubleRegister(1));
655       break;
656     case kArm64Float64Mod: {
657       // TODO(dcarney): implement directly. See note in lithium-codegen-arm64.cc
658       FrameScope scope(masm(), StackFrame::MANUAL);
659       DCHECK(d0.is(i.InputDoubleRegister(0)));
660       DCHECK(d1.is(i.InputDoubleRegister(1)));
661       DCHECK(d0.is(i.OutputDoubleRegister()));
662       // TODO(dcarney): make sure this saves all relevant registers.
663       __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()),
664                        0, 2);
665       break;
666     }
667     case kArm64Float64Sqrt:
668       __ Fsqrt(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
669       break;
670     case kArm64Float32ToFloat64:
671       __ Fcvt(i.OutputDoubleRegister(), i.InputDoubleRegister(0).S());
672       break;
673     case kArm64Float64ToFloat32:
674       __ Fcvt(i.OutputDoubleRegister().S(), i.InputDoubleRegister(0));
675       break;
676     case kArm64Float64ToInt32:
677       __ Fcvtzs(i.OutputRegister32(), i.InputDoubleRegister(0));
678       break;
679     case kArm64Float64ToUint32:
680       __ Fcvtzu(i.OutputRegister32(), i.InputDoubleRegister(0));
681       break;
682     case kArm64Int32ToFloat64:
683       __ Scvtf(i.OutputDoubleRegister(), i.InputRegister32(0));
684       break;
685     case kArm64Uint32ToFloat64:
686       __ Ucvtf(i.OutputDoubleRegister(), i.InputRegister32(0));
687       break;
688     case kArm64Ldrb:
689       __ Ldrb(i.OutputRegister(), i.MemoryOperand());
690       break;
691     case kArm64Ldrsb:
692       __ Ldrsb(i.OutputRegister(), i.MemoryOperand());
693       break;
694     case kArm64Strb:
695       __ Strb(i.InputRegister(2), i.MemoryOperand());
696       break;
697     case kArm64Ldrh:
698       __ Ldrh(i.OutputRegister(), i.MemoryOperand());
699       break;
700     case kArm64Ldrsh:
701       __ Ldrsh(i.OutputRegister(), i.MemoryOperand());
702       break;
703     case kArm64Strh:
704       __ Strh(i.InputRegister(2), i.MemoryOperand());
705       break;
706     case kArm64LdrW:
707       __ Ldr(i.OutputRegister32(), i.MemoryOperand());
708       break;
709     case kArm64StrW:
710       __ Str(i.InputRegister32(2), i.MemoryOperand());
711       break;
712     case kArm64Ldr:
713       __ Ldr(i.OutputRegister(), i.MemoryOperand());
714       break;
715     case kArm64Str:
716       __ Str(i.InputRegister(2), i.MemoryOperand());
717       break;
718     case kArm64LdrS:
719       __ Ldr(i.OutputDoubleRegister().S(), i.MemoryOperand());
720       break;
721     case kArm64StrS:
722       __ Str(i.InputDoubleRegister(2).S(), i.MemoryOperand());
723       break;
724     case kArm64LdrD:
725       __ Ldr(i.OutputDoubleRegister(), i.MemoryOperand());
726       break;
727     case kArm64StrD:
728       __ Str(i.InputDoubleRegister(2), i.MemoryOperand());
729       break;
730     case kArm64StoreWriteBarrier: {
731       Register object = i.InputRegister(0);
732       Register index = i.InputRegister(1);
733       Register value = i.InputRegister(2);
734       __ Add(index, object, index);
735       __ Str(value, MemOperand(index));
736       SaveFPRegsMode mode =
737           frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
738       // TODO(dcarney): we shouldn't test write barriers from c calls.
739       LinkRegisterStatus lr_status = kLRHasNotBeenSaved;
740       UseScratchRegisterScope scope(masm());
741       Register temp = no_reg;
742       if (csp.is(masm()->StackPointer())) {
743         temp = scope.AcquireX();
744         lr_status = kLRHasBeenSaved;
745         __ Push(lr, temp);  // Need to push a pair
746       }
747       __ RecordWrite(object, index, value, lr_status, mode);
748       if (csp.is(masm()->StackPointer())) {
749         __ Pop(temp, lr);
750       }
751       break;
752     }
753     case kCheckedLoadInt8:
754       ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrsb);
755       break;
756     case kCheckedLoadUint8:
757       ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrb);
758       break;
759     case kCheckedLoadInt16:
760       ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrsh);
761       break;
762     case kCheckedLoadUint16:
763       ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrh);
764       break;
765     case kCheckedLoadWord32:
766       ASSEMBLE_CHECKED_LOAD_INTEGER(Ldr);
767       break;
768     case kCheckedLoadFloat32:
769       ASSEMBLE_CHECKED_LOAD_FLOAT(32);
770       break;
771     case kCheckedLoadFloat64:
772       ASSEMBLE_CHECKED_LOAD_FLOAT(64);
773       break;
774     case kCheckedStoreWord8:
775       ASSEMBLE_CHECKED_STORE_INTEGER(Strb);
776       break;
777     case kCheckedStoreWord16:
778       ASSEMBLE_CHECKED_STORE_INTEGER(Strh);
779       break;
780     case kCheckedStoreWord32:
781       ASSEMBLE_CHECKED_STORE_INTEGER(Str);
782       break;
783     case kCheckedStoreFloat32:
784       ASSEMBLE_CHECKED_STORE_FLOAT(32);
785       break;
786     case kCheckedStoreFloat64:
787       ASSEMBLE_CHECKED_STORE_FLOAT(64);
788       break;
789   }
790 }
791
792
793 // Assemble branches after this instruction.
794 void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
795   Arm64OperandConverter i(this, instr);
796   Label* tlabel = branch->true_label;
797   Label* flabel = branch->false_label;
798   FlagsCondition condition = branch->condition;
799   ArchOpcode opcode = instr->arch_opcode();
800
801   if (opcode == kArm64CompareAndBranch32) {
802     switch (condition) {
803       case kEqual:
804         __ Cbz(i.InputRegister32(0), tlabel);
805         break;
806       case kNotEqual:
807         __ Cbnz(i.InputRegister32(0), tlabel);
808         break;
809       default:
810         UNREACHABLE();
811     }
812   } else if (opcode == kArm64TestAndBranch32) {
813     switch (condition) {
814       case kEqual:
815         __ Tbz(i.InputRegister32(0), i.InputInt5(1), tlabel);
816         break;
817       case kNotEqual:
818         __ Tbnz(i.InputRegister32(0), i.InputInt5(1), tlabel);
819         break;
820       default:
821         UNREACHABLE();
822     }
823   } else if (opcode == kArm64TestAndBranch) {
824     switch (condition) {
825       case kEqual:
826         __ Tbz(i.InputRegister64(0), i.InputInt6(1), tlabel);
827         break;
828       case kNotEqual:
829         __ Tbnz(i.InputRegister64(0), i.InputInt6(1), tlabel);
830         break;
831       default:
832         UNREACHABLE();
833     }
834   } else {
835     Condition cc = FlagsConditionToCondition(condition);
836     __ B(cc, tlabel);
837   }
838   if (!branch->fallthru) __ B(flabel);  // no fallthru to flabel.
839 }
840
841
842 void CodeGenerator::AssembleArchJump(BasicBlock::RpoNumber target) {
843   if (!IsNextInAssemblyOrder(target)) __ B(GetLabel(target));
844 }
845
846
847 // Assemble boolean materializations after this instruction.
848 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
849                                         FlagsCondition condition) {
850   Arm64OperandConverter i(this, instr);
851
852   // Materialize a full 64-bit 1 or 0 value. The result register is always the
853   // last output of the instruction.
854   DCHECK_NE(0u, instr->OutputCount());
855   Register reg = i.OutputRegister(instr->OutputCount() - 1);
856   Condition cc = FlagsConditionToCondition(condition);
857   __ Cset(reg, cc);
858 }
859
860
861 void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) {
862   Arm64OperandConverter i(this, instr);
863   Register input = i.InputRegister32(0);
864   for (size_t index = 2; index < instr->InputCount(); index += 2) {
865     __ Cmp(input, i.InputInt32(index + 0));
866     __ B(eq, GetLabel(i.InputRpo(index + 1)));
867   }
868   AssembleArchJump(i.InputRpo(1));
869 }
870
871
872 void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
873   Arm64OperandConverter i(this, instr);
874   UseScratchRegisterScope scope(masm());
875   Register input = i.InputRegister32(0);
876   Register temp = scope.AcquireX();
877   size_t const case_count = instr->InputCount() - 2;
878   Label table;
879   __ Cmp(input, case_count);
880   __ B(hs, GetLabel(i.InputRpo(1)));
881   __ Adr(temp, &table);
882   __ Add(temp, temp, Operand(input, UXTW, 2));
883   __ Br(temp);
884   __ StartBlockPools();
885   __ Bind(&table);
886   for (size_t index = 0; index < case_count; ++index) {
887     __ B(GetLabel(i.InputRpo(index + 2)));
888   }
889   __ EndBlockPools();
890 }
891
892
893 void CodeGenerator::AssembleDeoptimizerCall(int deoptimization_id) {
894   Address deopt_entry = Deoptimizer::GetDeoptimizationEntry(
895       isolate(), deoptimization_id, Deoptimizer::LAZY);
896   __ Call(deopt_entry, RelocInfo::RUNTIME_ENTRY);
897 }
898
899
900 // TODO(dcarney): increase stack slots in frame once before first use.
901 static int AlignedStackSlots(int stack_slots) {
902   if (stack_slots & 1) stack_slots++;
903   return stack_slots;
904 }
905
906
907 void CodeGenerator::AssemblePrologue() {
908   CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
909   int stack_slots = frame()->GetSpillSlotCount();
910   if (descriptor->kind() == CallDescriptor::kCallAddress) {
911     __ SetStackPointer(csp);
912     __ Push(lr, fp);
913     __ Mov(fp, csp);
914     // TODO(dcarney): correct callee saved registers.
915     __ PushCalleeSavedRegisters();
916     frame()->SetRegisterSaveAreaSize(20 * kPointerSize);
917   } else if (descriptor->IsJSFunctionCall()) {
918     CompilationInfo* info = this->info();
919     __ SetStackPointer(jssp);
920     __ Prologue(info->IsCodePreAgingActive());
921     frame()->SetRegisterSaveAreaSize(
922         StandardFrameConstants::kFixedFrameSizeFromFp);
923   } else if (stack_slots > 0) {
924     __ SetStackPointer(jssp);
925     __ StubPrologue();
926     frame()->SetRegisterSaveAreaSize(
927         StandardFrameConstants::kFixedFrameSizeFromFp);
928   }
929
930   if (info()->is_osr()) {
931     // TurboFan OSR-compiled functions cannot be entered directly.
932     __ Abort(kShouldNotDirectlyEnterOsrFunction);
933
934     // Unoptimized code jumps directly to this entrypoint while the unoptimized
935     // frame is still on the stack. Optimized code uses OSR values directly from
936     // the unoptimized frame. Thus, all that needs to be done is to allocate the
937     // remaining stack slots.
938     if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
939     osr_pc_offset_ = __ pc_offset();
940     DCHECK(stack_slots >= frame()->GetOsrStackSlotCount());
941     stack_slots -= frame()->GetOsrStackSlotCount();
942   }
943
944   if (stack_slots > 0) {
945     Register sp = __ StackPointer();
946     if (!sp.Is(csp)) {
947       __ Sub(sp, sp, stack_slots * kPointerSize);
948     }
949     __ Sub(csp, csp, AlignedStackSlots(stack_slots) * kPointerSize);
950   }
951 }
952
953
954 void CodeGenerator::AssembleReturn() {
955   CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
956   int stack_slots = frame()->GetSpillSlotCount();
957   if (descriptor->kind() == CallDescriptor::kCallAddress) {
958     if (frame()->GetRegisterSaveAreaSize() > 0) {
959       // Remove this frame's spill slots first.
960       if (stack_slots > 0) {
961         __ Add(csp, csp, AlignedStackSlots(stack_slots) * kPointerSize);
962       }
963       // Restore registers.
964       // TODO(dcarney): correct callee saved registers.
965       __ PopCalleeSavedRegisters();
966     }
967     __ Mov(csp, fp);
968     __ Pop(fp, lr);
969     __ Ret();
970   } else if (descriptor->IsJSFunctionCall() || stack_slots > 0) {
971     __ Mov(jssp, fp);
972     __ Pop(fp, lr);
973     int pop_count = descriptor->IsJSFunctionCall()
974                         ? static_cast<int>(descriptor->JSParameterCount())
975                         : 0;
976     __ Drop(pop_count);
977     __ Ret();
978   } else {
979     __ Ret();
980   }
981 }
982
983
984 void CodeGenerator::AssembleMove(InstructionOperand* source,
985                                  InstructionOperand* destination) {
986   Arm64OperandConverter g(this, NULL);
987   // Dispatch on the source and destination operand kinds.  Not all
988   // combinations are possible.
989   if (source->IsRegister()) {
990     DCHECK(destination->IsRegister() || destination->IsStackSlot());
991     Register src = g.ToRegister(source);
992     if (destination->IsRegister()) {
993       __ Mov(g.ToRegister(destination), src);
994     } else {
995       __ Str(src, g.ToMemOperand(destination, masm()));
996     }
997   } else if (source->IsStackSlot()) {
998     MemOperand src = g.ToMemOperand(source, masm());
999     DCHECK(destination->IsRegister() || destination->IsStackSlot());
1000     if (destination->IsRegister()) {
1001       __ Ldr(g.ToRegister(destination), src);
1002     } else {
1003       UseScratchRegisterScope scope(masm());
1004       Register temp = scope.AcquireX();
1005       __ Ldr(temp, src);
1006       __ Str(temp, g.ToMemOperand(destination, masm()));
1007     }
1008   } else if (source->IsConstant()) {
1009     Constant src = g.ToConstant(ConstantOperand::cast(source));
1010     if (destination->IsRegister() || destination->IsStackSlot()) {
1011       UseScratchRegisterScope scope(masm());
1012       Register dst = destination->IsRegister() ? g.ToRegister(destination)
1013                                                : scope.AcquireX();
1014       if (src.type() == Constant::kHeapObject) {
1015         __ LoadObject(dst, src.ToHeapObject());
1016       } else {
1017         __ Mov(dst, g.ToImmediate(source));
1018       }
1019       if (destination->IsStackSlot()) {
1020         __ Str(dst, g.ToMemOperand(destination, masm()));
1021       }
1022     } else if (src.type() == Constant::kFloat32) {
1023       if (destination->IsDoubleRegister()) {
1024         FPRegister dst = g.ToDoubleRegister(destination).S();
1025         __ Fmov(dst, src.ToFloat32());
1026       } else {
1027         DCHECK(destination->IsDoubleStackSlot());
1028         UseScratchRegisterScope scope(masm());
1029         FPRegister temp = scope.AcquireS();
1030         __ Fmov(temp, src.ToFloat32());
1031         __ Str(temp, g.ToMemOperand(destination, masm()));
1032       }
1033     } else {
1034       DCHECK_EQ(Constant::kFloat64, src.type());
1035       if (destination->IsDoubleRegister()) {
1036         FPRegister dst = g.ToDoubleRegister(destination);
1037         __ Fmov(dst, src.ToFloat64());
1038       } else {
1039         DCHECK(destination->IsDoubleStackSlot());
1040         UseScratchRegisterScope scope(masm());
1041         FPRegister temp = scope.AcquireD();
1042         __ Fmov(temp, src.ToFloat64());
1043         __ Str(temp, g.ToMemOperand(destination, masm()));
1044       }
1045     }
1046   } else if (source->IsDoubleRegister()) {
1047     FPRegister src = g.ToDoubleRegister(source);
1048     if (destination->IsDoubleRegister()) {
1049       FPRegister dst = g.ToDoubleRegister(destination);
1050       __ Fmov(dst, src);
1051     } else {
1052       DCHECK(destination->IsDoubleStackSlot());
1053       __ Str(src, g.ToMemOperand(destination, masm()));
1054     }
1055   } else if (source->IsDoubleStackSlot()) {
1056     DCHECK(destination->IsDoubleRegister() || destination->IsDoubleStackSlot());
1057     MemOperand src = g.ToMemOperand(source, masm());
1058     if (destination->IsDoubleRegister()) {
1059       __ Ldr(g.ToDoubleRegister(destination), src);
1060     } else {
1061       UseScratchRegisterScope scope(masm());
1062       FPRegister temp = scope.AcquireD();
1063       __ Ldr(temp, src);
1064       __ Str(temp, g.ToMemOperand(destination, masm()));
1065     }
1066   } else {
1067     UNREACHABLE();
1068   }
1069 }
1070
1071
1072 void CodeGenerator::AssembleSwap(InstructionOperand* source,
1073                                  InstructionOperand* destination) {
1074   Arm64OperandConverter g(this, NULL);
1075   // Dispatch on the source and destination operand kinds.  Not all
1076   // combinations are possible.
1077   if (source->IsRegister()) {
1078     // Register-register.
1079     UseScratchRegisterScope scope(masm());
1080     Register temp = scope.AcquireX();
1081     Register src = g.ToRegister(source);
1082     if (destination->IsRegister()) {
1083       Register dst = g.ToRegister(destination);
1084       __ Mov(temp, src);
1085       __ Mov(src, dst);
1086       __ Mov(dst, temp);
1087     } else {
1088       DCHECK(destination->IsStackSlot());
1089       MemOperand dst = g.ToMemOperand(destination, masm());
1090       __ Mov(temp, src);
1091       __ Ldr(src, dst);
1092       __ Str(temp, dst);
1093     }
1094   } else if (source->IsStackSlot() || source->IsDoubleStackSlot()) {
1095     UseScratchRegisterScope scope(masm());
1096     DoubleRegister temp_0 = scope.AcquireD();
1097     DoubleRegister temp_1 = scope.AcquireD();
1098     MemOperand src = g.ToMemOperand(source, masm());
1099     MemOperand dst = g.ToMemOperand(destination, masm());
1100     __ Ldr(temp_0, src);
1101     __ Ldr(temp_1, dst);
1102     __ Str(temp_0, dst);
1103     __ Str(temp_1, src);
1104   } else if (source->IsDoubleRegister()) {
1105     UseScratchRegisterScope scope(masm());
1106     FPRegister temp = scope.AcquireD();
1107     FPRegister src = g.ToDoubleRegister(source);
1108     if (destination->IsDoubleRegister()) {
1109       FPRegister dst = g.ToDoubleRegister(destination);
1110       __ Fmov(temp, src);
1111       __ Fmov(src, dst);
1112       __ Fmov(dst, temp);
1113     } else {
1114       DCHECK(destination->IsDoubleStackSlot());
1115       MemOperand dst = g.ToMemOperand(destination, masm());
1116       __ Fmov(temp, src);
1117       __ Ldr(src, dst);
1118       __ Str(temp, dst);
1119     }
1120   } else {
1121     // No other combinations are possible.
1122     UNREACHABLE();
1123   }
1124 }
1125
1126
1127 void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
1128   // On 64-bit ARM we emit the jump tables inline.
1129   UNREACHABLE();
1130 }
1131
1132
1133 void CodeGenerator::AddNopForSmiCodeInlining() { __ movz(xzr, 0); }
1134
1135
1136 void CodeGenerator::EnsureSpaceForLazyDeopt() {
1137   int space_needed = Deoptimizer::patch_size();
1138   if (!info()->IsStub()) {
1139     // Ensure that we have enough space after the previous lazy-bailout
1140     // instruction for patching the code here.
1141     intptr_t current_pc = masm()->pc_offset();
1142
1143     if (current_pc < (last_lazy_deopt_pc_ + space_needed)) {
1144       intptr_t padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
1145       DCHECK((padding_size % kInstructionSize) == 0);
1146       InstructionAccurateScope instruction_accurate(
1147           masm(), padding_size / kInstructionSize);
1148
1149       while (padding_size > 0) {
1150         __ nop();
1151         padding_size -= kInstructionSize;
1152       }
1153     }
1154   }
1155   MarkLazyDeoptSite();
1156 }
1157
1158 #undef __
1159
1160 }  // namespace compiler
1161 }  // namespace internal
1162 }  // namespace v8